diff --git a/Makefile b/Makefile index 249ecb97..4489c08f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/arch.mk b/arch.mk index 867e9ac4..1bbaf6fb 100644 --- a/arch.mk +++ b/arch.mk @@ -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 diff --git a/config/examples/cm4.config b/config/examples/cm4.config new file mode 100644 index 00000000..ea9ff84e --- /dev/null +++ b/config/examples/cm4.config @@ -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 diff --git a/config/examples/cm4_sdcard.config b/config/examples/cm4_sdcard.config new file mode 100644 index 00000000..3f230c30 --- /dev/null +++ b/config/examples/cm4_sdcard.config @@ -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 diff --git a/docs/Targets.md b/docs/Targets.md index dcc58928..dd3684fc 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -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). diff --git a/hal/cm4.c b/hal/cm4.c new file mode 100644 index 00000000..2a041ff0 --- /dev/null +++ b/hal/cm4.c @@ -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 +#include +#include +#include +#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 */ diff --git a/hal/cm4.h b/hal/cm4.h new file mode 100644 index 00000000..7b5d6503 --- /dev/null +++ b/hal/cm4.h @@ -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_ */ diff --git a/hal/cm4.ld b/hal/cm4.ld new file mode 100644 index 00000000..aff7e3dc --- /dev/null +++ b/hal/cm4.ld @@ -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; diff --git a/src/boot_aarch64_start.S b/src/boot_aarch64_start.S index 9fbff4c4..21bf55b0 100644 --- a/src/boot_aarch64_start.S +++ b/src/boot_aarch64_start.S @@ -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 diff --git a/test-app/app_cm4.c b/test-app/app_cm4.c new file mode 100644 index 00000000..865479cd --- /dev/null +++ b/test-app/app_cm4.c @@ -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 +#include "wolfboot/wolfboot.h" +#include "hal/cm4.h" /* BCM2711 UART register map */ + +#ifdef HAVE_FIPS +#include +#include +#include +#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 */