Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5ab9523
add MCXA577 bootloader, blinky app, and ec-slimloader-mcxa library
alamfarjadf Apr 2, 2026
e6c73e3
fix embassy dependency resolution and clippy warning
alamfarjadf Apr 2, 2026
6377471
remove load.rs from PR (kept locally, unused)
alamfarjadf Apr 2, 2026
d5f2142
Merge branch 'main' into mcxa-577-bootloader
alamfarjadf Apr 4, 2026
502cd91
git dep changes and SGI signature updates; add rustdoc comments
alamfarjadf Apr 7, 2026
e0f5580
Merge branch 'main' into mcxa-577-bootloader
alamfarjadf Apr 15, 2026
f185706
mcxa cmpa/cfpa provisioning
alamfarjadf Apr 27, 2026
13e2e38
fix STRIDE / hyenas CWE issues
alamfarjadf May 16, 2026
59d2344
Harden CMPA scratch lifecycle staging
alamfarjadf May 19, 2026
bbfa20c
Merge branch 'main' into mcxa-577-bootloader
alamfarjadf May 19, 2026
27d4f5f
Merge branch 'main' into mcxa-577-bootloader
alamfarjadf Jun 10, 2026
fb0c240
gitignore
alamfarjadf Jun 15, 2026
4a573ec
align embassy dependencies; use cortex-m reset; remove OTP ROM API fu…
alamfarjadf Jul 9, 2026
4537a8b
lifecycle provisioning updates
alamfarjadf Jul 17, 2026
b517176
provisining flow updates
alamfarjadf Jul 20, 2026
3e9eeaa
consolidate provisioning interface
alamfarjadf Jul 20, 2026
78ffbcf
jump update, use header validation; ROTKH provisioning updates
alamfarjadf Jul 25, 2026
af996ef
address NbootCtx inputs on an unprovisioned MCU
alamfarjadf Jul 30, 2026
3db3385
Add flexSPI boot config to first setup
alamfarjadf Aug 5, 2026
643c6e3
SBL first provision; cargo fmt; deny and check updates
alamfarjadf Aug 11, 2026
0001414
add missing toml files
alamfarjadf Aug 11, 2026
6746b85
security provisioning standalone cargo.toml
alamfarjadf Aug 11, 2026
ae68481
exclude MCXA from linux target in CI
alamfarjadf Aug 11, 2026
f38dd33
Update examples/mcxa-577app/app/src/main.rs
alamfarjadf Aug 13, 2026
0d2e8c0
Update app main.rs
alamfarjadf Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ jobs:
- name: cargo fmt --check (libs)
run: cargo fmt --check
working-directory: "./libs"
- name: cargo fmt --check (examples)
- name: cargo fmt --check (examples/rt685s)
run: cargo fmt --check
working-directory: "./examples/rt685s"
- name: cargo fmt --check (examples/mcxa-577app)
run: cargo fmt --check
working-directory: "./examples/mcxa-577app"
- name: cargo fmt --check (bootloader-tool)
run: cargo fmt --check
working-directory: "./bootloader-tool"
Expand Down Expand Up @@ -78,13 +81,20 @@ jobs:
clippy_flags: -- -F clippy::suspicious -D clippy::correctness -F clippy::perf -F clippy::style
github_token: ${{ secrets.GITHUB_TOKEN }}
workdir: "./libs"
- name: cargo clippy (examples)
- name: cargo clippy (examples/rt685s)
uses: giraffate/clippy-action@v1
with:
reporter: "github-pr-check"
clippy_flags: -- -F clippy::suspicious -D clippy::correctness -F clippy::perf -F clippy::style
github_token: ${{ secrets.GITHUB_TOKEN }}
workdir: "./examples/rt685s"
- name: cargo clippy (examples/mcxa-577app)
uses: giraffate/clippy-action@v1
with:
reporter: "github-pr-check"
clippy_flags: -- -F clippy::suspicious -D clippy::correctness -F clippy::perf -F clippy::style
github_token: ${{ secrets.GITHUB_TOKEN }}
workdir: "./examples/mcxa-577app"
- name: cargo clippy (bootloader-tool)
uses: giraffate/clippy-action@v1
with:
Expand Down Expand Up @@ -135,7 +145,7 @@ jobs:
working-directory: "./libs"
env:
RUSTDOCFLAGS: --cfg docsrs
- name: cargo doc (examples)
- name: cargo doc (examples/rt685s)
run: cargo doc --no-deps
working-directory: "./examples/rt685s"
env:
Expand Down Expand Up @@ -197,12 +207,18 @@ jobs:
log-level: warn
manifest-path: ./libs/Cargo.toml
command: check
- name: Cargo deny (examples)
- name: Cargo deny (examples/rt685s)
uses: EmbarkStudios/cargo-deny-action@v2
with:
log-level: warn
manifest-path: ./examples/rt685s/Cargo.toml
command: check
- name: Cargo deny (examples/mcxa-577app)
uses: EmbarkStudios/cargo-deny-action@v2
with:
log-level: warn
manifest-path: ./examples/mcxa-577app/Cargo.toml
command: check

