ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_dfu_program.c
Go to the documentation of this file.
1
20
21#include <string.h>
22
23#include "ra8_dfu.h"
24#include "ra8_dfu_internal.h"
25#include "ra8_flash.h"
26#include "ra8_register_guard.h"
27
38
46typedef enum : uint8_t {
49
52 .mrcfreq_mhz = (uint16_t)k_ra8_dfu_prog_mrcfreq_mhz,
53 .mrefreq_mhz = (uint8_t)k_ra8_dfu_prog_mrefreq_mhz,
54 .prefetch_en = true,
55 .ecc_encoder_enable = true,
56 .ecc_decoder_enable = true,
57};
58
61RA8_PRIV ra8_err_t priv_dfu_write_secure(uintptr_t addr, const uint8_t* src, uint32_t len)
62{
63 if ((src == nullptr) || (len == 0U)) {
65 }
66 /* Erased-state pattern in SRAM. ra8_flash_erase_block would read the driver's
67 * 0xFF constant out of code-MRAM while the array is busy programming, which
68 * faults the bus (ra8_flash.h SRAM-resident warning); sourcing 0xFF from this
69 * stack buffer keeps every operand in SRAM. */
70 uint8_t ones[k_ra8_dfu_page_size];
71 (void)memset(ones, (int)k_ra8_dfu_prog_erased_byte, sizeof(ones));
72 uint32_t off = 0U;
73 /* NASA Rule 2: bound is the caller-validated, page-aligned len. */
74 while (off < len) {
75 uint32_t chunk = len - off;
76 if (chunk > (uint32_t)k_ra8_dfu_page_size) {
77 chunk = (uint32_t)k_ra8_dfu_page_size;
78 }
79 /* Erase the page to all-ones first: code-MRAM cannot be reliably
80 * re-programmed over stale (non-0xFF) contents with ECC enabled, so each
81 * page is driven to the erased baseline before the body is written. Both
82 * the erase and the program run IRQ-masked so no ISR fetches code-MRAM
83 * while the array is busy. */
86 ra8_err_t err = ra8_flash_write_block((uint32_t)(addr + off), ones, chunk, k_ra8_flash_world_s);
87 if (err == k_ra8_ok) {
88 err = ra8_flash_write_block((uint32_t)(addr + off), &src[off], chunk, k_ra8_flash_world_s);
89 }
91 if (err != k_ra8_ok) {
92 return err;
93 }
94 off += chunk;
95 }
96 return k_ra8_ok;
97}
98
100{
101 uintptr_t base = 0U;
102 switch (slot) {
103 case k_ra8_dfu_slot_a:
104 base = (uintptr_t)k_ra8_dfu_slot_a_base;
105 break;
106 case k_ra8_dfu_slot_b:
107 base = (uintptr_t)k_ra8_dfu_slot_b_base;
108 break;
110 default:
111 base = 0U;
112 break;
113 }
114 return base;
115}
116
121
123{
124 if (out_hdr == nullptr) {
125 return k_ra8_err_null_ptr;
126 }
127 const uintptr_t base = ra8_dfu_slot_base(slot);
128 if (base == 0U) {
130 }
131 /* HUM Ch 59.1 "Code-MRAM" p 3543 -- the slot header is the LAST 32-byte data
132 * page of the slot (so the app vector table can sit at the aligned base);
133 * this is a plain MRAM data read, not a control register access. */
134 (void)memcpy(out_hdr, (const void*)(base + (uintptr_t)k_ra8_dfu_hdr_offset), sizeof(*out_hdr));
135 return k_ra8_ok;
136}
137
143{
144 ra8_dfu_img_hdr_t hdr = {};
145 const uintptr_t base = ra8_dfu_slot_base(slot);
146 if (base == 0U) {
147 return false;
148 }
149 if (ra8_dfu_read_header(slot, &hdr) != k_ra8_ok) {
150 return false;
151 }
152 /* Only fold the CRC over a length that is in-range + aligned; otherwise
153 * ra8_dfu_hdr_valid rejects on the length anyway, and we must not read past
154 * the slot. */
155 uint32_t crc = 0U;
156 /* Only fold the CRC over an in-range, page-aligned length; the false arms
157 * guard a stray MRAM over-read on a corrupt header (each condition is
158 * exercised for MC/DC by crafting the slot header directly -- see
159 * test_slot_valid_len_bound_mcdc). */
160 if ((hdr.img_len != 0U) && (hdr.img_len <= (uint32_t)k_ra8_dfu_img_max) &&
161 ((hdr.img_len % (uint32_t)k_ra8_dfu_page_size) == 0U)) {
162 crc = ra8_dfu_crc32((const uint8_t*)base, hdr.img_len);
163 }
164 return ra8_dfu_hdr_valid(&hdr, crc);
165}
166
168{
169 if (out_seq == nullptr) {
170 return k_ra8_err_null_ptr;
171 }
172 ra8_dfu_img_hdr_t hdr = {};
173 const ra8_err_t err = ra8_dfu_read_header(slot, &hdr);
174 if (err != k_ra8_ok) {
175 return err;
176 }
177 *out_seq = (hdr.magic == (uint32_t)k_ra8_dfu_hdr_magic) ? hdr.seq : 0U;
178 return k_ra8_ok;
179}
180
182{
183 const uintptr_t base = ra8_dfu_slot_base(inactive);
184 if (base == 0U) {
186 }
187 const uintptr_t high = base + (uintptr_t)k_ra8_dfu_slot_size;
189 if (err != k_ra8_ok) {
190 return err;
191 }
192 /* Fence every subsequent write to this one slot -- the bootloader and the
193 * active slot are outside [base, high) and cannot be touched. No erase: MRAM
194 * is byte-alterable, so ::ra8_dfu_program_image writes the body directly and
195 * ::ra8_dfu_program_commit overwrites the header. A torn download leaves the
196 * OLD header over a NEW partial image, whose CRC will not match -> invalid,
197 * so torn-write safety holds without an erase. (Erasing here would also read
198 * the driver's 0xFF constant out of code-MRAM while the array is busy
199 * programming, faulting the bus -- see ra8_flash.h's SRAM-resident warning.) */
200 return ra8_flash_set_window(base, high);
201}
202
204 uint32_t img_offset,
205 const uint8_t* data,
206 uint32_t len)
207{
208 if (data == nullptr) {
209 return k_ra8_err_null_ptr;
210 }
211 const uintptr_t base = ra8_dfu_slot_base(inactive);
212 if (base == 0U) {
214 }
215 if ((len == 0U) || ((len % (uint32_t)k_ra8_dfu_page_size) != 0U) ||
216 ((img_offset % (uint32_t)k_ra8_dfu_page_size) != 0U)) {
218 }
219 if (((uint64_t)img_offset + (uint64_t)len) > (uint64_t)k_ra8_dfu_img_max) {
221 }
222 const uintptr_t dst = base + (uintptr_t)img_offset;
223 return priv_dfu_write_secure(dst, data, len);
224}
225
226ra8_err_t ra8_dfu_program_commit(ra8_dfu_slot_t inactive, uint32_t img_len, uint32_t seq)
227{
228 const uintptr_t base = ra8_dfu_slot_base(inactive);
229 if (base == 0U) {
231 }
232 if ((img_len == 0U) || (img_len > (uint32_t)k_ra8_dfu_img_max) ||
233 ((img_len % (uint32_t)k_ra8_dfu_page_size) != 0U)) {
235 }
236 const uint32_t body_crc = ra8_dfu_crc32((const uint8_t*)base, img_len);
237 ra8_dfu_img_hdr_t hdr = {};
238 hdr.magic = (uint32_t)k_ra8_dfu_hdr_magic;
239 hdr.seq = seq;
240 hdr.img_len = img_len;
241 hdr.img_crc32 = body_crc;
242 hdr.entry = (uint32_t)k_ra8_dfu_run_base;
243 /* Header programmed LAST, into the slot's last page, so a torn write leaves
244 * the header erased (invalid), never a valid header over a partial image.
245 * internal_write_secure masks IRQs across the page program so no ISR fetches
246 * code-MRAM while the header page is being written. */
247 return priv_dfu_write_secure(base + (uintptr_t)k_ra8_dfu_hdr_offset,
248 (const uint8_t*)&hdr,
249 (uint32_t)k_ra8_dfu_hdr_size);
250}
251
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
Controller-agnostic USB-DFU MRAM bootloader core for the RA8D2.
ra8_dfu_slot_t
Application-slot identifier.
Definition ra8_dfu.h:133
@ k_ra8_dfu_slot_b
Slot B (0x02090000).
Definition ra8_dfu.h:135
@ k_ra8_dfu_slot_a
Slot A (0x02020000).
Definition ra8_dfu.h:134
@ k_ra8_dfu_slot_none
No valid slot present.
Definition ra8_dfu.h:136
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.
@ k_ra8_dfu_page_size
MRAM program page (32 bytes).
Definition ra8_dfu.h:101
@ k_ra8_dfu_hdr_offset
Header offset (slot's last page).
Definition ra8_dfu.h:104
@ k_ra8_dfu_slot_a_base
Slot A base (app vectors here).
Definition ra8_dfu.h:98
@ k_ra8_dfu_hdr_magic
Valid-image header magic ("RA8D").
Definition ra8_dfu.h:105
@ k_ra8_dfu_slot_size
Per-slot size (448 KiB).
Definition ra8_dfu.h:100
@ k_ra8_dfu_slot_b_base
Slot B base (app vectors here).
Definition ra8_dfu.h:99
@ k_ra8_dfu_hdr_size
Image header size (32 bytes).
Definition ra8_dfu.h:102
@ k_ra8_dfu_img_max
Max image bytes (slot - header).
Definition ra8_dfu.h:103
@ k_ra8_dfu_run_base
SRAM copy-to-run / payload link base.
Definition ra8_dfu.h:126
TU-shared surface for the DFU MRAM program/verify implementation.
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_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 priv_dfu_write_secure(uintptr_t addr, const uint8_t *src, uint32_t len)
Implementation of priv_dfu_write_secure() – IRQ-masked, page-at-a-time erase-then-program through the...
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_read_header(ra8_dfu_slot_t slot, ra8_dfu_img_hdr_t *out_hdr)
Copy a slot's 32-byte header out of MRAM.
ra8_dfu_prog_fill_t
MRAM erased-state fill byte.
@ k_ra8_dfu_prog_erased_byte
Erased-state byte for an MRAM page.
uintptr_t ra8_dfu_slot_base(ra8_dfu_slot_t slot)
Return the MRAM base address of a slot.
static const ra8_flash_cfg_t s_ra8_dfu_flash_cfg
Default controller bring-up descriptor for ra8_dfu_program_prepare.
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_dfu_slot_t ra8_dfu_other_slot(ra8_dfu_slot_t slot)
Return the opposite slot (A<->B).
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.
ra8_dfu_prog_cfg_t
MRAM clock notifications used to (re)open the controller.
@ k_ra8_dfu_prog_mrefreq_mhz
Extra-MRAM clock notification (MHz).
@ k_ra8_dfu_prog_mrcfreq_mhz
Code-MRAM clock notification (MHz).
bool ra8_dfu_slot_valid(ra8_dfu_slot_t slot)
Implementation of ra8_dfu_slot_valid() – guards the CRC read so a bogus img_len cannot drive an out-o...
@ k_ra8_err_crc_mismatch
CRC mismatch detected on received data.
Definition ra8_err.h:423
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Code MRAM + Extra MRAM + Option-Setting driver – DANGEROUS, brick-capable.
ra8_err_t ra8_flash_write_block(uint32_t mram_addr, const uint8_t *src, uint32_t len, ra8_flash_world_t world)
Program 1..32 contiguous bytes into one MRAM page.
Definition ra8_flash.c:709
ra8_err_t ra8_flash_set_window(uintptr_t low, uintptr_t high)
Configure the soft access window enforced by write/erase.
ra8_err_t ra8_flash_open(const ra8_flash_cfg_t *cfg)
FSP-parity bring-up: equivalent to ra8_flash_init.
@ k_ra8_flash_world_s
Open MRCPC1 (secure half).
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
IRQ-masked read-modify-write helper for shared registers.
static void ra8_register_guard_exit(const ra8_register_guard_t *guard)
Exit a critical section: restore PRIMASK.
static void ra8_register_guard_enter(ra8_register_guard_t *guard)
Enter a critical section: save PRIMASK, mask interrupts.
32-byte application-image header at the base of each slot.
Definition ra8_dfu.h:174
uint32_t img_crc32
CRC32 (IEEE) over the image body.
Definition ra8_dfu.h:178
uint32_t magic
Must equal k_ra8_dfu_hdr_magic.
Definition ra8_dfu.h:175
uint32_t seq
Monotonic sequence; higher valid slot wins.
Definition ra8_dfu.h:176
uint32_t entry
SRAM run base (== k_ra8_dfu_run_base).
Definition ra8_dfu.h:179
uint32_t img_len
Image body length in bytes (32-byte multiple).
Definition ra8_dfu.h:177
Initialisation descriptor for ra8_flash_init.
Opaque save-restore handle for IRQ masking.