cm4: add Raspberry Pi Compute Module 4 (BCM2711 Cortex-A72) target with authenticated boot

pull/863/head
David Garske 2026-07-10 17:30:27 -07:00 committed by Daniele Lacamera
parent 5c07c3797e
commit eb7ad83ca0
10 changed files with 751 additions and 1 deletions

View File

@ -377,6 +377,10 @@ ifeq ($(TARGET),tegra234)
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif
ifeq ($(TARGET),cm4)
MAIN_TARGET:=wolfboot.bin
endif
ifeq ($(TARGET),sim)
CFLAGS+=-fno-pie
LDFLAGS+=-no-pie

12
arch.mk
View File

@ -219,8 +219,18 @@ ifeq ($(ARCH),AARCH64)
# BOOT_EL1 itself is emitted by options.mk; nothing to add here.
endif
ifeq ($(TARGET),cm4)
# Raspberry Pi Compute Module 4 - Broadcom BCM2711, Cortex-A72
ARCH_FLAGS=-mcpu=cortex-a72+crypto -march=armv8-a+crypto -mtune=cortex-a72
# -mstrict-align: wolfBoot runs with the MMU off (simple startup), so data
# accesses are Device memory where unaligned access faults. Required for the
# FIPS in-core HMAC over the code region (NO_ARM_ASM drops the asm path that
# otherwise sets this).
CFLAGS+=$(ARCH_FLAGS) -DCORTEX_A72 -mstrict-align
endif
# Default ARM ASM setting for unrecognized AARCH64 targets
ifeq ($(filter zynq versal nxp_ls1028a,$(TARGET)),)
ifeq ($(filter zynq versal nxp_ls1028a cm4,$(TARGET)),)
NO_ARM_ASM?=1
endif

View File

@ -0,0 +1,18 @@
ARCH?=AARCH64
TARGET?=cm4
SIGN?=ECC384
HASH?=SHA384
DEBUG?=1
VTOR?=1
SPMATH?=1
IMAGE_HEADER_SIZE?=1024
PKA?=0
WOLFTPM?=0
DEBUG_UART?=0
NO_XIP?=1
NO_QNX?=1
WOLFBOOT_SECTOR_SIZE=0x400
WOLFBOOT_NO_PARTITIONS=1
WOLFBOOT_RAMBOOT_MAX_SIZE=0x20000000
WOLFBOOT_LOAD_ADDRESS?=0x3080000
WOLFBOOT_LOAD_DTS_ADDRESS?=0x400000

View File

@ -0,0 +1,39 @@
# Raspberry Pi CM4 (BCM2711) - eMMC/SD A/B disk boot (Phase 2)
#
# Boots from the BCM2711 EMMC2 controller (Arasan SDHCI @ 0xFE340000), which
# drives the onboard eMMC (eMMC variants) or the microSD (CM4 Lite). wolfBoot
# reads GPT A/B image partitions via update_disk.c and boots the highest valid
# version with rollback.
#
# NOTE: the EMMC2 platform glue in hal/cm4.c is bring-up scaffolding pending
# on-hardware validation (controller clock/pinmux state left by the GPU
# firmware, card-detect wiring on the carrier). Use SDHCI_FORCE_CARD_DETECT
# for the embedded eMMC path.
ARCH?=AARCH64
TARGET?=cm4
SIGN?=ECC384
HASH?=SHA384
IMAGE_HEADER_SIZE?=1024
DEBUG?=0
DEBUG_UART?=1
DISK_SDCARD?=1
DISK_EMMC?=0
CFLAGS_EXTRA+=-DSDHCI_FORCE_CARD_DETECT
EXT_FLASH?=0
NO_XIP=1
NO_QNX?=1
ELF?=1
VTOR?=1
SPMATH?=1
PKA?=0
WOLFTPM?=0
WOLFBOOT_NO_PARTITIONS=1
CFLAGS_EXTRA+=-DBOOT_PART_A=1
CFLAGS_EXTRA+=-DBOOT_PART_B=2
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000
WOLFBOOT_LOAD_ADDRESS?=0x10000000
WOLFBOOT_RAMBOOT_MAX_SIZE=0x2BC00000
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80200000
WOLFBOOT_PARTITION_SIZE=0x4000000
WOLFBOOT_SECTOR_SIZE=0x1000

View File