msrv:
# check that we can build using the minimal rust version that is specified by this crate
Expand Down Expand Up @@ -233,9 +249,12 @@ jobs:
- name: cargo +${{ matrix.msrv }} check (libs)
run: cargo check --features mimxrt685s
working-directory: "./libs"
- name: cargo +${{ matrix.msrv }} check (examples)
- name: cargo +${{ matrix.msrv }} check (examples/rt685s)
run: cargo check
working-directory: "./examples/rt685s"
- name: cargo +${{ matrix.msrv }} check (examples/mcxa-577app)
run: cargo check
working-directory: "./examples/mcxa-577app"
- name: cargo +${{ matrix.msrv }} check (bootloader-tool)
run: cargo check
working-directory: "./bootloader-tool"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Rust build artifacts
target/
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ A light-weight bootloader written in Rust with a fail-safe NOR-flash backed stat
This framework can run on any platform if support for the platform is implemented.
It is only opinionated with regards to how the state is stored.

Currently only supports the NXP IMXRT685S and IMXRT633S where it acts as a stage-two bootloader and copies the program to application RAM.
Also contains a tool for signing images, flashing them to the device, setting fuses (or shadow registers) containing crypto keys,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but both of these second and third points are still true?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely botched the README... oops.

I have to redo the whole thing.

and an example application to showcase the bootloaders A/B state functionality for this family of chipsets.

## Organisation

This repository is split up into three parts:
Expand All @@ -16,28 +12,28 @@ This repository is split up into three parts:
* bootloader-tool: a command-line utility only used to perform operations related to the NXP RT685S platform.
It uses the NXP SPSDK tooling to generate keys, sign images, and flash them to the target device. Also integrates probe-rs and allows for attaching to the RTT buffer for displaying `defmt` output.
This tool is not relevant if you want to use `ec-slimloader` with any other platform.
* examples/mcxa-577app: example bootloader and blinky demo application, can be run on MCXA5xx evalutation kit.

The libraries are split out as follows:
* ec-slimloader: general library crate providing a basic structure to build your bootloader binary application.
* ec-slimloader-state: library crate with all code relating to managing the state journal. Used by both the bootloader and the application to change which image slot should be booted.
* ec-slimloader-imxrt: library crate implementing support for the NXP IMXRT685S and IMXRT633S.
* imxrt-rom: library crate implementing Rust support for the NXP ROM API which provides access to fuses and allows calling into a verification routine for images.
* ec-slimloader-mcxa: library crate implementing support for the NXP MCXA5xx family with PQC cryptography support.

## How it works
Assuming your platform is already supported, you can define:
* a region of NOR-flash memory containing at least 2 pages for the bootloader state.
* at least two regions of any memory that will fit an application image.

Using the library crate for your platform (like `ec-slimloader-imxrt`) you can then implement your own bootloader binary by calling the `start` function in the `ec-slimloader` library crate.
Using the library crate for your platform (e.g., `ec-slimloader-mcxa`) you can implement your own bootloader binary by calling the `start` function in the `ec-slimloader` library crate.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reframing the doc here seems like a meaningless change?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README is screwed up, redoing it.


The `ec-slimloader` crate will handle for you:
* it will read from the state journal what image slot will be booted.
* on subsequent reboots, it will fall back to your defined backup slot if you do not mark your current application image as `confirmed`.

However, some aspects are handled by the platform support crate (and can differ from project-to-project):
* how application images are loaded. For `ec-slimloader-imxrt` images are copied to RAM in a quite chip-specific way. Typically for other platforms you might want to swap images between on-chip NOR flash and external NOR flash. The latter method is not implemented in this repository (yet).
* how application images are verified. By default the images themselves are not checked at all. `ec-slimloader-imxrt` leverages the native NXP authentication routines to check image integrity.
* how application images are bootloaded, or in other words are jumped to. This differs for cortex-m or RISCV processors.
* how application images are loaded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raw delete of content here but leaving the header doesn't make much sense to me. If this is not consistent across platforms, then lets document that accordingly

* how application images are verified.
* how application images are bootloaded, or in other words are jumped to.

Even when using `ec-slimloader-imxrt`, you will still have to implement a few details:
* from what memory is the `ec-slimloader` started, and what memory range is used for the bootloader data?
Expand All @@ -49,7 +45,7 @@ Finally, your application needs to also work with the state journal to:
* after rebooting, mark the current image slot from which the application is running as `confirmed`.
If the application does not do this, the bootloader will load the old 'backup' image and mark the current boot as `failed`.

For a full tour on how to use this framework, please refer to the `examples/rt685s` folder.
For a full tour on how to use this framework for MCXA5xx, refer to the MCX examples.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, seems odd to selectively change just this line but not the whole document


## Quick guide
This guide details how to use this repository on the NXP MIMXRT685S-EVK. First step is compiling the bootloader and application:
Expand Down Expand Up @@ -123,3 +119,6 @@ cargo run -- run application -i ../examples/rt685s/target/thumbv8m.main-none-eab
You can use the `USER_1` button to change the state journal to either `confirmed` or try the other slot in state `initial` if the current image is already `confirmed`.

You can use the `USER_2` button the reboot into the bootloader, which will set an image to `failed` if it does not verify or if it was in `attempting` without putting the state in `confirmed`.

The following describes the process for generating artifacts and signing/flashing for MCXA:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh you might want to just scrub the changes to this file, it's hard to see what the reason was for each independent edit, as they don't roll into the surrounding context meaningfully

...
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ allow = [
"MIT",
"Apache-2.0",
"Unicode-3.0",
"BSD-3-Clause",
#"Apache-2.0 WITH LLVM-exception",
]
# The confidence threshold for detecting a license from license text.
Expand Down
2 changes: 2 additions & 0 deletions examples/mcxa-577app/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "thumbv8m.main-none-eabihf"
12 changes: 12 additions & 0 deletions examples/mcxa-577app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[workspace]
resolver = "2"
members = [
"app",
"bootloader",
]

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/OpenDevicePartnership/ec-slimloader"
14 changes: 14 additions & 0 deletions examples/mcxa-577app/app/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[build]
target = "thumbv8m.main-none-eabihf"

[target.thumbv8m.main-none-eabihf]
runner = "probe-rs run --chip MCXA577 --speed 10000"

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
"-C", "link-arg=--nmagic",
"-C", "force-frame-pointers=yes",
]
25 changes: 25 additions & 0 deletions examples/mcxa-577app/app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "mcxa-577app"
version = "0.1.0"
edition = "2021"
license.workspace = true

[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] }
defmt = "1.0"
defmt-rtt = "1.0"
embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", default-features = false, features = ["rt", "defmt", "mcxa5xx"] }
embassy-executor = { git = "https://github.com/embassy-rs/embassy", default-features = false, features = ["platform-cortex-m", "executor-thread"] }
embassy-time = { git = "https://github.com/embassy-rs/embassy", features = ["defmt", "defmt-timestamp-uptime"] }
panic-probe = { version = "1.0", features = ["print-defmt"] }

[profile.dev]
panic = "abort"

[profile.release]
debug = 2 # Full DWARF info for probe-rs RTT location decoding
lto = false # Disable LTO to prevent stripping debug patterns
opt-level = 2 # Standard optimization


21 changes: 21 additions & 0 deletions examples/mcxa-577app/app/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());

// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}
11 changes: 11 additions & 0 deletions examples/mcxa-577app/app/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
MEMORY
{
/* MCXA577 app memory map */
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* Bootloader uses 0x0000_0000..0x0000_FFFF (64KiB). App starts at slot_a = 0x0001_0000. */
FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x001F0000
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}

/* Stack grows down from end of RAM */
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
66 changes: 66 additions & 0 deletions examples/mcxa-577app/app/src/main.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that this app should be "complete". That is, it should open the journal and mark it Confirmed. Examples set the patterns folks will end up using.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I will add this.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#![no_std]
#![no_main]

use defmt_rtt as _;
use embassy_executor::Spawner;
use embassy_mcxa as hal;
use embassy_time::Timer;
use hal::bind_interrupts;
use hal::dma::DmaChannel;
use hal::gpio::{DriveStrength, Level, Output, SlewRate};
use hal::peripherals::SGI0;
use hal::sgi::hash::HashSize;
use hal::sgi::{InterruptHandler, Sgi};
use panic_probe as _;

bind_interrupts!(struct Irqs {
SGI => InterruptHandler<SGI0>;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut p = hal::init(hal::config::Config::default());

defmt::info!("Blinky example with a sprinkle of SGI hashing");

let mut dma_ch0 = DmaChannel::new(p.DMA0_CH0.reborrow());
let mut hash_result = [0u8; 48];
let input_data: [u8; 256] = core::array::from_fn(|i| i as u8);

let mut sgi = Sgi::new(p.SGI0.reborrow(), Irqs).unwrap();
match sgi
.sha2_start_and_finalize(&mut dma_ch0, HashSize::Sha384, &input_data, &mut hash_result)
.await
{
Ok(()) => defmt::info!("DMA hash: {=[u8]:x}", &hash_result[..]),
Err(e) => defmt::error!("DMA hash failed: {:?}", defmt::Debug2Format(&e)),
}

let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);
let mut green = Output::new(p.P2_22, Level::High, DriveStrength::Normal, SlewRate::Fast);
let mut blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Fast);

let mut rate = 250;

defmt::info!("It's showtime...");

loop {
if rate > 1000 {
rate = 250; // wrap rate to avoid overflow and excessively long timers.
}
red.toggle();
Timer::after_millis(rate).await;

red.toggle();
green.toggle();
Timer::after_millis(rate).await;

green.toggle();
blue.toggle();
Timer::after_millis(rate).await;
blue.toggle();

Timer::after_millis(rate).await;
rate = rate.wrapping_add(100);
}
}
17 changes: 17 additions & 0 deletions examples/mcxa-577app/bootloader/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[build]
target = "thumbv8m.main-none-eabihf"

[target.thumbv8m.main-none-eabihf]
runner = "probe-rs run --chip MCXA577 --speed 10000"

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
"-C", "link-arg=--nmagic",
"-C", "force-frame-pointers=yes",
]

[env]
DEFMT_LOG = "trace"
37 changes: 37 additions & 0 deletions examples/mcxa-577app/bootloader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "mcxa-577app-bootloader"
version = "0.1.0"
edition = "2021"
license.workspace = true

[features]
default = ["defmt", "certificate-logging", "verification-logging"]
defmt = ["dep:defmt", "dep:defmt-or-log", "dep:defmt-rtt", "defmt-or-log/defmt"]
log = ["dep:defmt-or-log", "defmt-or-log/log"]
certificate-logging = ["ec-slimloader-mcxa/certificate-logging"]
verification-logging = ["ec-slimloader-mcxa/verification-logging"]

[[bin]]
name = "mcxa-577app-bootloader"
path = "src/main.rs"

[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"], default-features = false }
cortex-m-rt = "0.7"
defmt = { version = "1.0", optional = true }
defmt-or-log = { version = "0.2.3", optional = true }
defmt-rtt = { version = "1.0", optional = true }
panic-probe = { version = "1.0", features = ["print-defmt"] }

ec-slimloader-mcxa = { path = "../../../libs/ec-slimloader-mcxa", default-features = false, features = ["internal-only", "mcxa5xx", "defmt"] }
ec-slimloader = { path = "../../../libs/ec-slimloader", default-features = false }
mcxa-security-provisioning = { path = "../../../mcxa-security-provisioning", default-features = false, features = ["defmt"] }

embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", default-features = false, features = ["rt", "mcxa5xx"] }
embassy-executor = { git = "https://github.com/embassy-rs/embassy", default-features = false, features = ["platform-cortex-m", "executor-thread"] }

[profile.release]
debug = 2 # Full DWARF info for defmt/probe-rs decoding
lto = false # Disable LTO to prevent stripping debug patterns
opt-level = 2 # Standard optimization
panic = "abort" # Smaller binaries
Loading
Loading