-
Notifications
You must be signed in to change notification settings - Fork 14
Add support for PQC secure boot with MCXA 5xx family of MCUs #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5ab9523
e6c73e3
6377471
d5f2142
502cd91
e0f5580
f185706
13e2e38
59d2344
bbfa20c
27d4f5f
fb0c240
4a573ec
4537a8b
b517176
3e9eeaa
78ffbcf
af996ef
3db3385
643c6e3
0001414
6746b85
ae68481
f38dd33
0d2e8c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Rust build artifacts | ||
| target/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
| 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: | ||
|
|
@@ -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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reframing the doc here seems like a meaningless change?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||
|
|
@@ -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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
|
@@ -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: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| ... | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [build] | ||
| target = "thumbv8m.main-none-eabihf" |
| 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" |
| 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", | ||
| ] |
| 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 | ||
|
|
||
|
|
| 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"); | ||
| } |
| 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); |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } |
| 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" |
| 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.