|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Controller-agnostic USB-DFU MRAM bootloader core for the RA8D2. More...
Go to the source code of this file.
Data Structures | |
| struct | ra8_dfu_img_hdr_t |
| 32-byte application-image header at the base of each slot. More... | |
Enumerations | |
| enum | ra8_dfu_layout_t : uint32_t { k_ra8_dfu_mram_base = 0x02000000U , k_ra8_dfu_mram_size = 0x00100000U , k_ra8_dfu_bl_size = 0x00020000U , k_ra8_dfu_slot_a_base = 0x02020000U , k_ra8_dfu_slot_b_base = 0x02090000U , k_ra8_dfu_slot_size = 0x00070000U , k_ra8_dfu_page_size = 0x00000020U , k_ra8_dfu_hdr_size = 0x00000020U , k_ra8_dfu_img_max = 0x0006FFE0U , k_ra8_dfu_hdr_offset = 0x0006FFE0U , k_ra8_dfu_hdr_magic = 0x52413844U , k_ra8_dfu_trigger_magic = 0xDF00B007U } |
| Fixed MRAM bank-layout addresses and image-header constants. More... | |
| enum | ra8_dfu_run_t : uint32_t { k_ra8_dfu_run_base = 0x22020000U } |
| Fixed SRAM copy-to-run execution base. More... | |
| enum | ra8_dfu_slot_t : uint8_t { k_ra8_dfu_slot_a = 0U , k_ra8_dfu_slot_b = 1U , k_ra8_dfu_slot_none = 2U } |
| Application-slot identifier. More... | |
| enum | ra8_dfu_action_t : uint8_t { k_ra8_dfu_action_dfu = 0U , k_ra8_dfu_action_jump_a = 1U , k_ra8_dfu_action_jump_b = 2U } |
| Outcome of the reset-time boot decision. More... | |
Functions | |
| uint32_t | ra8_dfu_crc32 (const uint8_t *data, uint32_t len) |
| Compute the IEEE-802.3 CRC32 of a byte range (software). | |
| bool | ra8_dfu_hdr_valid (const ra8_dfu_img_hdr_t *hdr, uint32_t computed_crc) |
| Decide whether a slot header describes a valid bootable image. | |
| bool | ra8_dfu_run_target_valid (uint32_t entry, uint32_t img_len) |
| Decide whether a validated slot's image may be copied-to-run. | |
| void | ra8_dfu_launch (uintptr_t src, uint32_t img_len, uint32_t entry) |
| Copy an image to the SRAM run base and branch to it (copy-to-run). | |
| ra8_dfu_slot_t | ra8_dfu_select_slot (bool a_valid, uint32_t a_seq, bool b_valid, uint32_t b_seq) |
| Pick the active slot from the two slots' validity + sequence. | |
| ra8_dfu_action_t | ra8_dfu_boot_decide (bool dfu_trigger, bool a_valid, uint32_t a_seq, bool b_valid, uint32_t b_seq) |
| Reset-time boot decision: jump to a slot, or enter DFU. | |
| uintptr_t | ra8_dfu_slot_base (ra8_dfu_slot_t slot) |
| Return the MRAM base address of a slot. | |
| ra8_dfu_slot_t | ra8_dfu_other_slot (ra8_dfu_slot_t slot) |
| Return the opposite slot (A<->B). | |
| ra8_err_t | ra8_dfu_read_header (ra8_dfu_slot_t slot, ra8_dfu_img_hdr_t *out_hdr) |
| Copy a slot's 32-byte header out of MRAM. | |
| bool | ra8_dfu_slot_valid (ra8_dfu_slot_t slot) |
| Validate a slot live: header magic/length/CRC over its real image. | |
| ra8_err_t | ra8_dfu_slot_seq (ra8_dfu_slot_t slot, uint32_t *out_seq) |
| Read a slot header's sequence number (0 if the magic is wrong). | |
| ra8_err_t | ra8_dfu_program_prepare (ra8_dfu_slot_t inactive) |
| Open ra8_flash and fence all writes to one slot's window. | |
| ra8_err_t | ra8_dfu_program_image (ra8_dfu_slot_t inactive, uint32_t img_offset, const uint8_t *data, uint32_t len) |
| Program one image chunk into the inactive slot's body. | |
| ra8_err_t | ra8_dfu_program_commit (ra8_dfu_slot_t inactive, uint32_t img_len, uint32_t seq) |
| Finalize a slot: CRC the programmed body, then program the header. | |
| ra8_err_t | ra8_dfu_program_verify (ra8_dfu_slot_t slot) |
| Read-back verify: re-CRC a slot's body against its stored header. | |
Controller-agnostic USB-DFU MRAM bootloader core for the RA8D2.
Shared core behind the real dfu_bootloader app and the two bidirectional HIL self-loop twins (dfu_selftest_hs_host / dfu_selftest_fs_host). It bundles three concerns:
The bootloader is 128 KiB so a full USBX + ThreadX DFU device fits inside the immutable region; the two app slots fill the rest of the 1 MiB array.
A slot is staging only: the image body occupies [slot_base, slot_base + img_len) and the 32-byte header is the slot's LAST page (slot_base + k_ra8_dfu_hdr_offset), programmed last so a torn download leaves an invalid (CRC-mismatching) slot rather than a half-valid one. On a valid-slot boot the bootloader COPIES the body to a single fixed SRAM run base (k_ra8_dfu_run_base) and launches it there. A payload is therefore linked ONCE, at the run base, and the identical image boots from either slot – no per-slot build and no "which slot am I building for?" footgun. This sidesteps true position-independent code (ROPI/RWPI), which cannot relocate the absolute function pointers this codebase's interface structs store in .rodata.
The header's entry records that run base. The bootloader copies to the trusted k_ra8_dfu_run_base constant (not to entry), so a corrupted entry cannot redirect the copy – it only fails the ra8_dfu_run_target_valid cross-check, which drops the boot to DFU. (Header-last is kept purely for the torn-write atomicity above; the old VTOR-alignment reason no longer applies, since VTOR is set to the aligned run base, not the slot.)
The bootloader is never swapped (no BTFLG / startup-area swap); it reads both slot headers and jumps to the valid slot with the higher sequence number in software. A freshly-written bad slot fails its CRC, so the older valid slot is chosen automatically – brick-safe by construction, and SWD recovery is always available because the bootloader region is never written.
Definition in file ra8_dfu.h.
| enum ra8_dfu_action_t : uint8_t |
| enum ra8_dfu_layout_t : uint32_t |
Fixed MRAM bank-layout addresses and image-header constants.
The 1 MiB code-MRAM window splits into a 64 KiB immutable bootloader plus two equal 480 KiB application slots. Every numeric address / size / magic the core needs is named here (CLAUDE.md "C23 typed enums"; NASA Rule 8). HUM Ch 59 "Code-MRAM" p 3541 sites the window.
| enum ra8_dfu_run_t : uint32_t |
Fixed SRAM copy-to-run execution base.
On a valid-slot boot the bootloader copies the staged image body out of the slot to this single SRAM address and launches it there, so every payload is linked at exactly this base and one image runs from either slot. The value clears the bootloader's own low-SRAM footprint (< 64 KiB) with margin, and a maximum k_ra8_dfu_img_max image copied here ends well below the bootloader's stack at the top of SRAM – so the copy never clobbers live bootloader state. The payload's linker ORIGIN MUST equal this value.
| Enumerator | |
|---|---|
| k_ra8_dfu_run_base | SRAM copy-to-run / payload link base. |
| enum ra8_dfu_slot_t : uint8_t |
| ra8_dfu_action_t ra8_dfu_boot_decide | ( | bool | dfu_trigger, |
| bool | a_valid, | ||
| uint32_t | a_seq, | ||
| bool | b_valid, | ||
| uint32_t | b_seq ) |
Reset-time boot decision: jump to a slot, or enter DFU.
Returns k_ra8_dfu_action_dfu when the no-init DFU trigger is set or when neither slot is valid; otherwise maps ra8_dfu_select_slot onto a jump action. This is the single decision the bootloader's main branches on.
| [in] | dfu_trigger | true iff the no-init SRAM DFU-request magic was set. |
| [in] | a_valid | Slot A validity (ra8_dfu_hdr_valid result). |
| [in] | a_seq | Slot A header sequence number. |
| [in] | b_valid | Slot B validity (ra8_dfu_hdr_valid result). |
| [in] | b_seq | Slot B header sequence number. |
| k_ra8_dfu_action_dfu | Trigger set, or no valid slot. |
| k_ra8_dfu_action_jump_a | Boot Slot A. |
| k_ra8_dfu_action_jump_b | Boot Slot B. |
Definition at line 87 of file ra8_dfu_boot.c.
References k_ra8_dfu_action_dfu, k_ra8_dfu_action_jump_a, k_ra8_dfu_action_jump_b, k_ra8_dfu_slot_a, k_ra8_dfu_slot_b, and ra8_dfu_select_slot().
Referenced by blc_decide().
| uint32_t ra8_dfu_crc32 | ( | const uint8_t * | data, |
| uint32_t | len ) |
Compute the IEEE-802.3 CRC32 of a byte range (software).
Standard reflected CRC32 (poly 0xEDB88320, init 0xFFFFFFFF, final XOR 0xFFFFFFFF) – the same value crc32/zlib produce, so a host tool can pre-compute the header img_crc32. Pure and bounded: one pass over len bytes, no table state retained between calls.
| [in] | data | Source bytes; may be NULL only when len == 0. |
| [in] | len | Number of bytes to fold in. |
| 0x00000000 | Empty range (len == 0); init XOR final over zero bytes. |
Compute the IEEE-802.3 CRC32 of a byte range (software).
Definition at line 38 of file ra8_dfu_boot.c.
References k_ra8_dfu_crc_bits, k_ra8_dfu_crc_init, k_ra8_dfu_crc_lsb, k_ra8_dfu_crc_poly, and k_ra8_dfu_crc_xout.
Referenced by ra8_dfu_program_commit(), and ra8_dfu_slot_valid().
| bool ra8_dfu_hdr_valid | ( | const ra8_dfu_img_hdr_t * | hdr, |
| uint32_t | computed_crc ) |
Decide whether a slot header describes a valid bootable image.
A header is valid iff the magic matches, the length is a non-zero multiple of the 32-byte page within [k_ra8_dfu_page_size, k_ra8_dfu_img_max], and the supplied freshly-computed CRC equals the stored img_crc32. The caller computes computed_crc over the live slot image (ra8_dfu_crc32) before calling, so this function stays pure and host-testable.
| [in] | hdr | Header to test (may be NULL -> invalid). |
| [in] | computed_crc | CRC32 the caller computed over the image body. |
| true | Magic, length bounds/alignment, and CRC all pass. |
| false | hdr is NULL, or any check fails. |
Definition at line 55 of file ra8_dfu_boot.c.
References ra8_dfu_img_hdr_t::img_crc32, ra8_dfu_img_hdr_t::img_len, k_ra8_dfu_hdr_magic, k_ra8_dfu_img_max, k_ra8_dfu_page_size, and ra8_dfu_img_hdr_t::magic.
Referenced by ra8_dfu_slot_valid().
| void ra8_dfu_launch | ( | uintptr_t | src, |
| uint32_t | img_len, | ||
| uint32_t | entry ) |
Copy an image to the SRAM run base and branch to it (copy-to-run).
The shared copy-to-run hand-off (firmware only): validates entry / img_len with ra8_dfu_run_target_valid, copies img_len bytes from src to the trusted k_ra8_dfu_run_base, then sets the Secure VTOR to the run base, loads the image's initial MSP, and branches to its reset vector. Used by the dfu_bootloader (launching a slot body) and the dfu_copy_to_run HIL demo (launching an embedded image). The copy destination is always the run-base constant, never entry, so a corrupted entry cannot redirect it.
Interrupts are masked across the copy and switch; the launched image re-enables them after its own bring-up. Under RA8_OFF_TARGET the branch is elided (a host cannot reset MSP/VTOR), so the function validates and returns.
| [in] | src | Source image base (image body, vector table first); non-zero. |
| [in] | img_len | Image body length in bytes; a non-zero 32-byte multiple. |
| [in] | entry | The image's recorded run base; must equal k_ra8_dfu_run_base. |
Definition at line 97 of file ra8_dfu_launch.c.
References k_ra8_dfu_run_base, ra8_dfu_run_target_valid(), and ra8_scb_set_vtor().
Referenced by blc_boot_slot(), main(), and sb_tampered_rejected().
| ra8_dfu_slot_t ra8_dfu_other_slot | ( | ra8_dfu_slot_t | slot | ) |
Return the opposite slot (A<->B).
Inverts the slot identifier: Slot A returns Slot B and any other value (Slot B or k_ra8_dfu_slot_none) returns Slot A. Used by the DFU device and host glue to locate the inactive (write-target) slot when the active slot is known. Passing k_ra8_dfu_slot_none returns k_ra8_dfu_slot_a by the else branch, consistent with "no preference -> start with A".
| [in] | slot | Slot identifier (A or B). |
| k_ra8_dfu_slot_b | Input was k_ra8_dfu_slot_a. |
| k_ra8_dfu_slot_a | Input was k_ra8_dfu_slot_b or k_ra8_dfu_slot_none. |
Definition at line 117 of file ra8_dfu_program.c.
References k_ra8_dfu_slot_a, and k_ra8_dfu_slot_b.
Referenced by blc_decide().
|
nodiscard |
Finalize a slot: CRC the programmed body, then program the header.
Folds ra8_dfu_crc32 over the just-programmed body in MRAM, builds a ra8_dfu_img_hdr_t (magic, the given seq, img_len, that CRC, and entry = k_ra8_dfu_run_base), and programs the 32-byte header LAST into the slot's last page. After this the slot is bootable; a power loss before the header lands leaves the slot header erased (invalid) – never half-valid.
| [in] | inactive | Slot being committed. |
| [in] | img_len | Total image-body length programmed; 32-byte multiple. |
| [in] | seq | Sequence number to stamp (caller uses other-slot seq + 1). |
| k_ra8_ok | Header programmed; slot now valid. |
| k_ra8_err_invalid_arg | inactive none/invalid or img_len out of range. |
| k_ra8_err_hw_error | Controller reported a program error. |
Definition at line 226 of file ra8_dfu_program.c.
References ra8_dfu_img_hdr_t::entry, ra8_dfu_img_hdr_t::img_crc32, ra8_dfu_img_hdr_t::img_len, k_ra8_dfu_hdr_magic, k_ra8_dfu_hdr_offset, k_ra8_dfu_hdr_size, k_ra8_dfu_img_max, k_ra8_dfu_page_size, k_ra8_dfu_run_base, k_ra8_err_invalid_arg, ra8_dfu_img_hdr_t::magic, priv_dfu_write_secure(), ra8_dfu_crc32(), ra8_dfu_slot_base(), and ra8_dfu_img_hdr_t::seq.
|
nodiscard |
Program one image chunk into the inactive slot's body.
Writes len bytes at slot_base + img_offset through the SECURE MRAM gate (k_ra8_flash_world_s). Each 32-byte page is erased to 0xFF then programmed, IRQ-masked, by the SRAM-resident internal_write_secure (ra8_dfu_program.c). The header page (the slot's last page, at slot_base + k_ra8_dfu_hdr_offset) is left untouched until ra8_dfu_program_commit, so a torn download never leaves a valid-looking header over a partial image.
| [in] | inactive | Slot being programmed (must match ra8_dfu_program_prepare). |
| [in] | img_offset | Byte offset into the image body; 32-byte aligned. |
| [in] | data | Source bytes (non-NULL). |
| [in] | len | Length in bytes; non-zero multiple of k_ra8_dfu_page_size. |
| k_ra8_ok | Chunk programmed. |
| k_ra8_err_null_ptr | data is NULL. |
| k_ra8_err_invalid_arg | inactive none/invalid, bad alignment, or the chunk would exceed k_ra8_dfu_img_max. |
| k_ra8_err_out_of_range | The access window rejected the address. |
| k_ra8_err_hw_error | Controller reported a program error. |
Definition at line 203 of file ra8_dfu_program.c.
References k_ra8_dfu_img_max, k_ra8_dfu_page_size, k_ra8_err_invalid_arg, k_ra8_err_null_ptr, priv_dfu_write_secure(), and ra8_dfu_slot_base().
|
nodiscard |
Open ra8_flash and fence all writes to one slot's window.
Opens the MRAM controller (ra8_flash_open) and sets the software access window (ra8_flash_set_window) to exactly the inactive slot so any stray write outside it is rejected. It does NOT erase: there is no full-slot erase (which would block USB long enough to time out the host's DFU_GETSTATUS), and no header pre-erase either. Instead ra8_dfu_program_image erases each 32-byte page to 0xFF immediately before programming its body, and ra8_dfu_program_commit writes the header LAST – so a torn download leaves the OLD header over a NEW partial image, whose CRC will not match -> invalid. Brick defense: the bootloader region and the active slot are outside the window and untouchable.
| [in] | inactive | The slot to (re)program (A or B; must NOT be the running one). |
| k_ra8_ok | Slot opened and the write window fenced to it. |
| k_ra8_err_invalid_arg | inactive is none/invalid. |
| k_ra8_err_hw_error | Controller bring-up reported an error. |
Definition at line 181 of file ra8_dfu_program.c.
References k_ra8_dfu_slot_size, k_ra8_err_invalid_arg, k_ra8_ok, ra8_dfu_slot_base(), ra8_flash_open(), ra8_flash_set_window(), and s_ra8_dfu_flash_cfg.
|
nodiscard |
Read-back verify: re-CRC a slot's body against its stored header.
| [in] | slot | Slot to verify (A or B). |
| k_ra8_ok | Header valid and the body CRC matches. |
| k_ra8_err_invalid_arg | slot is none/invalid. |
| k_ra8_err_crc_mismatch | Header invalid or the recomputed CRC differs. |
Definition at line 252 of file ra8_dfu_program.c.
References k_ra8_err_crc_mismatch, k_ra8_err_invalid_arg, k_ra8_ok, ra8_dfu_slot_base(), and ra8_dfu_slot_valid().
|
nodiscard |
Copy a slot's 32-byte header out of MRAM.
| [in] | slot | Slot to read (A or B). |
| [out] | out_hdr | Destination header (non-NULL). |
| k_ra8_ok | Header copied. |
| k_ra8_err_invalid_arg | slot is none/invalid. |
| k_ra8_err_null_ptr | out_hdr is NULL. |
Definition at line 122 of file ra8_dfu_program.c.
References k_ra8_dfu_hdr_offset, k_ra8_err_invalid_arg, k_ra8_err_null_ptr, k_ra8_ok, memcpy(), and ra8_dfu_slot_base().
Referenced by blc_boot_slot(), ra8_dfu_slot_seq(), and ra8_dfu_slot_valid().
| bool ra8_dfu_run_target_valid | ( | uint32_t | entry, |
| uint32_t | img_len ) |
Decide whether a validated slot's image may be copied-to-run.
The boot-time cross-check before the bootloader copies a slot body to SRAM and launches it: the header's entry must be the fixed k_ra8_dfu_run_base (so the image was linked for the run base, and a corrupted entry is caught), and img_len must be a non-zero 32-byte multiple within [k_ra8_dfu_page_size, k_ra8_dfu_img_max] (so the copy length is sane and the image fits the SRAM run window). Pure – the bootloader still copies to the trusted k_ra8_dfu_run_base constant, never to entry itself.
| [in] | entry | The candidate image's header entry field. |
| [in] | img_len | The candidate image's body length, bytes. |
| true | entry == k_ra8_dfu_run_base and img_len is in range/aligned. |
| false | entry is not the run base, or img_len is zero / too large / not a 32-byte multiple. |
Definition at line 67 of file ra8_dfu_boot.c.
References k_ra8_dfu_img_max, k_ra8_dfu_page_size, and k_ra8_dfu_run_base.
Referenced by ra8_dfu_launch().
| ra8_dfu_slot_t ra8_dfu_select_slot | ( | bool | a_valid, |
| uint32_t | a_seq, | ||
| bool | b_valid, | ||
| uint32_t | b_seq ) |
Pick the active slot from the two slots' validity + sequence.
The valid slot with the higher sequence number wins; Slot A wins a tie (deterministic). If only one slot is valid, it wins; if neither is valid, k_ra8_dfu_slot_none is returned so the caller enters DFU.
| [in] | a_valid | true iff Slot A passed ra8_dfu_hdr_valid. |
| [in] | a_seq | Slot A header sequence number. |
| [in] | b_valid | true iff Slot B passed ra8_dfu_hdr_valid. |
| [in] | b_seq | Slot B header sequence number. |
| k_ra8_dfu_slot_a | A valid and (B invalid or a_seq >= b_seq). |
| k_ra8_dfu_slot_b | B valid and (A invalid or b_seq > a_seq). |
| k_ra8_dfu_slot_none | Neither slot valid. |
Definition at line 75 of file ra8_dfu_boot.c.
References k_ra8_dfu_slot_a, k_ra8_dfu_slot_b, and k_ra8_dfu_slot_none.
Referenced by blc_decide(), and ra8_dfu_boot_decide().
| uintptr_t ra8_dfu_slot_base | ( | ra8_dfu_slot_t | slot | ) |
Return the MRAM base address of a slot.
Maps the ra8_dfu_slot_t identifier to its fixed MRAM window start address. Slot A maps to k_ra8_dfu_slot_a_base and Slot B maps to k_ra8_dfu_slot_b_base. Returns 0 for k_ra8_dfu_slot_none or any out-of-range value so callers can detect the sentinel without a separate validity test.
| [in] | slot | Slot identifier. |
| k_ra8_dfu_slot_a_base | Slot A (slot == k_ra8_dfu_slot_a). |
| k_ra8_dfu_slot_b_base | Slot B (slot == k_ra8_dfu_slot_b). |
| 0 | slot is k_ra8_dfu_slot_none or out of range. |
Definition at line 99 of file ra8_dfu_program.c.
References k_ra8_dfu_slot_a, k_ra8_dfu_slot_a_base, k_ra8_dfu_slot_b, k_ra8_dfu_slot_b_base, and k_ra8_dfu_slot_none.
Referenced by blc_boot_slot(), ra8_dfu_program_commit(), ra8_dfu_program_image(), ra8_dfu_program_prepare(), ra8_dfu_program_verify(), ra8_dfu_read_header(), and ra8_dfu_slot_valid().
|
nodiscard |
Read a slot header's sequence number (0 if the magic is wrong).
| [in] | slot | Slot to read (A or B). |
| [out] | out_seq | Destination sequence (non-NULL); 0 when magic mismatches. |
| k_ra8_ok | *out_seq populated (0 if header magic wrong). |
| k_ra8_err_invalid_arg | slot is none/invalid. |
| k_ra8_err_null_ptr | out_seq is NULL. |
Definition at line 167 of file ra8_dfu_program.c.
References k_ra8_dfu_hdr_magic, k_ra8_err_null_ptr, k_ra8_ok, ra8_dfu_img_hdr_t::magic, ra8_dfu_read_header(), and ra8_dfu_img_hdr_t::seq.
Referenced by blc_decide().
| bool ra8_dfu_slot_valid | ( | ra8_dfu_slot_t | slot | ) |
Validate a slot live: header magic/length/CRC over its real image.
Reads the slot header, then folds ra8_dfu_crc32 over the live image body in MRAM and applies ra8_dfu_hdr_valid.
| [in] | slot | Slot to validate (A or B). |
| true | Header + image CRC pass. |
| false | slot is none/invalid, or any check fails. |
Validate a slot live: header magic/length/CRC over its real image.
Definition at line 142 of file ra8_dfu_program.c.
References ra8_dfu_img_hdr_t::img_len, k_ra8_dfu_img_max, k_ra8_dfu_page_size, k_ra8_ok, ra8_dfu_crc32(), ra8_dfu_hdr_valid(), ra8_dfu_read_header(), and ra8_dfu_slot_base().
Referenced by blc_decide(), dfu_host_pass(), and ra8_dfu_program_verify().