@ -7,6 +7,7 @@ This README describes configuration of supported targets.
* [Simulated](#simulated)
* [Analog Devices MAX32666](#analog-devices-max32666)
* [Cortex-A53 / Raspberry PI 3](#cortex-a53--raspberry-pi-3-experimental)
* [Cortex-A72 / Raspberry Pi Compute Module 4](#cortex-a72--raspberry-pi-compute-module-4-bcm2711)
* [Cypress PSoC-6](#cypress-psoc-6)
* [Infineon AURIX TC3xx](#infineon-aurix-tc3xx)
* [Intel x86-64 Intel FSP](#intel-x86_64-with-intel-fsp-support)
@ -3799,6 +3800,79 @@ qemu-system-aarch64 -M raspi3b -m 1024 -serial stdio -kernel wolfboot_linux_rasp
```
## Cortex-A72 / Raspberry Pi Compute Module 4 (BCM2711)
wolfBoot runs on the Raspberry Pi Compute Module 4 (CM4), a Broadcom BCM2711 with a quad-core Cortex-A72 (AArch64). wolfBoot takes the place of the second-stage OS loader: the BCM2711 boot ROM loads the VideoCore firmware, the firmware loads `kernel8.img` from the boot partition, and that `kernel8.img` is wolfBoot. wolfBoot then verifies the signed application image and boots it, extending the platform root of trust into the OS.
```
BCM2711 boot ROM -> SPI EEPROM bootloader -> VideoCore firmware (start4.elf)
-> kernel8.img (wolfBoot) -> verify (ECDSA/SHA) -> application
```
On CM4 modules with onboard eMMC the boot files live on the eMMC FAT boot partition; on CM4 Lite they live on a microSD. Either way the medium hangs off the BCM2711 EMMC2 controller (a standard SDHCI v3.0 Arasan block at `0xFE340000`).
### Building
```
cp config/examples/cm4.config .config
make CROSS_COMPILE=aarch64-none-elf-
```
The example uses `SIGN=ECC384 HASH=SHA384` (both FIPS-approved). wolfBoot is entered by the firmware at `0x80000` at EL2, matching `hal/cm4.ld`. The image is loaded from RAM: wolfBoot reads the signed application at `kernel_addr` (`0x140000`), verifies it, copies it to `WOLFBOOT_LOAD_ADDRESS`, and boots.
### Signing and assembling the boot image
Sign the application, then concatenate wolfBoot and the signed image so the signed image lands at `kernel_addr` (`0x140000` = `0x80000` load + `0xC0000`):
```
make keytools tools/bin-assemble/bin-assemble
IMAGE_HEADER_SIZE=1024 ./tools/keytools/sign --ecc384 --sha384 \
app.bin wolfboot_signing_private_key.der 1
tools/bin-assemble/bin-assemble kernel8.img \
0x0 wolfboot.bin \
0xC0000 app_v1_signed.bin
```
### config.txt
The CM4 UART on GPIO14/15 defaults to the mini-UART because the PL011 is used by Bluetooth. wolfBoot drives the PL011, so route it to the header and fix a known UART clock:
```
arm_64bit=1
kernel=kernel8.img
enable_uart=1
uart_2ndstage=1
dtoverlay=disable-bt
init_uart_clock=48000000
init_uart_baud=115200
```
### Flashing
- CM4 Lite: write `kernel8.img` + the RPi firmware (`start4.elf`, `fixup4.dat`) + `config.txt` to the microSD FAT boot partition.
- CM4 with eMMC: put the module in USB boot mode (nRPIBOOT), run `rpiboot` to expose the eMMC as USB mass storage, and write the same files to its FAT boot partition. See https://github.com/raspberrypi/usbboot .
### Boot output
With `DEBUG_UART=1`, a successful authenticated boot prints (115200 8N1):
```
wolfBoot CM4 (BCM2711 Cortex-A72) hal_init, EL2
Trying partition 0 at 0x140000
Checking integrity...done
Verifying signature...done
Firmware Valid
Booting at 0x3080000
```
### Optional: eMMC/SD A/B updates
`config/examples/cm4_sdcard.config` enables the disk updater (`DISK_SDCARD`/`DISK_EMMC`), driving the BCM2711 EMMC2 controller through the generic SDHCI driver (`src/sdhci.c`) for A/B image partitions on the boot medium. This path is provided as bring-up scaffolding and is not yet hardware-validated.
### FIPS 140-3
The CM4 target uses `SIGN=ECC384 HASH=SHA384` (FIPS-approved) and can perform its signature verification with the wolfCrypt FIPS 140-3 validated module. The on-target harness in `test-app/app_cm4.c` registers a FIPS callback and prints the runtime in-core integrity hash over the UART for the `verifyCore[]` bootstrap. See [FIPS.md](FIPS.md) for the full build and hash-sealing procedure.
## Xilinx Zynq UltraScale
AMD Zynq UltraScale+ MPSoC ZCU102 Evaluation Kit - Quad-core ARM Cortex-A53 (plus dual Cortex-R5).

314
hal/cm4.c 100644
View File

@ -0,0 +1,314 @@
/* cm4.c
*
* HAL for the Raspberry Pi Compute Module 4 (CM4): Broadcom BCM2711,
* quad-core Cortex-A72 (ARMv8-A).
*
* The VideoCore GPU firmware loads wolfBoot (as kernel8.img) to 0x80000 and
* releases the A72 cores; wolfBoot verifies the appended signed payload and
* boots it from RAM. hal_flash_* are no-ops (no in-place flash in this mode).
* Optional eMMC/SD A/B via the generic SDHCI driver is at the end of the file.
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <target.h>
#include "image.h"
#include "printf.h"
#include "hal/cm4.h"
#ifndef ARCH_AARCH64
# error "wolfBoot cm4 HAL: wrong architecture selected. Please compile with ARCH=AARCH64."
#endif
/* Hardware register map (bases, UART, EMMC2/SDHCI) is in hal/cm4.h */
/* Fixed addresses (provided by the linker script) */
extern void *kernel_addr, *update_addr, *dts_addr;
#if defined(DEBUG_UART)
static void uart_tx(char c)
{
while (*UART0_FR & 0x20) /* TXFF: wait while FIFO full */
;
*UART0_DR = c;
}
void uart_write(const char* buf, uint32_t sz)
{
while (sz-- > 0 && *buf)
uart_tx(*buf++);
}
void uart_init(void)
{
/* The VideoCore firmware has already routed the PL011 to GPIO14/15
* (dtoverlay=disable-bt) and set init_uart_clock=48MHz. Program the PL011
* for 115200 8N1 directly, without the VideoCore mailbox (a mailbox poll
* that never returns post-handoff would hang before any output).
* 48MHz UARTCLK: BAUDDIV = 48e6/(16*115200) = 26.04 -> IBRD 26, FBRD 3. */
*UART0_CR = 0;
*UART0_ICR = 0x7FF;
*UART0_IBRD = 26;
*UART0_FBRD = 3;
*UART0_LCRH = (1 << 4) | (1 << 5) | (1 << 6); /* FIFO, 8-bit */
*UART0_CR = (1 << 0) | (1 << 8) | (1 << 9); /* enable UART, TX, RX */
}
#endif /* DEBUG_UART */
void* hal_get_primary_address(void)
{
return (void*)&kernel_addr;
}
void* hal_get_update_address(void)
{
return (void*)&update_addr;
}
void* hal_get_dts_address(void)
{
return (void*)&dts_addr;
}
void* hal_get_dts_update_address(void)
{
return NULL; /* Not yet supported */
}
#ifdef EXT_FLASH
int ext_flash_read(unsigned long address, uint8_t *data, int len)
{
XMEMCPY(data, (void *)address, len);
return len;
}
int ext_flash_erase(unsigned long address, int len)
{
XMEMSET((void *)address, 0xFF, len);
return len;
}
int ext_flash_write(unsigned long address, const uint8_t *data, int len)
{
XMEMCPY((void *)address, data, len);
return len;
}
void ext_flash_lock(void)
{
}
void ext_flash_unlock(void)
{
}
#endif /* EXT_FLASH */
void hal_init(void)
{
#if defined(DEBUG_UART)
unsigned long el;
uart_init();
__asm__ volatile("mrs %0, CurrentEL" : "=r"(el));
wolfBoot_printf("wolfBoot CM4 (BCM2711 Cortex-A72) hal_init, EL%d\n",
(int)((el >> 2) & 0x3));
#endif
}
void hal_prepare_boot(void)
{
}
#if defined(HAVE_FIPS)
/* FIPS DRBG entropy seed from the BCM2711 RNG200 hardware TRNG. Registered via
* CUSTOM_RAND_GENERATE_SEED in include/user_settings.h. The RNG200 has NIST
* SP800-90B startup/continuous health tests in hardware. */
int wolfBoot_fips_seed(unsigned char* output, unsigned int sz)
{
static int rng_inited = 0;
unsigned int pos = 0;
unsigned int guard;
if (!rng_inited) {
/* iproc-rng200 bring-up: disable RBG, soft-reset the RBG then RNG
* cores, clear pending interrupt status, then re-enable the RBG. */
*RNG_CTRL = 0;
*RNG_RBG_SOFT_RESET = 1;
*RNG_RBG_SOFT_RESET = 0;
*RNG_SOFT_RESET = 1;
*RNG_SOFT_RESET = 0;
*RNG_INT_STATUS = 0xFFFFFFFF; /* write-1-to-clear all pending status */
*RNG_CTRL = RNG200_CTRL_RBGEN;
rng_inited = 1;
}
while (pos < sz) {
unsigned int word, n, i;
/* wait for at least one 32-bit word in the FIFO (bounded) */
guard = 0;
while ((*RNG_FIFO_COUNT & 0xFF) == 0) {
if (++guard > 200000000U) {
#if defined(DEBUG_UART)
wolfBoot_printf("RNG200 FIFO timeout int=0x%08x ctrl=0x%08x\n",
(unsigned)*RNG_INT_STATUS, (unsigned)*RNG_CTRL);
#endif
return -1;
}
}
word = *RNG_FIFO_DATA;
n = (sz - pos) < 4 ? (sz - pos) : 4;
for (i = 0; i < n; i++)
output[pos++] = (unsigned char)(word >> (i * 8));
}
#if defined(DEBUG_UART)
{
static int traced = 0;
if (!traced) {
wolfBoot_printf("RNG200 seed %02x %02x %02x %02x %02x %02x %02x %02x\n",
output[0], output[1], output[2], output[3],
output[4], output[5], output[6], output[7]);
traced = 1;
}
}
#endif
return 0;
}
#endif /* HAVE_FIPS */
int RAMFUNCTION hal_flash_write(uintptr_t address, const uint8_t *data, int len)
{
(void)address; (void)data; (void)len;
return 0;
}
void RAMFUNCTION hal_flash_unlock(void)
{
}
void RAMFUNCTION hal_flash_lock(void)
{
}
int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
{
(void)address; (void)len;
return 0;
}
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
/* BCM2711 EMMC2 platform glue for the generic SDHCI driver (src/sdhci.c).
* EMMC2 is a standard SDHCI v3.0 Arasan block at 0xFE340000. The driver uses
* Cadence-style SRS offsets (0x200 + std); translate them to the standard
* Arasan layout, mirroring the ZynqMP path in hal/zynq.c. NOTE: not yet
* hardware-validated; clock/caps/card-detect quirks may be required once
* validated on hardware. */
#include "sdhci.h"
uint32_t sdhci_reg_read(uint32_t offset)
{
volatile uint8_t *base = (volatile uint8_t *)BCM2711_EMMC2_BASE;
if (offset >= CADENCE_SRS_OFFSET) {
uint32_t std_off = offset - CADENCE_SRS_OFFSET;
uint32_t val;
if (std_off == 0x58) /* SRS22 -> legacy SDMA address (SRS00) */
return *((volatile uint32_t *)(base + STD_SDHCI_SDMA_ADDR));
if (std_off == 0x5C) /* SRS23: no 64-bit addressing on v3.0 */
return 0;
val = *((volatile uint32_t *)(base + std_off));
if (std_off == 0x40) /* SRS16 Capabilities: mask A64S (no HV4E) */
val &= ~SDHCI_SRS16_A64S;
return val;
}
return 0; /* HRS region not present on this Arasan block */
}
void sdhci_reg_write(uint32_t offset, uint32_t val)
{
volatile uint8_t *base = (volatile uint8_t *)BCM2711_EMMC2_BASE;
uint32_t std_off;
if (offset < CADENCE_SRS_OFFSET)
return; /* HRS region not present */
std_off = offset - CADENCE_SRS_OFFSET;
/* SRS10 (0x28): Host Control 1 / Power / Block Gap / Wakeup (8-bit each) */
if (std_off == 0x28) {
*((volatile uint8_t *)(base + STD_SDHCI_HOST_CTRL1)) = (uint8_t)val;
*((volatile uint8_t *)(base + STD_SDHCI_POWER_CTRL)) = (uint8_t)(val >> 8);
*((volatile uint8_t *)(base + STD_SDHCI_BLKGAP_CTRL)) = (uint8_t)(val >> 16);
*((volatile uint8_t *)(base + STD_SDHCI_WAKEUP_CTRL)) = (uint8_t)(val >> 24);
return;
}
/* SRS11 (0x2C): Clock Control (16-bit) / Timeout / Software Reset */
if (std_off == 0x2C) {
*((volatile uint16_t *)(base + STD_SDHCI_CLK_CTRL)) = (uint16_t)val;
*((volatile uint8_t *)(base + STD_SDHCI_TIMEOUT_CTRL)) = (uint8_t)(val >> 16);
*((volatile uint8_t *)(base + STD_SDHCI_SW_RESET)) = (uint8_t)(val >> 24);
return;
}
if (std_off == 0x58) { /* SRS22 -> legacy SDMA address; write restarts DMA */
*((volatile uint32_t *)(base + STD_SDHCI_SDMA_ADDR)) = val;
return;
}
if (std_off == 0x5C) /* SRS23: no 64-bit addressing on v3.0 */
return;
if (std_off == STD_SDHCI_HOST_CTRL2) /* SRS15: mask unsupported HV4E/A64 */
val &= ~(SDHCI_SRS15_HV4E | SDHCI_SRS15_A64);
*((volatile uint32_t *)(base + std_off)) = val;
}
void sdhci_platform_init(void)
{
/* The GPU firmware already brought up the EMMC2 clock/pinmux; just issue a
* controller soft reset and wait for it to clear. */
volatile uint8_t *base = (volatile uint8_t *)BCM2711_EMMC2_BASE;
volatile int i;
*((volatile uint8_t *)(base + STD_SDHCI_SW_RESET)) = STD_SDHCI_SRA;
for (i = 0; i < 100000; i++) {
if ((*((volatile uint8_t *)(base + STD_SDHCI_SW_RESET)) & STD_SDHCI_SRA) == 0)
break;
}
}
void sdhci_platform_irq_init(void)
{
/* Polled mode; no IRQ wiring needed for the boot path. */
}
void sdhci_platform_set_bus_mode(int is_emmc)
{
(void)is_emmc; /* handled by the generic driver via Host Control 1 */
}
/* Microseconds from the ARMv8 generic timer (SDHCI udelay + disk updater).
* Falls back to the BCM2711 system counter frequency if CNTFRQ_EL0 is 0. */
uint64_t hal_get_timer_us(void)
{
uint64_t count, freq;
__asm__ volatile("mrs %0, CNTPCT_EL0" : "=r"(count));
__asm__ volatile("mrs %0, CNTFRQ_EL0" : "=r"(freq));
if (freq == 0)
freq = BCM2711_TIMER_CLK_FREQ;
return (uint64_t)(((__uint128_t)count * 1000000ULL) / freq);
}
#endif /* DISK_SDCARD || DISK_EMMC */

88
hal/cm4.h 100644
View File

@ -0,0 +1,88 @@
/* cm4.h
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Raspberry Pi Compute Module 4 (CM4) - Broadcom BCM2711, Cortex-A72.
* Hardware register map (bases, UART, EMMC2/SDHCI) shared by the HAL and the
* test application. This header is also included from boot_aarch64_start.S, so
* pointer-cast accessors are guarded with __ASSEMBLER__. */
#ifndef _CM4_H_
#define _CM4_H_
/* Select the compact AArch64 startup path in boot_aarch64_start.S */
#define USE_BUILTIN_STARTUP
#define USE_SIMPLE_STARTUP
/* BCM2711 peripheral base addresses (low-peripheral 0xFE000000 view).
* Integer literals - safe to include from assembly. */
#define BCM2711_MMIO_BASE 0xFE000000
#define BCM2711_GPIO_BASE (BCM2711_MMIO_BASE + 0x200000)
#define BCM2711_UART0_BASE (BCM2711_GPIO_BASE + 0x1000) /* PL011 */
#define BCM2711_EMMC2_BASE (BCM2711_MMIO_BASE + 0x340000) /* Arasan SDHCI */
#define BCM2711_RNG_BASE (BCM2711_MMIO_BASE + 0x104000) /* RNG200 TRNG */
/* RNG200 (iproc-rng200) register offsets */
#define RNG200_CTRL 0x00 /* bit0 RBGEN = enable */
#define RNG200_SOFT_RESET 0x04
#define RNG200_RBG_SOFT_RESET 0x08
#define RNG200_INT_STATUS 0x18
#define RNG200_FIFO_DATA 0x20
#define RNG200_FIFO_COUNT 0x24 /* [7:0] = words available */
#define RNG200_CTRL_RBGEN 0x00000001
/* BCM2711 system counter frequency (fallback if CNTFRQ_EL0 reads 0) */
#define BCM2711_TIMER_CLK_FREQ 54000000ULL
/* SDHCI register offsets used by the EMMC2 glue. The generic SDHCI driver
* addresses registers with Cadence-style SRS offsets (0x200 + std); the glue
* translates to the standard Arasan layout below. Integers, assembler-safe. */
#define CADENCE_SRS_OFFSET 0x200
#define STD_SDHCI_SDMA_ADDR 0x00 /* SDMA System Address (32-bit) */
#define STD_SDHCI_HOST_CTRL1 0x28
#define STD_SDHCI_POWER_CTRL 0x29
#define STD_SDHCI_BLKGAP_CTRL 0x2A
#define STD_SDHCI_WAKEUP_CTRL 0x2B
#define STD_SDHCI_CLK_CTRL 0x2C
#define STD_SDHCI_TIMEOUT_CTRL 0x2E
#define STD_SDHCI_SW_RESET 0x2F
#define STD_SDHCI_HOST_CTRL2 0x3C
#define STD_SDHCI_SRA 0x01 /* Software Reset for All */
#ifndef __ASSEMBLER__
/* PL011 UART0 register accessors */
#define UART0_DR ((volatile unsigned int*)(BCM2711_UART0_BASE+0x00))
#define UART0_FR ((volatile unsigned int*)(BCM2711_UART0_BASE+0x18))
#define UART0_IBRD ((volatile unsigned int*)(BCM2711_UART0_BASE+0x24))
#define UART0_FBRD ((volatile unsigned int*)(BCM2711_UART0_BASE+0x28))
#define UART0_LCRH ((volatile unsigned int*)(BCM2711_UART0_BASE+0x2C))
#define UART0_CR ((volatile unsigned int*)(BCM2711_UART0_BASE+0x30))
#define UART0_ICR ((volatile unsigned int*)(BCM2711_UART0_BASE+0x44))
/* RNG200 hardware TRNG register accessors */
#define RNG_CTRL ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_CTRL))
#define RNG_SOFT_RESET ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_SOFT_RESET))
#define RNG_RBG_SOFT_RESET ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_RBG_SOFT_RESET))
#define RNG_INT_STATUS ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_INT_STATUS))
#define RNG_FIFO_DATA ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_FIFO_DATA))
#define RNG_FIFO_COUNT ((volatile unsigned int*)(BCM2711_RNG_BASE+RNG200_FIFO_COUNT))
#endif /* !__ASSEMBLER__ */
#endif /* _CM4_H_ */

71
hal/cm4.ld 100644
View File

@ -0,0 +1,71 @@
/* cm4.ld
*
* Linker script for the Raspberry Pi Compute Module 4 (BCM2711, Cortex-A72).
*
* The VideoCore GPU firmware loads kernel8.img to 0x80000 and releases the
* A72 cores there. DDR_MEM length covers the 1GB low-peripheral view common
* to all CM4 RAM variants (1/2/4/8 GB); the peripheral block sits above at
* 0xFE000000 and is not mapped here.
*/
MEMORY
{
DDR_MEM(rwx): ORIGIN = 0x00080000, LENGTH = 0x3c000000
}
ENTRY(_vector_table);
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.boot*))
*(.text*)
*(.rodata*)
*(.note.*)
. = ALIGN(4);
_end_text = .;
} > DDR_MEM
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > DDR_MEM
.data :
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > DDR_MEM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > DDR_MEM
. = ALIGN(8);
/* Heap start for the newlib sbrk (FIPS module malloc). Grows up into free
* DDR; the stack lives at END_STACK (0x80000) and grows down. */
PROVIDE(end = .);
PROVIDE(__end__ = .);
PROVIDE(_heap_start = .);
}
END_STACK = _start_text;
kernel_addr = 0x0140000;
update_addr = 0x1140000;
dts_addr = 0x00a0000;
kernel_load_addr = 0x20000000;
dts_load_addr = 0x21000000;
_wolfboot_partition_boot_address = kernel_addr;
_wolfboot_partition_update_address = update_addr;

View File

@ -42,6 +42,10 @@
#include "hal/tegra234.h"
#endif
#ifdef TARGET_cm4
#include "hal/cm4.h"
#endif
/* GICv2 Register Offsets */
#ifndef GICD_BASE
#define GICD_BASE 0xF9010000

128
test-app/app_cm4.c 100644
View File

@ -0,0 +1,128 @@
/* app_cm4.c
*
* Test application for Raspberry Pi CM4 (BCM2711). Prints a banner over the
* PL011 UART and, when built against wolfCrypt FIPS (HAVE_FIPS), runs the
* power-on self-tests and registers a FIPS callback that reports the runtime
* in-core integrity hash for the verifyCore[] bootstrap.
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
#include "wolfboot/wolfboot.h"
#include "hal/cm4.h" /* BCM2711 UART register map */
#ifdef HAVE_FIPS
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/fips_test.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#endif
#ifdef TARGET_cm4
static void uart_init(void)
{
/* PL011 for 115200 8N1 from the 48 MHz UART clock (see hal/cm4.c) */
*UART0_CR = 0;
*UART0_ICR = 0x7FF;
*UART0_IBRD = 26;
*UART0_FBRD = 3;
*UART0_LCRH = (1 << 4) | (1 << 5) | (1 << 6);
*UART0_CR = (1 << 0) | (1 << 8) | (1 << 9);
}
static void uart_putc(char c)
{
while (*UART0_FR & 0x20) /* wait while TX FIFO full */
;
*UART0_DR = (unsigned int)c;
}
static void uart_puts(const char* s)
{
while (*s) {
if (*s == '\n')
uart_putc('\r');
uart_putc(*s++);
}
}
static void uart_putdec(int v)
{
char buf[12];
unsigned int u;
int i = 0;
if (v < 0) {
uart_putc('-');
u = (unsigned int)(-v);
}
else {
u = (unsigned int)v;
}
do {
buf[i++] = (char)('0' + (u % 10));
u /= 10;
} while (u != 0);
while (i > 0)
uart_putc(buf[--i]);
}
#ifdef HAVE_FIPS
/* wolfCrypt FIPS callback. On an in-core integrity mismatch (IN_CORE_FIPS_E)
* the module reports the runtime hash here; copy it into verifyCore[] in
* wolfcrypt/src/fips_test.c and rebuild to seal the module boundary. */
static void cm4_fipsCb(int ok, int err, const char* hash)
{
uart_puts("FIPS callback: ok=");
uart_putdec(ok);
uart_puts(" err=");
uart_putdec(err);
uart_puts("\nhash = ");
uart_puts(hash != NULL ? hash : "(null)");
uart_puts("\n");
if (err == IN_CORE_FIPS_E) {
uart_puts("In-core integrity mismatch: copy the hash above into\n");
uart_puts("verifyCore[] in wolfcrypt/src/fips_test.c and rebuild.\n");
}
}
#endif /* HAVE_FIPS */
void main(void)
{
uart_init();
uart_puts("\n=== wolfBoot CM4 test-app ===\n");
#ifdef HAVE_FIPS
uart_puts("wolfCrypt FIPS 140-3 power-on self-test\n");
wolfCrypt_SetCb_fips(cm4_fipsCb);
if (wc_RunAllCast_fips() == 0)
uart_puts("FIPS CASTs: PASS\n");
else
uart_puts("FIPS CASTs: FAIL (see callback output above)\n");
uart_puts("FIPS status: ");
uart_putdec(wolfCrypt_GetStatus_fips());
uart_puts("\n");
#endif /* HAVE_FIPS */
uart_puts("test-app done; halting.\n");
while (1)
;
}
#endif /* TARGET_cm4 */