ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_tz_secure_boot.h File Reference

FSP-style TrustZone secure-boot for the Cortex-M85 (CPU0). More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_err.h"
Include dependency graph for ra8_tz_secure_boot.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ra8_ns_rot_header_t
 Self-describing RoT header the NS linker embeds in the NS image. More...

Enumerations

enum  ra8_tz_sau_region_t : uint8_t {
  k_ra8_tz_sau_region_code_nsc = 0U ,
  k_ra8_tz_sau_region_ns_mram = 1U ,
  k_ra8_tz_sau_region_sram_nsc = 2U ,
  k_ra8_tz_sau_region_ns_sram = 3U ,
  k_ra8_tz_sau_region_ns_periph = 4U ,
  k_ra8_tz_sau_region_count = 5U
}
 Canonical SAU region indices for the RA8D2 secure-boot layout. More...
enum  ra8_tz_secure_boot_step_t : uint8_t {
  k_ra8_tz_secure_boot_step_idle = 0U ,
  k_ra8_tz_secure_boot_step_sau_done = 1U ,
  k_ra8_tz_secure_boot_step_prcr_unlocked = 2U ,
  k_ra8_tz_secure_boot_step_ipcsar_written = 3U ,
  k_ra8_tz_secure_boot_step_prcr_relocked = 4U ,
  k_ra8_tz_secure_boot_step_blxns_armed = 5U ,
  k_ra8_tz_secure_boot_step_branched = 6U
}
 Progress markers stamped into g_ra8_tz_secure_boot_step. More...
enum  ra8_tz_ns_rot_header_magic_t : uint32_t { k_ra8_tz_ns_rot_header_magic = 0x3152534EU }
 Magic marker of the Non-Secure signed-image RoT header. More...
enum  ra8_tz_ns_rot_header_offset_t : uint8_t { k_ra8_tz_ns_rot_header_offset = 0x40U }
 Byte offset of the ra8_ns_rot_header_t from the NS image base. More...

Functions

uint32_t ra8_tz_ns_signed_body_len (const uint32_t *ns_vector_table)
 Read the NS image's self-describing signed-body length.
ra8_err_t ra8_tz_secure_boot_sau_init (void)
 Programme the SAU regions documented in ra8_tz_sau_region_t.
ra8_err_t ra8_tz_secure_boot_security_init (uint32_t ipcsar_value, uint32_t ipcpar_value)
 Unlock PRCR_S.PRC4 and write IPCSAR + IPCPAR.
ra8_err_t ra8_tz_secure_boot_jump_ns (const uint32_t *ns_vector_table)
 Switch to NS state and jump to the NS image's reset vector.
ra8_err_t ra8_tz_secure_boot_run (uint32_t ipcsar_value, uint32_t ipcpar_value, const uint32_t *ns_vector_table)
 Run the full secure-boot sequence and (on target) BLXNS.
void ra8_tz_secure_boot_host_reset (void)
 Reset the host-side state for unit-test fixtures.
ra8_tz_secure_boot_step_t ra8_tz_secure_boot_get_step (void)
 Read the boot progress counter (debug / test).
uint32_t ra8_tz_secure_boot_host_blxns_target (void)
 Read the captured BLXNS target (host / test).

Detailed Description

FSP-style TrustZone secure-boot for the Cortex-M85 (CPU0).

Tag
[Ring 1 / Boot] {World: S}

Mirrors the FSP R_BSP_SAUInit + R_BSP_SecurityInit + R_BSP_NonSecureEnter triplet without copying any FSP code. The entry point is ra8_tz_secure_boot_run, which is called from the per-app trustzone_init.c when RA8_TRUSTZONE_ENABLE is on.

