Skip to content

Add support for PQC secure boot with MCXA 5xx family of MCUs#36

Draft
alamfarjadf wants to merge 11 commits into
OpenDevicePartnership:mainfrom
alamfarjadf:mcxa-577-bootloader
Draft

Add support for PQC secure boot with MCXA 5xx family of MCUs#36
alamfarjadf wants to merge 11 commits into
OpenDevicePartnership:mainfrom
alamfarjadf:mcxa-577-bootloader

Conversation

@alamfarjadf

@alamfarjadf alamfarjadf commented Apr 2, 2026

Copy link
Copy Markdown

Adding ROM API support for MCXA 5xx family with Post-Quantum Cryptography hybrid secure boot (ML-DSA-87 and EC-DSA P384).

Added ec-slimloader-mcxa under libs and mcxa-577 examples (bootloader and blinky).

Validated on MCXA577 eval kit with hybrid signed AHAB MBI images which successfully authenticate and boot up.

Not yet added to PR: Imaging tools for MCXA.

BSD-3 license is acceptable (e.g. NXP-PAC), need to update deny.toml.

Clippy currently fails because SGI hashing PR waiting to be merged in embassy-mcxa

@jerrysxie jerrysxie assigned jerrysxie and unassigned jerrysxie Apr 7, 2026

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.

@JamesHuard This particular file maybe of interest to you.

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.

Let me connect with you offline, I'm currently building out the requirements for the SBL boot flow here and want to make sure we're aligned at that level (also there's a LOT of code here for me to review 😆 )

@alamfarjadf alamfarjadf force-pushed the mcxa-577-bootloader branch from 08f268c to f185706 Compare April 27, 2026 20:14

@diondokter diondokter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any split image support yet? If there is I can't find it (so please point me to it 😊)
Or are we assuming the ROM memory mapped it for us already?

target = "thumbv8m.main-none-eabihf"

[target.thumbv8m.main-none-eabihf]
runner = "probe-rs run --chip MCXA577 --chip-description-path MCXA_custom.yaml"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't see the MCXA_custom.yaml committed here... Could we fix that?

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.

@alamfarjadf is this a file you're maintaining locally?

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.

@alamfarjadf is this a file you're maintaining locally?

Yes, I can add.

Comment on lines +209 to +214
const AIRCR: *mut u32 = 0xE000ED0C as *mut u32;
const AIRCR_VECTKEY: u32 = 0x5FA << 16;
const AIRCR_SYSRESETREQ: u32 = 1 << 2;
unsafe {
core::ptr::write_volatile(AIRCR, AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where does this code actually come from? Translated from the C SDK or something?
A lot of it is very non-obvious and has magic values

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.

@alamfarjadf can correct me, but I believe it's a mix of referencing the prelim Reference Manual and the C SDK for the part.

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.

Where does this code actually come from? Translated from the C SDK or something? A lot of it is very non-obvious and has magic values

@diondokter This is from the preliminary reference manual as well as NXP's own SDK code.

The magic values are strictly defined header/ magic values that NXP puts in the AHAB certificate and expects to be there.

Refer to chapter 7 of the SRM.

Comment on lines +36 to +71
// (similar to imxrt): disable interrupts & timer
// Disable all maskable interrupts
// info!("jump: disabling interrupts");
#[cfg(target_arch = "arm")]
asm!("cpsid i", options(nostack, preserves_flags));

// Disable SysTick (if previously configured by loader)
const SYST_CSR: *mut u32 = 0xE000E010 as *mut u32; // SysTick Control and Status Register
core::ptr::write_volatile(SYST_CSR, 0);
// info!("jump: SysTick disabled");

// Disable NVIC interrupts & clear any pending bits (MCXN556s up to IRQ 155 → 5 * 32 blocks)
const NVIC_ICER_BASE: *mut u32 = 0xE000E180 as *mut u32; // Interrupt Clear/Enable Registers
const NVIC_ICPR_BASE: *mut u32 = 0xE000E280 as *mut u32; // Interrupt Clear/Pending Registers
for i in 0..5 {
core::ptr::write_volatile(NVIC_ICER_BASE.add(i), 0xFFFF_FFFF);
core::ptr::write_volatile(NVIC_ICPR_BASE.add(i), 0xFFFF_FFFF);
}
// info!("jump: NVIC interrupts disabled & pending cleared");

// Clear selected system handler pending bits (SecureFault, PendSV) in SHCSR
const SCB_SHCSR: *mut u32 = 0xE000ED24 as *mut u32; // System Handler Control and State Register
// Write-1-to-clear for pending bits is not supported; instead, clear enable bits to avoid servicing.
// Ensure SVC/Debug/PendSV/SysTick not enabled by loader.
core::ptr::write_volatile(SCB_SHCSR, 0);
// info!("jump: SHCSR cleared");

// Ensure privileged thread mode & use MSP (clear CONTROL.nPRIV & CONTROL.SPSEL)
#[cfg(target_arch = "arm")]
asm!("msr CONTROL, {0}", in(reg) 0u32, options(nostack, preserves_flags));
#[cfg(target_arch = "arm")]
asm!("isb", options(nostack, preserves_flags));

// Set VTOR to application's vector table
const SCB_VTOR: *mut u32 = 0xE000ED08 as *mut u32;
core::ptr::write_volatile(SCB_VTOR, entry);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This needs a pass. It does things we don't need (like touch the systick) and it uses raw pointer math while cortex-m can do things for us

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.

Agreed, I got carried away without being fully familiar with the with the cortex-m crate.

Comment on lines +134 to +143
let flash = InternalFlash::new();
let journal = match FlashJournal::new::<JOURNAL_BUFFER_SIZE>(flash).await {
Ok(journal) => journal,
Err(_) => {
mcxa_error!("Critical: failed to initialize flash journal");
loop {
cortex_m::asm::wfi();
}
}
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I thought we weren't putting the journal in internal flash?
Or do we have a custom board impl for that? (privately)

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.

That is an update that's needed here. Journal should be the first partition in external flash, since the internal flash doesn't support multiwrite

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How opinionated should this be? Currently this is a very specific implementation. Another mcxa user might want to store things in different locations.

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.

Yea, agreed this is too opinionated. I think we should follow a similar method as was done on the RT6 part, where we can specify these using the partition TOML

@JamesHuard

Copy link
Copy Markdown
Contributor

Is there any split image support yet? If there is I can't find it (so please point me to it 😊) Or are we assuming the ROM memory mapped it for us already?

The image header that the NXP ROM APIs/ROM Loader use has a section for identifying multiple images. The thought here is that it'd be a 'no-op' from an implementation perspective of the SBL firmware to support split-image, because it's already baked into the infra. The change then is in the build tooling to actually generate these additional fields and hashes.

That's also where the open question is to NXP around if the secondary image(s) are simply hash verified or included in the full image signature block.

@alamfarjadf

Copy link
Copy Markdown
Author

Is there any split image support yet? If there is I can't find it (so please point me to it 😊) Or are we assuming the ROM memory mapped it for us already?

@diondokter There is no split image support yet.

From my understanding, the split image on the verification side should be opaque to the caller, IF the image is built correctly (The idea here is that the second executable occupies the area for the CRC binary and is hashed and verified automatically by the APIs). But that idea hasn't been validated yet partly because I haven't been able to generate a split image binary yet.

We have reached out to NXP for further clarification and are awaiting their response.

When we have a split binary, the API will use the single AHAB container to hash both binaries and validate them, so infrastructure needed is mostly on the artifact generation side (which is not published as part of this public PR yet but available in private repos).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants