diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index ac71b05af0..53350f2f20 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -8,6 +8,12 @@ on: jobs: + agilex5_test: + uses: ./.github/workflows/test-build-aarch64.yml + with: + arch: aarch64 + config-file: ./config/examples/agilex5_013b_sdcard.config + cypsoc6_test: uses: ./.github/workflows/test-build-psoc6.yml with: diff --git a/arch.mk b/arch.mk index c291fde827..1f63ef1067 100644 --- a/arch.mk +++ b/arch.mk @@ -167,6 +167,22 @@ ifeq ($(ARCH),AARCH64) CFLAGS_EXTRA+=-DSDHCI_SDMA_DISABLED endif + ifeq ($(TARGET),agilex5) + # Altera Agilex 5: Cortex-A55, entered as non-secure BL33 at EL2 by + # the GSRD TF-A BL31 image. SPL owns DDR, clocks, pinmux and resets. + ARCH_FLAGS=-mcpu=cortex-a55+crypto -march=armv8.2-a+crypto \ + -mtune=cortex-a55 + CFLAGS+=$(ARCH_FLAGS) -DCORTEX_A55 + CFLAGS+=-DWOLFBOOT_DUALBOOT -DWOLFBOOT_UBOOT_LEGACY + # BL31 owns GICv3 and secondary-core/PSCI setup. Do not touch RVBAR. + CFLAGS+=-DSKIP_RVBAR=1 -DSKIP_GIC_INIT=1 + ifeq ($(DEBUG_UART),1) + OBJS+=hal/uart/ns16550.o + endif + # Full SDHCI host reset: SPL leaves the Cadence host in UHS-I 1.8V/SDR104, + # which mutes CMD8 against the 3.3V re-powered card (hardware-verified). + endif + ifeq ($(TARGET),nxp_ls1028a) # DUART console on the generic driver (plain MMIO on AArch64). ifeq ($(DEBUG_UART),1) @@ -335,7 +351,7 @@ ifeq ($(ARCH),AARCH64) # asm (sp_arm64.c / WOLFSSL_SP_ARM64_ASM); that is enabled independently on # __aarch64__ in include/user_settings.h, so a non-FIPS cm4 build still links # sp_arm64 asm. - ifeq ($(filter zynq versal nxp_ls1028a,$(TARGET)),) + ifeq ($(filter zynq versal agilex5 nxp_ls1028a,$(TARGET)),) NO_ARM_ASM?=1 endif diff --git a/config/examples/agilex5_013b_sdcard.config b/config/examples/agilex5_013b_sdcard.config new file mode 100644 index 0000000000..0e7bf4b627 --- /dev/null +++ b/config/examples/agilex5_013b_sdcard.config @@ -0,0 +1,66 @@ +# wolfBoot configuration for Altera Agilex 5 DK-A5E013BM16AEA (013B) +# +# SDM -> U-Boot SPL -> TF-A BL31 -> wolfBoot BL33/EL2 -> signed Linux FIT + +ARCH?=AARCH64 +TARGET?=agilex5 +WOLFBOOT_VERSION?=1 + +SIGN?=RSA4096 +HASH?=SHA3 +IMAGE_HEADER_SIZE?=1024 + +DEBUG?=1 +DEBUG_SYMBOLS=1 +DEBUG_UART=1 + +DISK_SDCARD?=1 +DISK_EMMC?=0 +EXT_FLASH?=0 +NO_XIP=1 +ELF?=1 +GZIP?=1 + +VTOR?=1 +NO_ASM?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +SPMATH?=1 +RAM_CODE?=0 +DUALBANK_SWAP?=0 +PKA?=0 +WOLFTPM?=0 + +USE_GCC=1 +CROSS_COMPILE?=aarch64-none-elf- + +# SPL/BL31 load this binary in place of U-Boot proper. These values must match +# the u-boot.itb BL33 load and entry properties. +WOLFBOOT_ORIGIN=0x80200000 + +# Signed payload staging, extracted Linux Image, and DTB do not overlap. +WOLFBOOT_LOAD_ADDRESS?=0x90000000 +WOLFBOOT_LOAD_DTS_ADDRESS?=0x8f000000 +WOLFBOOT_RAMBOOT_MAX_SIZE?=0x10000000 + +# MBR: p1 FAT boot, p2 signed A, p3 signed B, p4 Linux rootfs. +WOLFBOOT_NO_PARTITIONS=1 +CFLAGS_EXTRA+=-DBOOT_PART_A=1 -DBOOT_PART_B=2 +CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 +CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\" + +# Board settings, see docs/Agilex5.md. Defaults match the 013B dev kit. +#CFLAGS_EXTRA+=-DDEBUG_UART_NUM=1 +#CFLAGS_EXTRA+=-DAGILEX5_DDR_SIZE=0x70000000 +CFLAGS_EXTRA+=-DWOLFBOOT_FDT_DISABLE_PATHS=\"/leds\" + +# SD6HC uses SDMA for cache-coherent multi-block transfers. +CFLAGS_EXTRA+=-DSDHCI_FORCE_CARD_DETECT + +# Required by generic config checks even though disk partitions select A/B. +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x90000000 +WOLFBOOT_PARTITION_SIZE=0x0c800000 +WOLFBOOT_SECTOR_SIZE=0x1000 + +# Uncomment during storage bring-up. +#CFLAGS_EXTRA+=-DDEBUG_SDHCI -DDEBUG_DISK diff --git a/docs/Agilex5.md b/docs/Agilex5.md new file mode 100644 index 0000000000..d882422dcb --- /dev/null +++ b/docs/Agilex5.md @@ -0,0 +1,116 @@ +# Altera Agilex 5 + +The Agilex 5 port follows wolfBoot's established Cortex-A TF-A model. It does +not replace the platform first stage or TF-A: + +```text +SDM -> U-Boot SPL -> TF-A BL31/EL3 -> wolfBoot BL33/EL2 -> signed Linux FIT +``` + +SPL remains responsible for DDR, clocks, resets, pinmux, the SDHCI PHY and +loading the signed U-Boot FIT. BL31 remains responsible for EL3, GICv3, PSCI, +secondary CPUs and security-controller setup. The U-Boot-proper payload in the +FIT is replaced with `wolfboot.bin` at load/entry address `0x80200000`. + +## Build + +Use an isolated wolfBoot checkout with initialized wolfSSL submodules: + +```sh +cp config/examples/agilex5_013b_sdcard.config .config +make clean +make -j"$(nproc)" +``` + +The hosted build is covered by the Agilex 5 job in the `Test Example Configs` +GitHub Actions workflow. It installs the same freestanding AArch64 toolchain +used by the other Cortex-A ports and builds this configuration on every pull +request; hardware is required only for the SD-card and Linux handoff steps +below. + +The default RSA-4096/SHA3-384 key is for development only. Production builds +must supply controlled signing keys and keep them separate from both the SDM +owner key and the SPL FIT-signing key. + +Create the Linux FIT from the GSRD `Image` and the exact board DTB, then sign +that FIT with wolfBoot's normal image-signing flow. Initialize both raw A/B +partitions with a valid signed image for the first boot. + +### Board settings + +The example config targets the DK-A5E013BM16AEA. Other boards set these in +their config: + +| Setting | Default | Purpose | +|---|---|---| +| `CFLAGS_EXTRA+=-DDEBUG_UART_NUM=<0\|1>` | UART0 (`0x10C02000`) | select UART0 or UART1 (`0x10C02100`) for wolfBoot and Linux `earlycon` | +| `CFLAGS_EXTRA+=-DDEBUG_UART_BASE=` | unset | any other console UART; write the address without a `UL` suffix | +| `CFLAGS_EXTRA+=-DAGILEX5_DDR_SIZE=` | `0x70000000` (1792 MiB) | DDR size written to the DTB memory node | +| `CFLAGS_EXTRA+=-DWOLFBOOT_FDT_DISABLE_PATHS=\"/leds\"` | unset | comma-separated DTB node paths to mark `disabled`, for hardware behind the unprogrammed FPGA | + +wolfBoot does not read the DDR size from the DTB, because without U-Boot the +memory node holds whatever the DTB author wrote rather than the fitted size. + +The checked-in `hal/agilex5.its` is a minimal FIT example. A GSRD production +image must use the GSRD-generated kernel load address and board DTB, then be +signed with the deployment key. Do not place private keys in the source tree +or the WIC. + +## GSRD integration + +Keep the GSRD `u-boot.itb` structure and signature intact: + +- retain the existing BL31 firmware node; +- replace only the U-Boot-proper/BL33 data with `wolfboot.bin`; +- retain load and entry `0x80200000`; +- rebuild and sign the ITB using the same GSRD/SPL trust configuration. + +Use a four-partition WIC: + +1. 128 MiB FAT boot partition containing the signed boot artifacts; +2. 200 MiB raw wolfBoot A partition; +3. 200 MiB raw wolfBoot B partition; +4. ext4 Linux root filesystem. + +The wolfSSL FCS packages belong in partition 4. libfcs is Linux userspace +software and is deliberately not linked into bare-metal wolfBoot. + +## Test order + +1. Cross-build the existing ZynqMP and Versal SD-card configurations to catch + shared AArch64 regressions, then build Agilex 5. +2. First boot a minimal signed payload that prints its exception level and + proves the generic timer and PSCI reset path. +3. Exercise SD reads in PIO mode, then valid, corrupt-signature, truncated and + oversized images. +4. Boot Linux and require the DTB in x0, x1-x3 zero, four online CPUs, working + PSCI reboot, and rootfs `/dev/mmcblk0p4`. +5. Build the complete WIC, inspect all four partitions, flash the whole card, + compare the complete image span, cold boot, and test A-to-B fallback by + corrupting only a disposable copy of A. Both slots must hold the same + version: with `ALLOW_DOWNGRADE=0` wolfBoot refuses to fall back to a lower + version (`Rollback to lower version not allowed`) and panics instead. +6. From the booted WIC run `/usr/bin/wolfcrypttest`. Require exit zero and + `ALTERA-FCS test passed!` to prove the Linux image still provides hardware + FCS offload. + +For the reproducible meta-wolfSSL customer flow, use +`recipes-wolfssl/wolfboot/agilex5/README.md` in the meta-wolfSSL checkout. It +covers the Kas fragment, the four-partition WIC inspection, whole-device flash +and byte comparison, virtual SDM owner-key reprovisioning after power loss, +and the target-side loader/FCS checks. wolfBoot itself does not link libfcs; +the FCS test belongs to the Linux image in WIC partition 4. + +Do not patch TF-A or take over GICv3, SMMU/firewall, mailbox, or PSCI ownership +without a reproduced hardware failure showing the stock GSRD contract is +insufficient. + +## CI and hardware boundary + +CI proves that the Agilex 5 configuration, linker script, startup assembly, +SDHCI changes, and test application remain buildable without Agilex hardware. +It cannot prove SPL DDR training, TF-A handoff, SDM services, signed FIT +verification, or FCS offload. Those require the GSRD image and the +DK-A5E013BM16AEA. Record the WIC hash, complete-card comparison, UART boot +log, target `uname`, loader output, and `wolfcrypttest` exit status and FCS +marker for a hardware release sign-off. diff --git a/docs/Targets.md b/docs/Targets.md index dd70453cfc..ad6a35f859 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -4,6 +4,7 @@ This README describes configuration of supported targets. ## Supported Targets +* [Altera Agilex 5](#altera-agilex-5-013b) * [Simulated](#simulated) * [Analog Devices MAX32666](#analog-devices-max32666) * [Cortex-A53 / Raspberry PI 3](#cortex-a53--raspberry-pi-3-experimental) @@ -10750,3 +10751,23 @@ The C28x boot ROM selects its boot source before any application code runs, so t | `DEBUG` | Enables verbose boot progress and a JTAG-readable survive-log mirror (`g_log`) for bring-up. | A/B update / rollback is a follow-on: the partitions are declared, but the update path (flash erase/write, swap, trailer) is not yet wide-byte-hardened. + +## Altera Agilex 5 (013B) + +The Agilex 5 DK-A5E013BM16AEA port runs as the signed BL33 payload in the +GSRD handoff: SDM -> U-Boot SPL -> TF-A BL31 -> wolfBoot -> Linux FIT. SPL +owns DDR and controller initialization; TF-A owns EL3, GICv3 and PSCI. The +port keeps those responsibilities unchanged and loads signed A/B Linux FITs +from raw MBR partitions on the SD card. + +Build the hosted configuration with: + +```sh +cp config/examples/agilex5_013b_sdcard.config .config +make clean +make -j"$(nproc)" +``` + +See [docs/Agilex5.md](Agilex5.md) for the GSRD FIT/WIC layout, DT handoff, +hardware test order, and the boundary between the bare-metal bootloader and +the Linux libfcs/wolfSSL integration. diff --git a/hal/agilex5.c b/hal/agilex5.c new file mode 100644 index 0000000000..17d3a3ba88 --- /dev/null +++ b/hal/agilex5.c @@ -0,0 +1,319 @@ +/* agilex5.c + * + * 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 + */ + +#ifdef TARGET_agilex5 + +#include + +#include "fdt.h" +#include "hal.h" +#include "hal/agilex5.h" +#include "aarch64_arch.h" +#include "image.h" +#include "printf.h" + +#ifndef ARCH_AARCH64 +#error "wolfBoot Agilex 5 HAL requires ARCH=AARCH64" +#endif + +#ifndef LINUX_BOOTARGS +#ifndef LINUX_BOOTARGS_ROOT +#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4" +#endif +#define AGILEX5_STR2(x) #x +#define AGILEX5_STR(x) AGILEX5_STR2(x) +#define LINUX_BOOTARGS \ + "earlycon=uart8250,mmio32," AGILEX5_STR(DEBUG_UART_BASE) \ + " console=ttyS0,115200 root=" LINUX_BOOTARGS_ROOT " rootwait" +#endif + +#ifdef DEBUG_UART +#include "ns16550.h" + +static struct ns16550_dev uart_console; + +void uart_init(void) +{ + uart_console.base = (uintptr_t)DEBUG_UART_BASE; + uart_console.reg_shift = 2; + uart_console.reg_off = 0; + uart_console.clk_hz = 0; + uart_console.io_width = 4; + uart_console.crlf = 1; + /* Keep the baud, clock and pinmux settings inherited from SPL. */ +} + +void uart_write(const char *buf, uint32_t len) +{ + (void)ns16550_write(&uart_console, buf, len); +} +#else +#define uart_init() do {} while (0) +#endif + +uint64_t hal_get_timer_us(void) +{ + uint64_t frequency = timer_get_freq(); + uint64_t count = timer_get_count(); + uint64_t whole; + uint64_t rem; + + /* Split the conversion so it stays in 64-bit math: a __uint128_t divide + * pulls in libgcc's __udivti3, which the bare-metal link has no runtime + * for. The whole and remainder parts are exact and cannot overflow. */ + whole = count / frequency; + rem = count % frequency; + return whole * 1000000ULL + (rem * 1000000ULL) / frequency; +} + +void hal_init(void) +{ + uart_init(); +#if defined(DEBUG_UART) && defined(__WOLFBOOT) + wolfBoot_printf("\nwolfBoot Secure Boot - Altera Agilex 5\n"); + wolfBoot_printf("TF-A BL33 entry, current EL: %d\n", current_el()); +#endif +} + +void hal_prepare_boot(void) +{ + __asm__ volatile("dsb sy" : : : "memory"); + __asm__ volatile("ic iallu" : : : "memory"); + __asm__ volatile("dsb sy" : : : "memory"); + __asm__ volatile("isb" : : : "memory"); +} + +#ifdef MMU +void *hal_get_dts_address(void) +{ + return (void *)WOLFBOOT_LOAD_DTS_ADDRESS; +} + +void *hal_get_dts_update_address(void) +{ +#ifdef WOLFBOOT_DTS_UPDATE_ADDRESS + return (void *)WOLFBOOT_DTS_UPDATE_ADDRESS; +#else + return NULL; +#endif +} + +#ifdef __WOLFBOOT +#ifdef WOLFBOOT_FDT_DISABLE_PATHS +static const char* const fdt_disable_paths[] = { WOLFBOOT_FDT_DISABLE_PATHS }; +#endif + +int hal_dts_fixup(void *dts_addr, uint32_t capacity) +{ + fdt_ctx ctx; + int off; + int ret; +#ifdef WOLFBOOT_FDT_DISABLE_PATHS + unsigned int i; +#endif + + /* Bare-metal and RTOS payloads carry no DTB. */ + if (dts_addr == NULL) + return 0; + + /* Validate the blob against the window it actually occupies. */ + ret = fdt_open(&ctx, dts_addr, capacity); + if (ret != 0) { + wolfBoot_printf("FDT: invalid header (%d)\n", ret); + return ret; + } + ret = fdt_grow(&ctx, WOLFBOOT_FDT_FIXUP_HEADROOM); + if (ret != 0) { + wolfBoot_printf("FDT: no headroom for fixups (%d)\n", ret); + return ret; + } + + off = fdt_find_devtype(&ctx, -1, "memory"); + if (off >= 0) { + uint64_t reg[2]; + + reg[0] = cpu_to_fdt64(AGILEX5_DDR_BASE); + reg[1] = cpu_to_fdt64(AGILEX5_DDR_SIZE); + ret = fdt_setprop(&ctx, off, "reg", reg, sizeof(reg)); + if (ret != 0) { + wolfBoot_printf("FDT: failed to set memory (%d)\n", ret); + return ret; + } + wolfBoot_printf("FDT: Set memory, start=0x%lx, size=0x%lx\n", + (unsigned long)AGILEX5_DDR_BASE, + (unsigned long)AGILEX5_DDR_SIZE); + } + + /* wolfBoot does not program the fabric, so the board config lists the + * nodes whose hardware sits behind the unprogrammed FPGA. */ +#ifdef WOLFBOOT_FDT_DISABLE_PATHS + for (i = 0; i < sizeof(fdt_disable_paths) / + sizeof(fdt_disable_paths[0]); i++) { + off = fdt_path_offset(&ctx, fdt_disable_paths[i]); + if (off < 0) + continue; + ret = fdt_fixup_str(&ctx, off, fdt_disable_paths[i], "status", + "disabled"); + if (ret != 0) { + wolfBoot_printf("FDT: failed to disable %s (%d)\n", + fdt_disable_paths[i], ret); + return ret; + } + } +#endif + + off = fdt_subnode_offset(&ctx, 0, "chosen"); + if (off == -FDT_ERR_NOTFOUND) + off = fdt_add_subnode(&ctx, 0, "chosen"); + if (off < 0) + return off; + return fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS); +} +#endif +#endif + +void RAMFUNCTION hal_flash_unlock(void) {} +void RAMFUNCTION hal_flash_lock(void) {} + +int RAMFUNCTION hal_flash_write(uintptr_t address, const uint8_t *data, + int len) +{ + (void)address; + (void)data; + (void)len; + return -1; +} + +int RAMFUNCTION hal_flash_erase(uintptr_t address, int len) +{ + (void)address; + (void)len; + return -1; +} + +#if defined(DISK_SDCARD) || defined(DISK_EMMC) +#include "sdhci.h" + +static void agilex5_sdhci_write_phy(uint32_t address, uint32_t value) +{ + sdhci_reg_write(SDHCI_HRS04, address); + sdhci_reg_write(AGILEX5_SDHCI_HRS05, value); +} + +static int agilex5_sdhci_phy_init(void) +{ + uint32_t reg; + uint32_t timeout = AGILEX5_SDHCI_PHY_INIT_TIMEOUT; + + reg = sdhci_reg_read(AGILEX5_SDHCI_HRS09); + reg &= ~AGILEX5_SDHCI_PHY_SW_RESET; + sdhci_reg_write(AGILEX5_SDHCI_HRS09, reg); + + /* Match the 013B SD default-speed settings used by its U-Boot DTS. */ + agilex5_sdhci_write_phy(AGILEX5_SDHCI_PHY_DQS_TIMING, 0x00780000U); + agilex5_sdhci_write_phy(AGILEX5_SDHCI_PHY_GATE_LPBK_CTRL, 0x81A40040U); + agilex5_sdhci_write_phy(AGILEX5_SDHCI_PHY_DLL_MASTER_CTRL, 0x00800004U); + agilex5_sdhci_write_phy(AGILEX5_SDHCI_PHY_DLL_SLAVE_CTRL, 0x00A000FEU); + + reg |= AGILEX5_SDHCI_PHY_SW_RESET; + sdhci_reg_write(AGILEX5_SDHCI_HRS09, reg); + while (((sdhci_reg_read(AGILEX5_SDHCI_HRS09) & + AGILEX5_SDHCI_PHY_INIT_COMPLETE) == 0U) && + (--timeout > 0U)) {} + if (timeout == 0U) + return -1; + + agilex5_sdhci_write_phy(AGILEX5_SDHCI_PHY_DQ_TIMING, 0x28000001U); + + reg = sdhci_reg_read(AGILEX5_SDHCI_HRS09); + reg &= ~((1U << 16) | (1U << 15) | (1U << 3) | (1U << 2)); + reg |= 0x0001800CU; + sdhci_reg_write(AGILEX5_SDHCI_HRS09, reg); + sdhci_reg_write(AGILEX5_SDHCI_HRS10, 0x00020000U); + sdhci_reg_write(AGILEX5_SDHCI_HRS16, 0x00000000U); + sdhci_reg_write(AGILEX5_SDHCI_HRS07, 0x00080000U); + + return 0; +} + +uint32_t sdhci_reg_read(uint32_t offset) +{ + return *(volatile uint32_t *)(AGILEX5_SDHCI_BASE + offset); +} + +void sdhci_reg_write(uint32_t offset, uint32_t value) +{ + *(volatile uint32_t *)(AGILEX5_SDHCI_BASE + offset) = value; +} + +void sdhci_platform_init(void) +{ + /* SPL initialized the clocks and pinmux; the generic driver then soft + * resets the host, so restore the 013B PHY timing here. */ + if (agilex5_sdhci_phy_init() != 0) + wolfBoot_printf("SDHCI: Cadence PHY initialization timed out\n"); +} + +void sdhci_platform_irq_init(void) +{ + /* The generic driver polls; BL31 retains ownership of GICv3 setup. */ +} + +void sdhci_platform_set_bus_mode(int is_emmc) +{ + (void)is_emmc; +} + +#ifndef SDHCI_SDMA_DISABLED +void sdhci_platform_dma_prepare(void *buf, uint32_t size, int is_write) +{ + uintptr_t addr; + uintptr_t start = (uintptr_t)buf & ~(CACHE_LINE_SIZE - 1U); + uintptr_t end = ((uintptr_t)buf + size + CACHE_LINE_SIZE - 1U) & + ~(CACHE_LINE_SIZE - 1U); + + for (addr = start; addr < end; addr += CACHE_LINE_SIZE) { + if (is_write != 0) + __asm__ volatile("dc cvac, %0" : : "r"(addr) : "memory"); + else + __asm__ volatile("dc ivac, %0" : : "r"(addr) : "memory"); + } + __asm__ volatile("dsb sy" : : : "memory"); +} + +void sdhci_platform_dma_complete(void *buf, uint32_t size, int is_write) +{ + uintptr_t addr; + uintptr_t start = (uintptr_t)buf & ~(CACHE_LINE_SIZE - 1U); + uintptr_t end = ((uintptr_t)buf + size + CACHE_LINE_SIZE - 1U) & + ~(CACHE_LINE_SIZE - 1U); + + if (is_write == 0) { + for (addr = start; addr < end; addr += CACHE_LINE_SIZE) + __asm__ volatile("dc ivac, %0" : : "r"(addr) : "memory"); + __asm__ volatile("dsb sy" : : : "memory"); + } +} +#endif +#endif + +#endif /* TARGET_agilex5 */ diff --git a/hal/agilex5.h b/hal/agilex5.h new file mode 100644 index 0000000000..afe6d177e7 --- /dev/null +++ b/hal/agilex5.h @@ -0,0 +1,112 @@ +/* agilex5.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 + */ + +#ifndef WOLFSSL_AGILEX5_H +#define WOLFSSL_AGILEX5_H + +#ifndef EL3_SECURE +#define EL3_SECURE 0 +#endif +#ifndef EL2_HYPERVISOR +#define EL2_HYPERVISOR 1 +#endif +#ifndef EL1_NONSECURE +#define EL1_NONSECURE 1 +#endif +#ifndef HYP_GUEST +#define HYP_GUEST 0 +#endif +#ifndef FPU_TRAP +#define FPU_TRAP 0 +#endif +#ifndef SKIP_GIC_INIT +#define SKIP_GIC_INIT 1 +#endif + +/* Preserve EL2 for Linux/KVM, matching the normal TF-A -> U-Boot handoff. */ +#if !defined(BOOT_EL1) && !defined(BOOT_EL2) +#define BOOT_EL2 1 +#endif +#if defined(BOOT_EL1) && defined(BOOT_EL2) +#error "BOOT_EL1 and BOOT_EL2 are mutually exclusive" +#endif + +#define AGILEX5_DDR_BASE 0x80000000UL +/* The GSRD U-Boot device tree starts HPS DDR at 0x80000000. U-Boot normally + * writes the measured size into the DTB; wolfBoot supplies the 013B's + * 1792 MiB because it hands the DTB directly to Linux. */ +#ifndef AGILEX5_DDR_SIZE +#define AGILEX5_DDR_SIZE 0x70000000UL +#endif + +/* Agilex 5 HPS UART0 and UART1 MMIO bases. The GSRD device tree maps these as + * serial@10c02000 and serial@10c02100. The selected value is also emitted in + * Linux's earlycon argument, so keep the address macros free of a C suffix. */ +#define AGILEX5_UART0_BASE 0x10C02000 +#define AGILEX5_UART1_BASE 0x10C02100 +#ifndef DEBUG_UART_BASE + #if defined(DEBUG_UART_NUM) && DEBUG_UART_NUM == 1 + #define DEBUG_UART_BASE AGILEX5_UART1_BASE + #else + #define DEBUG_UART_BASE AGILEX5_UART0_BASE + #endif +#endif + +/* HPS SDMMC controller base from the Agilex 5 HPS memory map. */ +#define AGILEX5_SDHCI_BASE 0x10808000UL +/* The Cortex-A55 data cache uses 64-byte lines. */ +#define CACHE_LINE_SIZE 64UL + +/* TF-A programs the architectural counter to 100 MHz on the GSRD boot path. */ +#ifndef TIMER_CLK_FREQ +#define TIMER_CLK_FREQ 100000000UL +#endif + +/* Cadence SD6HC host and PHY register offsets from the GSRD U-Boot driver. */ +#define AGILEX5_SDHCI_HRS05 0x014U +#define AGILEX5_SDHCI_HRS07 0x01CU +#define AGILEX5_SDHCI_HRS09 0x024U +#define AGILEX5_SDHCI_HRS10 0x028U +#define AGILEX5_SDHCI_HRS16 0x040U +#define AGILEX5_SDHCI_PHY_DQ_TIMING 0x2000U +#define AGILEX5_SDHCI_PHY_DQS_TIMING 0x2004U +#define AGILEX5_SDHCI_PHY_GATE_LPBK_CTRL 0x2008U +#define AGILEX5_SDHCI_PHY_DLL_MASTER_CTRL 0x200CU +#define AGILEX5_SDHCI_PHY_DLL_SLAVE_CTRL 0x2010U +#define AGILEX5_SDHCI_PHY_INIT_COMPLETE (1U << 1) +#define AGILEX5_SDHCI_PHY_SW_RESET (1U << 0) +#define AGILEX5_SDHCI_PHY_INIT_TIMEOUT 1000000U + +/* wolfBoot occupies the GSRD BL33 region at 0x80200000. The DTB is staged at + * 0x8f000000, matching agilex5.its, and the signed image uses the next + * 256 MiB at 0x90000000 so neither buffer overlaps wolfBoot or the DTB. */ +#ifndef WOLFBOOT_LOAD_ADDRESS +#define WOLFBOOT_LOAD_ADDRESS 0x90000000UL +#endif +#ifndef WOLFBOOT_LOAD_DTS_ADDRESS +#define WOLFBOOT_LOAD_DTS_ADDRESS 0x8F000000UL +#endif +#ifndef WOLFBOOT_RAMBOOT_MAX_SIZE +#define WOLFBOOT_RAMBOOT_MAX_SIZE 0x10000000UL +#endif + +#endif /* WOLFSSL_AGILEX5_H */ diff --git a/hal/agilex5.its b/hal/agilex5.its new file mode 100644 index 0000000000..d906008640 --- /dev/null +++ b/hal/agilex5.its @@ -0,0 +1,39 @@ +/dts-v1/; + +/ { + description = "Altera Agilex 5 wolfBoot Linux payload"; + #address-cells = <1>; + + images { + kernel-1 { + description = "Linux kernel"; + data = /incbin/("../Image"); + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x82000000>; + entry = <0x82000000>; + hash-1 { algo = "sha256"; }; + }; + fdt-1 { + description = "Agilex 5 device tree"; + data = /incbin/("../socfpga_agilex5_socdk.dtb"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + load = <0x8f000000>; + hash-1 { algo = "sha256"; }; + }; + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "Agilex 5 Linux"; + kernel = "kernel-1"; + fdt = "fdt-1"; + hash-1 { algo = "sha256"; }; + }; + }; +}; diff --git a/hal/agilex5.ld b/hal/agilex5.ld new file mode 100644 index 0000000000..d391974142 --- /dev/null +++ b/hal/agilex5.ld @@ -0,0 +1,132 @@ +/* agilex5.ld + * + * 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 + */ + +OUTPUT_FORMAT("elf64-littleaarch64") +OUTPUT_ARCH(aarch64) +ENTRY(_vector_table) + +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x20000; +_EL0_STACK_SIZE = 0x1000; +_EL1_STACK_SIZE = 0x2000; +_EL2_STACK_SIZE = 0x8000; + +MEMORY +{ + /* Matches CONFIG_TEXT_BASE and the BL33 load/entry in GSRD u-boot.itb. */ + DDR (rwx) : ORIGIN = 0x80200000, LENGTH = 0x200000 +} + +SECTIONS +{ + .text : { + _start_text = .; + KEEP (*(.vectors)) + *(.boot) + *(.text .text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.ARM.extab*) + _end_text = .; + } > DDR + + .rodata : { + . = ALIGN(64); + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + } > DDR + + .keystore : { + . = ALIGN(8); + KEEP (*(.keystore)) + } > DDR + + .data : { + . = ALIGN(64); + _start_data = .; + __data_start = .; + *(.data .data.*) + *(.sdata .sdata.*) + *(.got .got.*) + _end_data = .; + __data_end = .; + } > DDR + + .mmu_tbl0 (ALIGN(4096)) : { + *(.mmu_tbl0) + } > DDR + .mmu_tbl1 (ALIGN(4096)) : { + *(.mmu_tbl1) + } > DDR + .mmu_tbl2 (ALIGN(4096)) : { + *(.mmu_tbl2) + } > DDR + + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > DDR + + .ramcode : { + . = ALIGN(64); + *(.ramcode .ramcode.*) + } > DDR + + .bss (NOLOAD) : { + . = ALIGN(64); + __bss_start__ = .; + _start_bss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + . = ALIGN(64); + __bss_end__ = .; + _end_bss = .; + } > DDR + + .heap (NOLOAD) : { + . = ALIGN(64); + _heap = .; + . += _HEAP_SIZE; + __heap_end = .; + } > DDR + + .stack (NOLOAD) : { + . = ALIGN(64); + . += 0x1000; + __el3_stack = .; + . += _EL2_STACK_SIZE; + __el2_stack = .; + . += _EL1_STACK_SIZE; + __el1_stack = .; + . += _EL0_STACK_SIZE; + __el0_stack = .; + } > DDR + + _end = .; + ASSERT(_end <= ORIGIN(DDR) + LENGTH(DDR), + "wolfBoot Agilex 5 image exceeds the 2 MiB BL33 reservation") +} + +PROVIDE(_start_vector = _start_text); +PROVIDE(__stack = __el2_stack); +PROVIDE(_stack_base = .); diff --git a/hal/versal.h b/hal/versal.h index eddd6d5885..31e36873ff 100644 --- a/hal/versal.h +++ b/hal/versal.h @@ -598,14 +598,6 @@ */ #ifndef __ASSEMBLER__ -/* Get current exception level */ -static inline unsigned int current_el(void) -{ - unsigned long el; - __asm__ volatile("mrs %0, CurrentEL" : "=r" (el)); - return (unsigned int)((el >> 2) & 0x3); -} - /* Memory barrier */ static inline void dmb(void) { diff --git a/hal/zynq.c b/hal/zynq.c index 1791521b00..734cc59dca 100644 --- a/hal/zynq.c +++ b/hal/zynq.c @@ -34,6 +34,7 @@ #endif #include +#include "aarch64_arch.h" #include "image.h" #include "printf.h" #include "hal_fpga.h" @@ -113,7 +114,6 @@ static int test_ext_flash(QspiDev_t* dev); /* asm function */ extern void flush_dcache_range(unsigned long start, unsigned long stop); -extern unsigned int current_el(void); void hal_delay_ms(uint64_t ms); uint64_t hal_timer_ms(void); diff --git a/include/aarch64_arch.h b/include/aarch64_arch.h index 2c7c81e0f7..eb51c1f18a 100644 --- a/include/aarch64_arch.h +++ b/include/aarch64_arch.h @@ -19,23 +19,28 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -/* Architectural AArch64 helpers that carry no SoC-specific state: the generic - * timer and set/way data-cache maintenance. Several HALs each grew their own - * copy of these; this is the shared home for them. Inline, so including it - * changes nothing for a HAL that does not call them. +/* Architectural AArch64 helpers that carry no SoC-specific state: exception + * level access, the generic timer and set/way data-cache maintenance. Several + * HALs each grew their own copy of these; this is the shared home for them. + * Inline, so including it changes nothing for a HAL that does not call them. * - * Define TIMER_CLK_FREQ before including for the rate to assume when firmware - * left CNTFRQ_EL0 unprogrammed. */ + * Define TIMER_CLK_FREQ before including to expose the timer helpers and set + * the rate to assume when firmware left CNTFRQ_EL0 unprogrammed. */ #ifndef _AARCH64_ARCH_H_ #define _AARCH64_ARCH_H_ #include -#ifndef TIMER_CLK_FREQ -#error "TIMER_CLK_FREQ must be defined before including aarch64_arch.h" -#endif +static inline unsigned int current_el(void) +{ + unsigned long el; + __asm__ volatile("mrs %0, CurrentEL" : "=r"(el) : : "cc"); + return (unsigned int)((el >> 2) & 0x3U); +} + +#ifdef TIMER_CLK_FREQ /* CNTPCT_EL0, the architectural counter. */ static inline uint64_t timer_get_count(void) { @@ -68,6 +73,7 @@ static inline int timer_expired(uint64_t deadline) { return timer_get_count() > deadline; } +#endif /* Data-cache maintenance by set/way over every level to the point of * coherency. clean != 0 -> clean+invalidate, else invalidate only. */ diff --git a/include/sdhci.h b/include/sdhci.h index 333dcb3e2a..091b7feb41 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -247,6 +247,8 @@ #define SDHCI_SRS11_SDCFSH_MASK (0x03U << 6) #define SDHCI_SRS11_DTCV_SHIFT 16 #define SDHCI_SRS11_DTCV_MASK (0x0FU << 16) +#define SDHCI_SRS11_RESET_CMD (1U << 25) +#define SDHCI_SRS11_RESET_DAT (1U << 26) #define SDHCI_SRS11_RESET_DAT_CMD ((1U << 25) | (1U << 26)) #define SDHCI_SRS11_RESET_ALL (1U << 24) @@ -490,4 +492,3 @@ void sdhci_platform_set_bus_mode(int is_emmc); #endif /* DISK_SDCARD || DISK_EMMC */ #endif /* SDHCI_H */ - diff --git a/lib/wolfssl b/lib/wolfssl index 57502e3ded..f99453320f 160000 --- a/lib/wolfssl +++ b/lib/wolfssl @@ -1 +1 @@ -Subproject commit 57502e3ded5776ada0f3a15c4b0a6e0219007563 +Subproject commit f99453320ff306fe5bfb10788117f18c9bfeaf6b diff --git a/src/boot_aarch64.c b/src/boot_aarch64.c index 85b825959e..a06ccc9868 100644 --- a/src/boot_aarch64.c +++ b/src/boot_aarch64.c @@ -32,12 +32,16 @@ * compile in for the active target. */ #if defined(TARGET_versal) #include "hal/versal.h" +#elif defined(TARGET_agilex5) +#include "hal/agilex5.h" #elif defined(TARGET_zynq) #include "hal/zynq.h" #elif defined(TARGET_nxp_ls1028a) #include "hal/nxp_ls1028a.h" #endif +#include "aarch64_arch.h" + /* Linker exported variables */ extern unsigned int __bss_start__; extern unsigned int __bss_end__; @@ -76,16 +80,6 @@ extern void flush_dcache_range(unsigned long start, unsigned long end); #define SKIP_GIC_INIT #endif -#ifndef TARGET_versal -/* current_el() is defined in hal/versal.h for Versal */ -unsigned int current_el(void) -{ - unsigned long el; - asm volatile("mrs %0, CurrentEL" : "=r" (el) : : "cc"); - return (unsigned int)((el >> 2) & 0x3U); -} -#endif - #if defined(BOOT_EL1) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1 /** * @brief Transition from EL2 to EL1 and jump to application diff --git a/src/boot_aarch64_start.S b/src/boot_aarch64_start.S index e9c99d44be..ef7375b017 100644 --- a/src/boot_aarch64_start.S +++ b/src/boot_aarch64_start.S @@ -30,6 +30,10 @@ #include "hal/versal.h" #endif +#ifdef TARGET_agilex5 +#include "hal/agilex5.h" +#endif + #ifdef TARGET_nxp_ls1028a #include "hal/nxp_ls1028a.h" #endif @@ -95,10 +99,11 @@ .set L2Table, MMUTableL2 .set vector_base, _vector_table -#ifdef TARGET_versal +#if defined(TARGET_versal) /* Versal: RVBAR is handled by PLM, not accessible from APU in JTAG mode */ -/* Cortex-A72 timestamp clock frequency (from Versal HW) */ .set counterfreq, 100000000 +#elif defined(TARGET_agilex5) +/* BL31 has already programmed CNTFRQ_EL0 before entering wolfBoot. */ #else .set rvbar_base, 0xFD5C0040 /* Cortex-A53 timestamp clock frequency */ @@ -230,7 +235,7 @@ InitEL3: * sets it to the actual system-counter rate, which our usleep() needs for * accurate psu_init DDR-training delays. Hardcoding 100MHz here is wrong * when the counter runs at the BootROM (undivided) rate. */ -#ifndef WOLFBOOT_ZYNQMP_FSBL +#if !defined(WOLFBOOT_ZYNQMP_FSBL) && !defined(TARGET_agilex5) #ifdef TARGET_nxp_ls1028a /* Read the true base frequency from the system counter (CNTFID0 @ SYSCNT * base+0x20 = SYS_REF_CLK/4); the counterfreq constant is the undivided @@ -724,6 +729,14 @@ MMUTableL2: .set SECT, 0 +#ifdef TARGET_agilex5 +/* Agilex 5 HPS peripherals and bridges occupy the low 2GiB. Keep them + * strongly ordered; cached HPS DDR starts at 0x8000_0000 below. */ +.rept 0x0400 /* 0x0000_0000 - 0x7FFF_FFFF */ +.8byte SECT + Device +.set SECT, SECT+0x200000 +.endr +#else #ifdef XPAR_PSU_DDR_0_S_AXI_BASEADDR .set DDR_0_START, XPAR_PSU_DDR_0_S_AXI_BASEADDR .set DDR_0_END, XPAR_PSU_DDR_0_S_AXI_HIGHADDR @@ -749,7 +762,15 @@ MMUTableL2: .8byte SECT + reserved .set SECT, SECT+0x200000 .endr +#endif /* TARGET_agilex5 */ +#ifdef TARGET_agilex5 +/* Agilex 5 GSRD maps the 2GiB HPS DDR window at 0x8000_0000. */ +.rept 0x0400 /* 0x8000_0000 - 0xFFFF_FFFF */ +.8byte SECT + Memory +.set SECT, SECT+0x200000 +.endr +#else .rept 0x0200 /* 0x8000_0000 - 0xBFFF_FFFF */ .8byte SECT + Device /* 1GB lower PL */ .set SECT, SECT+0x200000 @@ -813,6 +834,7 @@ converted to 62MB */ .set SECT, SECT+0x200000 /* 0xFFE0_0000 - 0xFFFF_FFFF*/ .8byte SECT + Memory /* 2MB OCM/TCM */ +#endif /* TARGET_agilex5 */ #else /* TARGET_nxp_ls1028a */ /* ============================================================================ diff --git a/src/sdhci.c b/src/sdhci.c index 42af1fe713..614ddafb7a 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -904,10 +904,24 @@ static int sdcard_power_init_seq(uint32_t voltage) * SDHCI platforms deliberately: the delay is harmless settle * margin and the SD spec permits it. */ udelay(200); - /* send the operating conditions command */ - status = sdhci_cmd(SD_CMD8_SEND_IF_COND, SD_IF_COND_27V_33V, - SDHCI_RESP_R7); - cmd8_err = g_last_cmd_err; + /* A corrupted CMD8 response can be transient while the card settles + * after CMD0. A timeout alone is how an SD v1.x card reports that it + * does not implement CMD8, so do not repeat that silent probe. */ + for (retries = 0; retries < 10; retries++) { + status = sdhci_cmd(SD_CMD8_SEND_IF_COND, SD_IF_COND_27V_33V, + SDHCI_RESP_R7); + cmd8_err = g_last_cmd_err; + if (status == 0 || + ((cmd8_err & SDHCI_SRS12_ECT) != 0 && + (cmd8_err & (SDHCI_SRS12_ECCRC | SDHCI_SRS12_ECEB | + SDHCI_SRS12_ECI)) == 0)) { + break; + } + udelay(10000); + } + if (status == 0 && retries > 0) { + wolfBoot_printf("SD: CMD8 succeeded after %d retries\n", retries); + } #if defined(DISK_SDCARD) && defined(SDHCI_UHS_RECOVER_ON_INIT) if (status != 0) { /* Opt-in. A card a previous stage left in UHS-I is at 1.8V and @@ -1918,7 +1932,7 @@ int sdhci_init(void) * not be ready to accept register writes on some platforms. */ udelay(1000); /* 1ms */ - /* Reset the host controller */ + /* Restore host controller state after the earlier boot stage. */ sdhci_reg_or(SDHCI_HRS00, SDHCI_HRS00_SWR); /* Bit will clear when reset is done */ while ((SDHCI_REG(SDHCI_HRS00) & SDHCI_HRS00_SWR) != 0); diff --git a/src/update_disk.c b/src/update_disk.c index 8cdacb7a6a..20fd19cef9 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -753,7 +753,7 @@ void RAMFUNCTION wolfBoot_start(void) load_address = (uint32_t *)((((uintptr_t)_end_wb) + 0xf) & ~0xf); #endif - wolfBoot_printf("Load address 0x%x\r\n", load_address); + wolfBoot_printf("Load address %p\r\n", load_address); /* Upper bound on anything the media may claim about the image size. * The payload is copied into the load region before its signature is @@ -840,7 +840,13 @@ void RAMFUNCTION wolfBoot_start(void) pB_ver_u = pB_ver; wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver_u, pB_ver_u); - wolfBoot_printf("Load block size: %dKB\r\n", DISK_BLOCK_SIZE / 1024); +#if DISK_BLOCK_SIZE < 1024 + wolfBoot_printf("Load block size: %u bytes\r\n", + (unsigned int)DISK_BLOCK_SIZE); +#else + wolfBoot_printf("Load block size: %uKB\r\n", + (unsigned int)(DISK_BLOCK_SIZE / 1024U)); +#endif max_ver = (pB_ver_u > pA_ver_u) ? pB_ver_u : pA_ver_u; /* Choose partition with higher version */ @@ -1252,7 +1258,7 @@ void RAMFUNCTION wolfBoot_start(void) } #endif - wolfBoot_printf("Booting at %08lx\r\n", load_address); + wolfBoot_printf("Booting at %p\r\n", load_address); #ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT (void)hal_hsm_disconnect(); diff --git a/test-app/Makefile b/test-app/Makefile index 6de928cee9..103bef92d0 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -600,10 +600,18 @@ else ifeq ($(WOLFHAL),1) APP_OBJS+=../hal/wolfhal.o else - APP_OBJS+=../hal/$(TARGET).o + # Keep the Agilex test HAL separate from the bootloader HAL. The + # test-app rule below is forced and uses different feature defines; + # sharing ../hal/agilex5.o can silently replace the bootloader object + # before a later incremental relink. + ifeq ($(TARGET),agilex5) + APP_OBJS+=hal_agilex5.o + else + APP_OBJS+=../hal/$(TARGET).o + endif # The test-app links the same HAL object, so HALs using the # generic NS16550 console need it here too. - ifneq (,$(filter $(TARGET),nxp_ls1028a nxp_t2080 nxp_t1024 nxp_t1040 nxp_p1021)) + ifneq (,$(filter $(TARGET),agilex5 nxp_ls1028a nxp_t2080 nxp_t1024 nxp_t1040 nxp_p1021)) ifeq ($(DEBUG_UART),1) APP_OBJS+=../hal/uart/ns16550.o endif @@ -1696,6 +1704,9 @@ delta-extra-data: image.bin ../hal/$(TARGET).o: ../hal/$(TARGET).c FORCE $(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/$(TARGET).c +hal_agilex5.o: ../hal/agilex5.c FORCE + $(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/agilex5.c + ../hal/$(TARGET)_ns.o: ../hal/$(TARGET).c FORCE $(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/$(TARGET).c -DNONSECURE_APP diff --git a/test-app/app_agilex5.c b/test-app/app_agilex5.c new file mode 100644 index 0000000000..7eddbaf3ac --- /dev/null +++ b/test-app/app_agilex5.c @@ -0,0 +1,49 @@ +/* app_agilex5.c + * + * 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 "hal.h" +#include "hal/agilex5.h" +#include "aarch64_arch.h" +#include "printf.h" + +void main(void) +{ + uint64_t start; + uint64_t now; + + hal_init(); + wolfBoot_printf("\nAgilex 5 BL33 smoke test\n"); + wolfBoot_printf("Current EL: %u (expected 2)\n", current_el()); + + start = hal_get_timer_us(); + do { + now = hal_get_timer_us(); + } while ((now - start) < 1000U); + wolfBoot_printf("Generic timer advanced by %u us\n", + (unsigned int)(now - start)); + wolfBoot_printf("AGILEX5_BL33_SMOKE_PASS\n"); + + while (1) + __asm__ volatile("wfi"); +} diff --git a/test-app/app_versal.c b/test-app/app_versal.c index f87b295ef7..23d4abbf0f 100644 --- a/test-app/app_versal.c +++ b/test-app/app_versal.c @@ -25,6 +25,7 @@ #include "hal.h" #include "hal/versal.h" +#include "aarch64_arch.h" #include "wolfboot/wolfboot.h" #include "printf.h" diff --git a/test-app/app_zynq.c b/test-app/app_zynq.c index f9137439ab..ea9539aa88 100644 --- a/test-app/app_zynq.c +++ b/test-app/app_zynq.c @@ -23,19 +23,12 @@ #include +#include "aarch64_arch.h" #include "wolfboot/wolfboot.h" #include "printf.h" #ifdef TARGET_zynq -/* Provide current_el() for hal/zynq.o (normally in boot_aarch64.c) */ -__attribute__((weak)) unsigned int current_el(void) -{ - unsigned long el; - __asm__ volatile("mrs %0, CurrentEL" : "=r" (el) : : "cc"); - return (unsigned int)((el >> 2) & 0x3U); -} - /* Stub for QSPI DMA code in hal/zynq.o (not used with SD card boot) */ void flush_dcache_range(unsigned long start, unsigned long end) { diff --git a/tools/test.mk b/tools/test.mk index 64f92ba950..6226fdf287 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1243,6 +1243,9 @@ test-size-all: # Re-measured 2026-09-29: NONE +24B (5204), ECC256/ECC384 +12B each, from # the uart printf %u/is_signed change compiled into every build; RSA, # RSAPSS, LMS and ML_DSA measured 8B smaller than their limits. + # Re-measured 2026-10-12 after the lib/wolfssl bump to f99453320: XMSS + # +20B from working-state zeroization in bb748a75a, ML_DSA +36B from the + # same wolfSSL update, and LMS +4B (still below its existing limit). make test-size SIGN=NONE LIMIT=5204 NO_ARM_ASM=1 make keysclean make test-size SIGN=ED25519 LIMIT=12356 NO_ARM_ASM=1 @@ -1287,8 +1290,8 @@ test-size-all: make keysclean make test-size SIGN=XMSS XMSS_PARAMS='XMSS-SHA2_10_256' \ IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE?=4096 \ - LIMIT=8852 NO_ARM_ASM=1 + LIMIT=8856 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19694 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19726 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 5ccc3509b1..6be825897f 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -1370,10 +1370,11 @@ unit-sama5d3-ext-read: unit-sama5d3-ext-read.c sama5d3_read_extract.h # -O0 a guaranteed infinite loop in the timer helpers. # hal/versal.c now gets its counter accessors from include/aarch64_arch.h. # The generator only rewrites the .c and .h it copies, so the header needs the -# same treatment: its two "mrs" reads have output operands and must become +# same treatment: its "mrs" reads have output operands and must become # assignments, not deletions (see the note above versal_host.h). aarch64_arch_host.h: ../../include/aarch64_arch.h - sed -E -e 's/__asm__ volatile\("mrs %0, cntpct_el0" : "=r" \(cntpct\)\);/cntpct = ++versal_host_ticks;/' \ + sed -E -e 's/__asm__ volatile\("mrs %0, CurrentEL" : "=r"\(el\) : : "cc"\);/el = 3UL << 2;/' \ + -e 's/__asm__ volatile\("mrs %0, cntpct_el0" : "=r" \(cntpct\)\);/cntpct = ++versal_host_ticks;/' \ -e 's/__asm__ volatile\("mrs %0, cntfrq_el0" : "=r" \(cntfrq\)\);/cntfrq = 1000000ULL;/' \ -e 's/__asm__ volatile\("mrs %0, clidr_el1" : "=r"\(clidr\)\);/clidr = 0; \/* host: report no cache levels, so the walk is a no-op *\//' \ -e 's/__asm__ volatile\("mrs %0, ccsidr_el1" : "=r"\(ccsidr\)\);/ccsidr = 0;/' \ @@ -1385,7 +1386,6 @@ aarch64_arch_host.h: ../../include/aarch64_arch.h versal_host.h: ../../hal/versal.h sed -E -e 's/0xF1030000UL/((uintptr_t)g_vqspi_regs)/' \ - -e 's/__asm__ volatile\("mrs %0, CurrentEL" : "=r" \(el\)\);/el = 3UL << 2; \/* host stands in at EL3 *\//' \ -e 's/^[[:space:]]*__asm__[[:space:]]+(__)?(volatile__|volatile)\("[^"]*"\);[[:space:]]*//' \ -e 's/^[[:space:]]*__asm__[[:space:]]+(__)?(volatile__|volatile)\("[^"]*"[[:space:]]*:[[:space:]]*:[^;]*\);[[:space:]]*//' $< > $@ test -s $@ diff --git a/tools/unit-tests/unit-sdhci-cmd-inhibit.c b/tools/unit-tests/unit-sdhci-cmd-inhibit.c index 619d1e6e2c..d664d5c1a4 100644 --- a/tools/unit-tests/unit-sdhci-cmd-inhibit.c +++ b/tools/unit-tests/unit-sdhci-cmd-inhibit.c @@ -61,6 +61,7 @@ static int g_cmd8_never; /* v1.x: CMD8 is never answered */ static int g_cmd8_needs_18v; /* left in UHS: answered only at 1.8V */ static int g_cicmd_stuck; /* the command line never goes idle */ static int g_cmd8_mangled; /* CMD8 IS answered, but the link corrupts it */ +static int g_cmd8_mangled_once; /* first CMD8 response is corrupted */ static int g_reset_never; /* the reset bit itself never self-clears */ static int g_inhibit_sticky; /* reset completes, but CICMD stays set */ static unsigned int g_cmd8_attempts; @@ -116,7 +117,8 @@ void sdhci_reg_write(uint32_t offset, uint32_t val) & SDHCI_SRS15_V18SE) != 0; g_cmd8_attempts++; - if (g_cmd8_mangled) { + if (g_cmd8_mangled || + (g_cmd8_mangled_once && g_cmd8_attempts == 1)) { /* The card DID answer; the link mangled the response. Not a * timeout, so not a legacy card. */ g_sdhci_regs[SDHCI_SRS12 / sizeof(uint32_t)] |= @@ -184,6 +186,7 @@ static void setup(void) g_cmd8_never = 0; g_cmd8_needs_18v = 0; g_cmd8_mangled = 0; + g_cmd8_mangled_once = 0; g_reset_never = 0; g_inhibit_sticky = 0; g_wdt_pets = 0; @@ -275,6 +278,20 @@ START_TEST(test_uhs_recover_rolls_back) END_TEST #endif +/* A transient response error is retried; a working card still reaches + * ACMD41 after its next CMD8 answer. */ +START_TEST(test_cmd8_transient_error_retries) +{ + script_card_present(); + g_cmd8_mangled_once = 1; + + (void)sdcard_card_full_init(); + + ck_assert_uint_eq(g_cmd8_attempts, 2); + ck_assert_uint_gt(g_acmd41_polls, 0); +} +END_TEST + /* A mangled CMD8 response is NOT a legacy card. * * Silence means the card does not implement CMD8; a CRC or index error means @@ -345,6 +362,7 @@ Suite *sdhci_cmd_inhibit_suite(void) tcase_add_test(tc, test_command_inhibit_does_not_hang); tcase_add_test(tc, test_cmd8_absent_is_not_fatal); tcase_add_test(tc, test_cmd8_answered_normally); + tcase_add_test(tc, test_cmd8_transient_error_retries); tcase_add_test(tc, test_cmd8_bus_error_is_still_fatal); tcase_add_test(tc, test_inhibit_that_survives_reset_does_not_hang); tcase_add_test(tc, test_reset_that_never_clears_does_not_hang);