The boot sequence is:

  1. Programme the SAU with the canonical RA8D2 partition (see ra8_tz_sau_region_t for the layout).
  2. Enable the SAU with ALLNS = 0 (default-deny).
  3. Unlock CPSCU writes via PRCR_S.PRC4 (HUM Ch 13.2.1 – write key 0xA510 to address 0x4001E3FA).
  4. Write IPCSAR so the channels CPU1 (always-NS on this chip) must reach are tagged NS in the per-channel SAIPCIRn bits.
  5. Re-lock PRCR_S.
  6. Configure the NS main stack pointer via MSP_NS and jump to the NS reset vector with BLXNS.

On a host (RA8_OFF_TARGET defined) the module exposes a pure-C implementation that writes to a mocked CPSCU window, so the unit tests under tests/ can drive the IPCSAR-unlock sequence without touching real hardware.

TrustZone Safety:
  • Validates: SAU_TYPE.SREGION reports >= 5 regions before programming the partition (the FSP-style layout needs 5).
  • Validates: ns_vector_table is non-NULL and word-aligned.
  • Trusts: the boot ROM left the SAU disabled and the IDAU in its documented reset state (bit-28 split).
  • Denies: any return path from ra8_tz_secure_boot_run – the function is [[noreturn]] once the BLXNS branches out.
See also
NSR1 – The Non-Secure Image RoT Header – the full NSR1 wire-format specification (rationale, algorithms, worked example, failure modes).

Definition in file ra8_tz_secure_boot.h.

Enumeration Type Documentation

◆ ra8_tz_ns_rot_header_magic_t

Magic marker of the Non-Secure signed-image RoT header.

The 32-bit little-endian word the NS linker emits as the first field of the ra8_ns_rot_header_t. It is the ASCII string "NSR1" (bytes N,S,R,1), so a hex dump of the flashed NS image reads the marker in order. A mismatched magic means the NS image was not built with the RoT header (or is corrupt) and the secure boot default-denies the BLXNS.

Enumerator
k_ra8_tz_ns_rot_header_magic 

ASCII "NSR1" (little-endian).

Definition at line 122 of file ra8_tz_secure_boot.h.

◆ ra8_tz_ns_rot_header_offset_t

Byte offset of the ra8_ns_rot_header_t from the NS image base.

The header sits immediately after the 16-slot ARMv8-M Non-Secure vector table (16 * 4 = 64 bytes), so 0x40 is the first free, deterministic offset from ns_vector_table. Both the NS linker script (which places .ns_rot_header here and ASSERTs the placement) and the Secure verifier agree on this constant; it is the whole cross-image contract, so the NS vector table MUST remain exactly 16 entries.

Enumerator
k_ra8_tz_ns_rot_header_offset 

Offset after the 16-slot NS vectors.

Definition at line 138 of file ra8_tz_secure_boot.h.

◆ ra8_tz_sau_region_t

enum ra8_tz_sau_region_t : uint8_t

Canonical SAU region indices for the RA8D2 secure-boot layout.

The layout deliberately mirrors FSP's R_BSP_SAUInit – five regions are programmed and the SAU is enabled with ALLNS = 0. Region 3 (NSC veneer) points at the unused 0x10000000 IDAU alias rather than at the actual .gnu.sgstubs placement inside MRAM, because the latter has been bench-verified to brick the chip (see project memory project_sau_sgstubs_brick). With ALLNS = 0 and Region 3 pointing at an unused alias, callers must not emit any Non-Secure entries (cmse_nonsecure_entry) until a follow-up change moves the veneers into their own non-S-executable region.

Region 0 Code-Flash NSC alias 0x10000000..0x100FFFE0 NSC
Region 1 NS upper MRAM 0x02080000..0x020FFFE0 NS
Region 2 NSC SRAM alias 0x12000000..0x1200FFE0 NSC
Region 3 NS upper SRAM 0x22100000..0x221FFFE0 NS
Region 4 NS peripheral window 0x50000000..0x5FFFFFE0 NS
Enumerator
k_ra8_tz_sau_region_code_nsc 

NSC alias for code-flash veneers.

k_ra8_tz_sau_region_ns_mram 

Non-Secure upper MRAM (NS image).

k_ra8_tz_sau_region_sram_nsc 

NSC alias for SRAM veneers.

k_ra8_tz_sau_region_ns_sram 

Non-Secure upper SRAM (NS data).

k_ra8_tz_sau_region_ns_periph 

Non-Secure peripheral window.

k_ra8_tz_sau_region_count 

Number of programmed regions.

Definition at line 82 of file ra8_tz_secure_boot.h.

◆ ra8_tz_secure_boot_step_t

enum ra8_tz_secure_boot_step_t : uint8_t

Progress markers stamped into g_ra8_tz_secure_boot_step.

Mirrors the diagnostic counters in the existing cpu1_pingpong app – each step is set in-order as the secure boot makes forward progress, so a J-Link memprobe can pinpoint exactly where bring-up wedged if the BLXNS never fires.

Enumerator
k_ra8_tz_secure_boot_step_idle 

Pre-run sentinel.

k_ra8_tz_secure_boot_step_sau_done 

SAU regions programmed.

k_ra8_tz_secure_boot_step_prcr_unlocked 

PRCR_S.PRC4 set.

k_ra8_tz_secure_boot_step_ipcsar_written 

IPCSAR write landed.

k_ra8_tz_secure_boot_step_prcr_relocked 

PRCR_S.PRC4 cleared.

k_ra8_tz_secure_boot_step_blxns_armed 

MSP_NS / VTOR set up.

k_ra8_tz_secure_boot_step_branched 

BLXNS executed (host).

Definition at line 101 of file ra8_tz_secure_boot.h.

Function Documentation

◆ ra8_tz_ns_signed_body_len()

uint32_t ra8_tz_ns_signed_body_len ( const uint32_t * ns_vector_table)
nodiscard

Read the NS image's self-describing signed-body length.

Reads the ra8_ns_rot_header_t the NS linker embedded at k_ra8_tz_ns_rot_header_offset from ns_vector_table and returns its body_len field after confirming the header magic. This is the BLXNS boundary's analog of the DFU header's body-length word on the copy-to-run boundary: it tells the root-of-trust gate exactly how many bytes to hash and where the appended ra8_rot_trailer_t begins (ns_vector_table + body_len).

Reading body_len from the untrusted NS image is safe: a lie about it only changes which bytes are hashed, and no attacker can forge a valid ECDSA-P256 signature over any body without the held-out private key – so a wrong length simply default-denies at the signature check.

Parameters
[in]ns_vector_tableBase of the NS image (its vector table). May be NULL (handled: returns 0).
Returns
Signed body length in bytes, or 0 on any failure (deny sentinel).
Return values
0ns_vector_table is NULL, or the header magic is wrong (no RoT header present) – the caller must NOT BLXNS.
non-zeroThe body_len recorded in the NS RoT header.
Precondition
ns_vector_table addresses at least k_ra8_tz_ns_rot_header_offset + sizeof(ra8_ns_rot_header_t) bytes, or is NULL.
The NS image was linked with a .ns_rot_header section (RoT builds).
Postcondition
No state is mutated; the result depends only on the header bytes.
A 0 return unambiguously signals "no valid header" to the caller.
Note
Thread-safe (pure read; no statics). Performs no MMIO – it reads the already-resident NS image, so no HUM citation applies.
See also
ra8_ns_rot_header_t
ra8_rot_trailer_after
Since
0.1.0

Definition at line 510 of file ra8_tz_secure_boot.c.

References ra8_ns_rot_header_t::body_len, k_ra8_tz_ns_rot_header_magic, k_ra8_tz_ns_rot_header_offset, ra8_log_error, and s_tag.

Referenced by internal_ns_verify_or_deny().

◆ ra8_tz_secure_boot_get_step()

ra8_tz_secure_boot_step_t ra8_tz_secure_boot_get_step ( void )

Read the boot progress counter (debug / test).

Returns the most-recent ra8_tz_secure_boot_step_t value stored in the internal progress counter. Useful for J-Link memprobe scripts on the bench and for unit-test assertions on the host.

Returns
Current progress step.
Return values
k_ra8_tz_secure_boot_step_idlePre-run.
k_ra8_tz_secure_boot_step_sau_doneSAU init complete.
k_ra8_tz_secure_boot_step_ipcsar_writtenIPCSAR landed.
k_ra8_tz_secure_boot_step_branchedBLXNS executed.
Precondition
None.
None.
Postcondition
No state change.
Caller-visible state matches the most recent boot step.
Note
Thread-safe (single 32-bit load).
Since
0.1.0

Definition at line 691 of file ra8_tz_secure_boot.c.

References s_step.

Referenced by ra8_trustzone_init().

◆ ra8_tz_secure_boot_host_blxns_target()

uint32_t ra8_tz_secure_boot_host_blxns_target ( void )

Read the captured BLXNS target (host / test).

Host-only accessor: returns the NS reset vector that ra8_tz_secure_boot_jump_ns was last asked to branch to. On target builds this returns 0 because BLXNS does not return.

Returns
Captured reset-vector address, or 0 if not yet armed.
Return values
0BLXNS not yet armed (or running on target).
non-zeroNS reset vector that was loaded for BLXNS.
Precondition
None.
None.
Postcondition
No state change.
Caller-visible state matches the most recent BLXNS arm.
Note
Test-only on host; returns 0 on target.
Since
0.1.0

Definition at line 233 of file ra8_tz_secure_boot.c.

◆ ra8_tz_secure_boot_host_reset()

void ra8_tz_secure_boot_host_reset ( void )

Reset the host-side state for unit-test fixtures.

Host-only helper that clears the fake PRCR_S / IPCSAR / IPCPAR captures and the BLXNS target so each test starts from a known baseline. Cross-compiled (target) builds expose the symbol but the body is empty.

Precondition
None.
None.
Postcondition
Host state reset to k_ra8_tz_secure_boot_step_idle.
BLXNS target capture cleared.
Note
Test-only on host; no-op on target.
Since
0.1.0

Definition at line 225 of file ra8_tz_secure_boot.c.

References s_tag.

◆ ra8_tz_secure_boot_jump_ns()

ra8_err_t ra8_tz_secure_boot_jump_ns ( const uint32_t * ns_vector_table)
nodiscard

Switch to NS state and jump to the NS image's reset vector.

Reads the initial-SP slot of the NS vector table into MSP_NS, sets VTOR_NS to ns_vector_table, then performs a BLXNS to the reset-vector slot. The function is declared [[noreturn]] and does not return on hardware.

On target, a root-of-trust gate runs BEFORE VTOR_NS is armed and BEFORE the BLXNS: ra8_rot_verify_image must authenticate the Non-Secure image (SHA-256 + ECDSA-P256) against the provisioned root public key. This is default-deny – any failure (missing / malformed signature trailer, tampered body, or an invalid signature) returns the verify error and the function does NOT branch into the NS world. The signed body length is read from the NS image's own ra8_ns_rot_header_t (emitted by the NS linker at k_ra8_tz_ns_rot_header_offset, see ra8_tz_ns_signed_body_len), and the ra8_rot_trailer_t sits immediately after that body at ns_vector_table + body_len – the same self-describing [ body ] [ trailer ] layout the copy-to-run boundary uses.

On the host (RA8_OFF_TARGET defined) the function stamps the progress counter, sets s_ra8_tz_secure_boot_blxns_target to the supplied reset vector, and returns k_ra8_ok so unit tests can assert the documented transition state; the gate's decision logic is covered directly in tests/security/src/test_ra8_root_of_trust.c.

Parameters
[in]ns_vector_tablePointer to the NS image's vector table.
Returns
ra8_err_t On host only – never reached on target.
Return values
k_ra8_okOn host: BLXNS state captured.
k_ra8_err_null_ptrns_vector_table is NULL.
k_ra8_err_invalid_argns_vector_table is not 4-byte aligned, or the reset-vector slot holds an obviously bogus value (0 or 0xFFFFFFFF).
k_ra8_err_*On target: the root-of-trust gate denied the NS image (e.g. k_ra8_err_crc_mismatch for a bad signature) – BLXNS is not performed.
Precondition
ns_vector_table is non-NULL and 4-byte aligned.
ns_vector_table[0] (initial SP) and ns_vector_table[1] (reset vector) hold valid values for the NS image.
Postcondition
On hardware: control transfers to the NS image and never returns to the caller.
On host: s_ra8_tz_secure_boot_blxns_target captures the NS reset vector for test inspection.
Note
Not thread-safe; runs once at boot from a single-threaded context.
Since
0.1.0

Definition at line 612 of file ra8_tz_secure_boot.c.

References internal_ns_verify_or_deny(), internal_write32(), k_ra8_err_invalid_arg, k_ra8_ok, k_ra8_tz_scb_vtor_ns_addr, k_ra8_tz_secure_boot_step_blxns_armed, k_ra8_tz_secure_boot_step_branched, RA8_CHECK_NULL_PTR, ra8_log_error, ra8_log_error_val, s_step, and s_tag.

Referenced by main(), ra8_trustzone_init(), and ra8_tz_secure_boot_run().

◆ ra8_tz_secure_boot_run()

ra8_err_t ra8_tz_secure_boot_run ( uint32_t ipcsar_value,
uint32_t ipcpar_value,
const uint32_t * ns_vector_table )
nodiscard

Run the full secure-boot sequence and (on target) BLXNS.

Convenience wrapper: ra8_tz_secure_boot_sau_init -> ra8_tz_secure_boot_security_init -> ra8_tz_secure_boot_jump_ns, returning the first non-OK status.

Parameters
[in]ipcsar_valueForwarded to security_init.
[in]ipcpar_valueForwarded to security_init.
[in]ns_vector_tableForwarded to jump_ns.
Returns
ra8_err_t First failing step's status; never returns on target in the happy path.
Return values
k_ra8_okOn host happy path (BLXNS captured).
k_ra8_err_null_ptrns_vector_table is NULL.
k_ra8_err_not_supportedSAU_TYPE.SREGION < 5.
k_ra8_err_invalid_argNS vector table mis-aligned / invalid.
Precondition
Secure state, single-threaded boot context.
ns_vector_table non-NULL.
Postcondition
On hardware happy path: NS image running, no return.
On any failure step: SAU left in whatever partial state the failing call established; caller logs and halts.
Note
Not thread-safe; runs once at boot.
Since
0.1.0

Definition at line 671 of file ra8_tz_secure_boot.c.

References k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_tz_secure_boot_jump_ns(), ra8_tz_secure_boot_sau_init(), ra8_tz_secure_boot_security_init(), and s_tag.

◆ ra8_tz_secure_boot_sau_init()

ra8_err_t ra8_tz_secure_boot_sau_init ( void )
nodiscard

Programme the SAU regions documented in ra8_tz_sau_region_t.

Equivalent to the FSP R_BSP_SAUInit function: writes each region's RBAR / RLAR, then enables the SAU with ALLNS = 0. Does NOT touch CPSCU or jump to the NS image – callers issue ra8_tz_secure_boot_security_init and ra8_tz_secure_boot_jump_ns separately so unit tests can exercise each phase in isolation.

Returns
ra8_err_t Error code.
Return values
k_ra8_okSAU programmed and enabled.
k_ra8_err_not_supportedSAU_TYPE.SREGION < 5.
Precondition
Caller is in Secure state (the SAU window lives at 0xE000EDD0 which is secure-only).
This function has not been called previously in the current boot (SAU programming is one-shot per reset).
Postcondition
On success, SAU_CTRL.ENABLE = 1 and the five regions cover the canonical layout.
On failure, the SAU stays disabled (default-allow Secure).
Note
Not thread-safe; runs once at boot.
Since
0.1.0

Definition at line 441 of file ra8_tz_secure_boot.c.

References internal_dsb(), internal_isb(), internal_read32(), internal_sau_set_region(), internal_write32(), k_ra8_err_not_supported, k_ra8_ok, k_ra8_tz_part_code_nsc_base, k_ra8_tz_part_code_nsc_limit, k_ra8_tz_part_ns_mram_base, k_ra8_tz_part_ns_mram_limit, k_ra8_tz_part_ns_per_base, k_ra8_tz_part_ns_per_limit, k_ra8_tz_part_ns_sram_base, k_ra8_tz_part_ns_sram_limit, k_ra8_tz_part_sram_nsc_base, k_ra8_tz_part_sram_nsc_limit, k_ra8_tz_sau_ctrl_addr, k_ra8_tz_sau_ctrl_enable, k_ra8_tz_sau_region_code_nsc, k_ra8_tz_sau_region_count, k_ra8_tz_sau_region_ns_mram, k_ra8_tz_sau_region_ns_periph, k_ra8_tz_sau_region_ns_sram, k_ra8_tz_sau_region_sram_nsc, k_ra8_tz_sau_type_addr, k_ra8_tz_sau_type_mask, k_ra8_tz_secure_boot_step_sau_done, ra8_log_error, s_step, and s_tag.

Referenced by ra8_trustzone_init(), and ra8_tz_secure_boot_run().

◆ ra8_tz_secure_boot_security_init()

ra8_err_t ra8_tz_secure_boot_security_init ( uint32_t ipcsar_value,
uint32_t ipcpar_value )
nodiscard

Unlock PRCR_S.PRC4 and write IPCSAR + IPCPAR.

Writes ipcsar_value to CPSCU.IPCSAR and ipcpar_value to CPSCU.IPCPAR after first opening the PRCR_S.PRC4 write-protect gate (HUM Ch 13.2.1 "PRCR_S" – key 0xA510 at address 0x4001E3FA). The gate is closed again before the function returns.

For the cpu1_pingpong_ipc app the canonical value is ipcsar_value = 0x00050000, which sets SAIPCIR0 (IPC0 channel 0, CPU1 -> CPU0) and SAIPCIR2 (IPC1 channel 0, CPU0 -> CPU1) – the two channels CPU1 (always-NS) must reach. ipcpar_value stays at 0 so the channels remain Privileged-only.

Parameters
[in]ipcsar_valueValue to write into CPSCU.IPCSAR.
[in]ipcpar_valueValue to write into CPSCU.IPCPAR.
Returns
ra8_err_t Error code.
Return values
k_ra8_okAlways (the unlock + write + relock sequence cannot fail in software; bench bring-up may still observe read-back mismatches if the chip's CPSCU is in an unexpected state).
Precondition
Caller is in Secure state (CPSCU and PRCR_S are secure-only).
Caller has already programmed the SAU partition.
Postcondition
IPCSAR contains ipcsar_value (verifiable via JTAG read).
PRCR_S.PRC4 is cleared (write-protect restored).
Note
Not thread-safe; runs once at boot.
Since
0.1.0

Definition at line 481 of file ra8_tz_secure_boot.c.

References internal_dsb(), internal_write16(), internal_write32(), k_ra8_ok, k_ra8_tz_ipcpar_addr, k_ra8_tz_ipcsar_addr, k_ra8_tz_prcr_s_addr, k_ra8_tz_prcr_s_close, k_ra8_tz_prcr_s_open, k_ra8_tz_secure_boot_step_ipcsar_written, k_ra8_tz_secure_boot_step_prcr_relocked, k_ra8_tz_secure_boot_step_prcr_unlocked, and s_step.

Referenced by ra8_trustzone_init(), and ra8_tz_secure_boot_run().