From 00459d8f928165524a521e017046da847cee871d Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 15 Apr 2026 14:16:04 -0700 Subject: [PATCH 1/5] ZynqMP ZCU102 SD-card Linux boot: EL2 cleanup, DTS bootargs, SDHCI init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the pieces needed to boot Linux end-to-end from the ZCU102 SD card with wolfBoot at EL2: * src/boot_aarch64_start.S: new el2_flush_and_disable_mmu helper that cleans D-cache to PoC, invalidates I-cache to PoU, and clears SCTLR_EL2.{M,C,I}, then returns. Satisfies the ARM64 Linux boot protocol and is also correct for any other payload that sets up its own translation (hypervisor, RTOS, later bootloader stage). * src/boot_aarch64.c: call el2_flush_and_disable_mmu from do_boot() on the EL2 direct-jump path before falling through to the br x4 block. Also pull in hal/zynq.h and hal/nxp_ls1028a.h so the EL_HYPERVISOR / BOOT_EL1 guards compile for those targets. * hal/zynq.c: implement hal_dts_fixup() — set /chosen/bootargs from LINUX_BOOTARGS (with a LINUX_BOOTARGS_ROOT default of /dev/mmcblk0p4) and grow DTB totalsize by 512 bytes to give fdt_setprop() headroom (matches hal/versal.c). Add hal_get_timer_us() via CNTPCT_EL0. * src/sdhci.c: add a 1 ms settling delay after sdhci_platform_init() and a CMD0 retry loop (up to 10 x 10 ms) so the ZCU102 Arasan controller reliably detects the card after the slot-type change + soft reset. * config/examples/zynqmp_sdcard.config: stay at EL2 by default (comment out BOOT_EL1), default rootfs to /dev/mmcblk0p4, turn DEBUG off. * hal/versal.c: correct the default LINUX_BOOTARGS_ROOT to /dev/mmcblk0p4 to match the shipped MBR layout. * docs/Targets.md: note the unconditional EL2 cleanup in the ZynqMP and Versal SD-card sections. Behavior change: non-Linux AArch64 EL2 payloads now enter with MMU off and caches clean instead of inheriting wolfBoot's tables. No in-tree payload relies on the old state leakage. --- config/examples/zynqmp_sdcard.config | 19 +++-- docs/Targets.md | 4 + hal/versal.c | 2 +- hal/zynq.c | 71 ++++++++++++++++- src/boot_aarch64.c | 39 ++++++++- src/boot_aarch64_start.S | 114 +++++++++++++++++++++++++++ src/sdhci.c | 28 ++++++- 7 files changed, 263 insertions(+), 14 deletions(-) diff --git a/config/examples/zynqmp_sdcard.config b/config/examples/zynqmp_sdcard.config index 00f62c4013..1f22c95acf 100644 --- a/config/examples/zynqmp_sdcard.config +++ b/config/examples/zynqmp_sdcard.config @@ -18,7 +18,7 @@ HASH?=SHA3 IMAGE_HEADER_SIZE?=1024 # Debug options -DEBUG?=1 +DEBUG?=0 DEBUG_SYMBOLS=1 DEBUG_UART=1 CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1 @@ -39,8 +39,12 @@ NO_XIP=1 # ELF loading support ELF?=1 -# Boot Exception Level: transition from EL2 -> EL1 before jumping to app -BOOT_EL1?=1 +# Boot Exception Level: leave wolfBoot at EL2 for handoff to Linux (matches +# the standard PetaLinux U-Boot flow and preserves KVM/hypervisor use of +# EL2). The EL2 Linux-cleanup path in do_boot() will clean dcache/disable +# MMU before jumping to the kernel. To drop to EL1 via ERET instead, set +# BOOT_EL1?=1 (requires EL2_HYPERVISOR=1, which is the hal/zynq.h default). +#BOOT_EL1?=1 # General options VTOR?=1 @@ -78,8 +82,13 @@ CFLAGS_EXTRA+=-DBOOT_PART_B=2 # Disk read chunk size (512KB) CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 -# Linux rootfs is on partition 4 (SD1 = mmcblk1) -CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk1p4\" +# Linux rootfs is on partition 4. Device naming depends on whether both +# ZynqMP SDHCI controllers are enabled in the XSA / device tree: +# * both sdhci0 + sdhci1 enabled -> SD1 = /dev/mmcblk1 +# * only sdhci1 enabled (ZCU102 default -> only external SD populated) +# -> SD1 = /dev/mmcblk0 +# Check `ls /sys/class/mmc_host/` on your running target to confirm. +CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\" # ============================================================================ # Boot Memory Layout diff --git a/docs/Targets.md b/docs/Targets.md index 6c915ee22d..f8d68a81f4 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -2599,6 +2599,8 @@ qemu-system-aarch64 -machine xlnx-zcu102 -cpu cortex-a53 -serial stdio -display Use `config/examples/zynqmp_sdcard.config`. This uses the Arasan SDHCI controller (SD1 - external SD card slot on ZCU102) and an **MBR** partitioned SD card. +wolfBoot unconditionally flushes the EL2 D-cache/I-cache and disables the EL2 MMU before handoff (see `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`), satisfying the ARM64 Linux boot protocol with no extra config flag required. + **Partition layout** | Partition | Name | Size | Type | Contents | |-----------|--------|-----------|-------------------------------|-------------------------------------------| @@ -3005,6 +3007,8 @@ Typical boot timing with ECC384/SHA384 signing: Use `config/examples/versal_vmk180_sdcard.config`. This uses the Arasan SDHCI controller and an **MBR** partitioned SD card. +Versal defaults to `BOOT_EL1` — the handoff goes through `el2_to_el1_boot` (ERET to EL1). Custom `BOOT_EL2` Versal configs get the same EL2 cache/MMU teardown as ZynqMP via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`, so no extra config flag is needed to boot Linux directly at EL2. + **Partition layout** | Partition | Name | Size | Type | Contents | |-----------|------|------|------|----------| diff --git a/hal/versal.c b/hal/versal.c index 5423e7c720..baf8aadd7a 100644 --- a/hal/versal.c +++ b/hal/versal.c @@ -66,7 +66,7 @@ /* Linux kernel command line arguments */ #ifndef LINUX_BOOTARGS #ifndef LINUX_BOOTARGS_ROOT -#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p2" +#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4" #endif #define LINUX_BOOTARGS \ diff --git a/hal/zynq.c b/hal/zynq.c index 37ff2ee181..1462e220d3 100644 --- a/hal/zynq.c +++ b/hal/zynq.c @@ -57,6 +57,20 @@ /* QSPI bare-metal */ #endif +/* DTB fixup for kernel command line. Override LINUX_BOOTARGS or + * LINUX_BOOTARGS_ROOT in your config to customize. + * + * Note: console=ttyPS0 is ZynqMP-specific (PS UART0). Versal's default + * (hal/versal.c) omits the console= token because Versal relies on + * earlycon alone plus a DT-declared stdout-path. */ +#ifndef LINUX_BOOTARGS +#ifndef LINUX_BOOTARGS_ROOT +#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4" +#endif +#define LINUX_BOOTARGS \ + "earlycon console=ttyPS0,115200 root=" LINUX_BOOTARGS_ROOT " rootwait" +#endif + /* QSPI Slave Device Information */ typedef struct QspiDev { uint32_t mode; /* GQSPI_GEN_FIFO_MODE_SPI, GQSPI_GEN_FIFO_MODE_DSPI or GQSPI_GEN_FIFO_MODE_QSPI */ @@ -1795,7 +1809,20 @@ void RAMFUNCTION ext_flash_unlock(void) } -#ifdef MMU +#if defined(MMU) && defined(__WOLFBOOT) +/* Get current time in microseconds using ARMv8 generic timer */ +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) + return 0; + /* Use __uint128_t to avoid overflow of (count * 1e6) at long uptimes + * (would overflow uint64_t after ~51h at 100MHz). */ + return (uint64_t)(((__uint128_t)count * 1000000ULL) / freq); +} + void* hal_get_dts_address(void) { #ifdef WOLFBOOT_DTS_BOOT_ADDRESS @@ -1809,8 +1836,46 @@ void* hal_get_dts_address(void) int hal_dts_fixup(void* dts_addr) { - /* place FDT fixup specific to ZynqMP here */ - //fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + int off, ret; + struct fdt_header *fdt = (struct fdt_header *)dts_addr; + + /* Verify FDT header */ + ret = fdt_check_header(dts_addr); + if (ret != 0) { + wolfBoot_printf("FDT: Invalid header! %d\n", ret); + return ret; + } + + wolfBoot_printf("FDT: Version %d, Size %d\n", + fdt_version(fdt), fdt_totalsize(fdt)); + + /* Expand totalsize so fdt_setprop() has in-blob free space to place + * a new/larger bootargs property. Physical headroom is already + * guaranteed by the load-address layout (DTB at WOLFBOOT_LOAD_DTS_ADDRESS, + * kernel loaded much higher), so growing the header is safe. Matches + * the pattern used in hal/versal.c:hal_dts_fixup. */ + fdt_set_totalsize(fdt, fdt_totalsize(fdt) + 512); + + /* Find /chosen node */ + off = fdt_find_node_offset(fdt, -1, "chosen"); + if (off < 0) { + /* Create /chosen node if it doesn't exist */ + off = fdt_add_subnode(fdt, 0, "chosen"); + } + if (off < 0) { + wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off); + return off; + } + + /* Set bootargs property - overrides PetaLinux default root= with + * the wolfBoot partition layout. */ + wolfBoot_printf("FDT: Setting bootargs: %s\n", LINUX_BOOTARGS); + ret = fdt_fixup_str(fdt, off, "chosen", "bootargs", LINUX_BOOTARGS); + if (ret < 0) { + wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret); + return ret; + } + return 0; } #endif diff --git a/src/boot_aarch64.c b/src/boot_aarch64.c index 7b8fccbadc..495a5ae61f 100644 --- a/src/boot_aarch64.c +++ b/src/boot_aarch64.c @@ -26,9 +26,16 @@ #include "printf.h" #include "wolfboot/wolfboot.h" -/* Include platform-specific header for EL configuration defines */ -#ifdef TARGET_versal +/* Include platform-specific header for EL configuration defines + * (EL2_HYPERVISOR, etc.). Must be visible here so the BOOT_EL1 / + * EL2_HYPERVISOR guards around the EL2->EL1 ERET transition below + * compile in for the active target. */ +#if defined(TARGET_versal) #include "hal/versal.h" +#elif defined(TARGET_zynq) +#include "hal/zynq.h" +#elif defined(TARGET_ls1028a) +#include "hal/nxp_ls1028a.h" #endif /* Linker exported variables */ @@ -43,6 +50,17 @@ extern unsigned int _end_data; extern void main(void); extern void gicv2_init_secure(void); +/* Asm helper in boot_aarch64_start.S: cleans the entire D-cache to PoC, + * invalidates the I-cache to PoU, and disables MMU + I-cache + D-cache + * via SCTLR_EL2, then returns. Required before handoff to any payload + * that sets up its own translation (Linux kernel, hypervisor, bare-metal + * RTOS, later bootloader stage), and mandatory for the ARM64 Linux boot + * protocol. Only built when EL2_HYPERVISOR == 1 is visible to + * boot_aarch64_start.S (e.g. via hal/zynq.h on ZynqMP). */ +#if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1 +extern void el2_flush_and_disable_mmu(void); +#endif + /* SKIP_GIC_INIT - Skip GIC initialization before booting app * This is needed for: * - Versal: Uses GICv3, not GICv2. BL31 handles GIC setup. @@ -163,7 +181,22 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset) el2_to_el1_boot((uintptr_t)app_offset, dts); } #else - /* Stay at current EL (EL2 or EL3) and jump directly to application */ + /* Stay at current EL (EL2 or EL3) and jump directly to application. + * + * Before the jump, tear down wolfBoot's EL2 MMU/caches so the next + * stage enters with a clean state. Mandatory for the ARM64 Linux + * boot protocol (Linux's arm64_panic_block_init() panics with + * "Non-EFI boot detected with MMU and caches enabled" otherwise), + * and correct for any payload that sets up its own translation + * (hypervisor, RTOS, later bootloader stage). */ +#if defined(MMU) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1 + if (current_el() == 2) { + wolfBoot_printf("do_boot: flushing caches, disabling MMU\n"); + el2_flush_and_disable_mmu(); + } +#endif + + /* Non-Linux EL2 and EL3 path: legacy direct br x4 */ /* Set application address via x4 */ asm volatile("mov x4, %0" : : "r"(app_offset)); diff --git a/src/boot_aarch64_start.S b/src/boot_aarch64_start.S index 03b6359e71..195c8ef03c 100644 --- a/src/boot_aarch64_start.S +++ b/src/boot_aarch64_start.S @@ -1334,4 +1334,118 @@ el2_to_el1_boot: b . #endif /* BOOT_EL1 && EL2_HYPERVISOR */ + +/* + * Clean entire D-cache to the Point of Coherency (PoC), invalidate the + * I-cache to the Point of Unification (PoU), and disable MMU + I/D-cache + * at EL2. Returns normally to the caller. + * + * Terminology (ARM ARM B2.8): + * PoC - Point of Coherency: the point at which all observers (CPUs, + * DMA masters, etc.) see the same memory. Cleaning to PoC + * guarantees the image bytes we memcpy'd are visible to the + * next stage's first uncached instruction fetches. + * PoU - Point of Unification: the point at which instruction and data + * caches converge. Invalidating I-cache to PoU ensures stale + * fetches are discarded before we hand off. + * + * wolfBoot's startup (line ~347 above) enables MMU+I+D cache at EL2 for + * its own use. Any payload we hand off to (Linux kernel, hypervisor, + * bare-metal RTOS, a later bootloader stage) expects to enter without + * inheriting wolfBoot's translation tables, and the ARM64 Linux boot + * protocol (Documentation/arch/arm64/booting.rst) explicitly REQUIRES + * MMU off, D-cache off, and the loaded image cleaned to PoC. This + * helper performs that teardown and returns; the caller then performs + * the actual jump with whatever ABI the payload expects. + * + * Safe to return because wolfBoot's .text is identity-mapped (VA=PA) + * at EL2, so instruction fetch keeps working after SCTLR_EL2.M is + * cleared. + * + * AAPCS64: clobbers x0-x11; x30 (LR) is preserved because the + * set/way loop body does not touch it. + */ +#if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1 +.global el2_flush_and_disable_mmu +el2_flush_and_disable_mmu: + /* ---- 1. Clean & invalidate entire data cache to PoC by set/way ---- + * Standard ARMv8 routine, adapted from arm-trusted-firmware / + * U-Boot / Linux. Iterates every (level, set, way) triple and + * issues `dc cisw` on it. Terminates at the Level of Coherency + * (LoC) read from CLIDR_EL1. */ + mrs x0, clidr_el1 + and x3, x0, #0x07000000 /* x3 = LoC (level of coherency) */ + lsr x3, x3, #23 /* x3 = LoC * 2 */ + cbz x3, .Ldcache_done + mov x10, #0 /* x10 = current cache level << 1 */ + +.Ldcache_level_loop: + add x2, x10, x10, lsr #1 /* x2 = level * 3 */ + lsr x1, x0, x2 /* x1 = ctype field for this level */ + and x1, x1, #7 + cmp x1, #2 + b.lt .Ldcache_skip_level /* No data cache at this level */ + msr csselr_el1, x10 /* Select cache level (instruction = 0) */ + isb + mrs x1, ccsidr_el1 + and x2, x1, #7 /* x2 = log2(line length) - 4 */ + add x2, x2, #4 /* x2 = log2(line length) */ + mov x4, #0x3ff + and x4, x4, x1, lsr #3 /* x4 = max way number */ + clz w5, w4 /* x5 = bit position of way size */ + mov x7, #0x7fff + and x7, x7, x1, lsr #13 /* x7 = max set number */ + +.Ldcache_set_loop: + mov x9, x4 /* x9 = current way */ +.Ldcache_way_loop: + lsl x6, x9, x5 + orr x11, x10, x6 /* level | way */ + lsl x6, x7, x2 + orr x11, x11, x6 /* level | way | set */ + dc cisw, x11 /* clean & invalidate by set/way */ + subs x9, x9, #1 + b.ge .Ldcache_way_loop + subs x7, x7, #1 + b.ge .Ldcache_set_loop + +.Ldcache_skip_level: + add x10, x10, #2 + cmp x3, x10 + b.gt .Ldcache_level_loop + +.Ldcache_done: + mov x10, #0 + msr csselr_el1, x10 + dsb sy + isb + + /* ---- 2. Invalidate entire I-cache to PoU ---- + * `ic iallu` invalidates all instruction cache to the Point of + * Unification for the local PE. */ + ic iallu + dsb ish + isb + + /* ---- 3. Disable MMU + I-cache + D-cache at EL2 ---- + * SCTLR_EL2.M (bit 0) = MMU enable + * SCTLR_EL2.C (bit 2) = D-cache enable + * SCTLR_EL2.I (bit 12) = I-cache enable + * + * ARM ARM (B2.7.2) requires `dsb sy` before `isb` when modifying + * SCTLR_ELx.M so the system register write is observable before the + * pipeline is re-synchronized. Matches the MMU-enable sequence used + * earlier in this file. + */ + mrs x0, SCTLR_EL2 + bic x0, x0, #(1 << 0) /* M */ + bic x0, x0, #(1 << 2) /* C */ + bic x0, x0, #(1 << 12) /* I */ + msr SCTLR_EL2, x0 + dsb sy + isb + + ret +#endif /* EL2_HYPERVISOR */ + .end diff --git a/src/sdhci.c b/src/sdhci.c index ba82947228..222ca5daf9 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -581,6 +581,7 @@ static uint32_t sdhci_get_response_bits(int from, int count) /* voltage: 0=off or SDHCI_SRS10_BVS_[X_X]V */ static int sdcard_power_init_seq(uint32_t voltage) { + int retries; /* Set power to specified voltage */ int status = sdhci_set_power(voltage); #ifdef DEBUG_SDHCI @@ -590,9 +591,24 @@ static int sdcard_power_init_seq(uint32_t voltage) SDHCI_REG(SDHCI_SRS09), SDHCI_REG(SDHCI_SRS10), SDHCI_REG(SDHCI_SRS11), SDHCI_REG(SDHCI_SRS12)); #endif - if (status == 0) { - /* send CMD0 (go idle) to reset card */ + if (status != 0) + return status; + /* SD spec requires >= 1ms after power stabilizes before CMD0. */ + udelay(1000); + /* Some cards and the ZynqMP Arasan controller need more settling + * time after the slot-type change + soft reset in sdhci_platform_init(). + * Use a retry loop: if CMD0 fails, wait and retry (self-calibrating). */ + for (retries = 0; retries < 10; retries++) { status = sdhci_cmd(MMC_CMD0_GO_IDLE, 0, SDHCI_RESP_NONE); + if (status == 0) + break; + udelay(10000); /* 10ms between retries */ + } + if (status != 0) { + wolfBoot_printf("SD: CMD0 failed after %d retries\n", retries); + } + else if (retries > 0) { + wolfBoot_printf("SD: CMD0 succeeded after %d retries\n", retries); } if (status == 0) { /* send the operating conditions command */ @@ -1387,6 +1403,11 @@ int sdhci_init(void) /* Call platform-specific initialization (clocks, resets, pin mux) */ sdhci_platform_init(); + /* Allow controller to settle after platform init (slot type change, + * soft reset, clock configuration). Without this, the controller may + * not be ready to accept register writes on some platforms. */ + udelay(1000); /* 1ms */ + /* Reset the host controller */ sdhci_reg_or(SDHCI_HRS00, SDHCI_HRS00_SWR); /* Bit will clear when reset is done */ @@ -1482,6 +1503,9 @@ int sdhci_init(void) /* Setup 400khz starting clock */ sdhci_set_clock(SDHCI_CLK_400KHZ); + /* Allow clock to stabilize before issuing first command */ + udelay(1000); /* 1ms */ + #ifdef DISK_EMMC /* Run full eMMC card initialization */ status = emmc_card_full_init(); From cc174cc8b09fbacc783b030557f7efdf8be178d8 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 17 Apr 2026 10:26:46 -0700 Subject: [PATCH 2/5] ZynqMP ZCU102 SD-card boot fixes --- config/examples/zynqmp_sdcard.config | 8 +++++- docs/Targets.md | 19 ++++++++++++++ include/sdhci.h | 11 ++++++-- src/sdhci.c | 39 +++++++++++++++++++++------- src/update_disk.c | 1 + 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/config/examples/zynqmp_sdcard.config b/config/examples/zynqmp_sdcard.config index 1f22c95acf..6a87512935 100644 --- a/config/examples/zynqmp_sdcard.config +++ b/config/examples/zynqmp_sdcard.config @@ -23,6 +23,9 @@ DEBUG_SYMBOLS=1 DEBUG_UART=1 CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1 +# Display boot timing data +#BOOT_BENCHMARK?=1 + # SD card support - use SDHCI driver DISK_SDCARD?=1 DISK_EMMC?=0 @@ -38,6 +41,7 @@ NO_XIP=1 # ELF loading support ELF?=1 +#DEBUG_ELF?=1 # Boot Exception Level: leave wolfBoot at EL2 for handoff to Linux (matches # the standard PetaLinux U-Boot flow and preserves KVM/hypervisor use of @@ -79,7 +83,9 @@ WOLFBOOT_NO_PARTITIONS=1 CFLAGS_EXTRA+=-DBOOT_PART_A=1 CFLAGS_EXTRA+=-DBOOT_PART_B=2 -# Disk read chunk size (512KB) +# Disk read chunk size for firmware loading (update_disk.c). 512KB gives the +# best throughput (~1.4s for 32MB). The SDMA engine handles boundary crossings +# every 4KB (SDHCI_DMA_THRESHOLD default) within each 512KB chunk. CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 # Linux rootfs is on partition 4. Device naming depends on whether both diff --git a/docs/Targets.md b/docs/Targets.md index f8d68a81f4..9e35491e51 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -2689,6 +2689,25 @@ Set the ZCU102 boot mode switches (SW6) for SD card boot: | QSPI32 | 0 0 1 0 | on, on, off, on | | SD1 | 1 1 1 0 | off, off, off, on | +**SDHCI Notes (Arasan controller)** + +The ZynqMP uses an Arasan SDHCI v3.0 controller. Key considerations: + +- **SDMA vs PIO**: The PIO (Programmed I/O) multi-block read path has a race condition on + this controller under compiler optimization (`-Os`/`-O2`). The BRR (Buffer Read Ready) + flag is re-polled too quickly between blocks, causing stale data reads that corrupt + firmware images. The default `SDHCI_DMA_THRESHOLD=4096` forces all multi-block reads + through the SDMA path, which avoids this issue entirely. +- **HV4E redirect**: The Arasan controller does not support Host Version 4 Enable (HV4E). + The platform HAL in `hal/zynq.c` transparently redirects SRS22/SRS23 writes to the + legacy SRS00 register for 32-bit SDMA addressing. +- **Card detect**: The Arasan controller does not support CDSS/CDTL card detect test + level. `SDHCI_FORCE_CARD_DETECT` is set in the config since FSBL already booted from + the same SD card. +- **`DISK_BLOCK_SIZE`**: Controls the firmware read chunk size in `update_disk.c` (default + 64KB). This determines the per-read size passed to the SDHCI driver. Must be less than + the SDMA buffer boundary (4KB with the default threshold). + **Debug** Enable SDHCI debug output by uncommenting in the config: diff --git a/include/sdhci.h b/include/sdhci.h index 65fccf48ef..1880361c03 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -40,9 +40,16 @@ #define SDHCI_BLOCK_SIZE 512 #endif -/* DMA threshold - minimum transfer size to use DMA mode (default: 512KB) */ +/* DMA threshold - minimum transfer size (bytes) to use SDMA instead of PIO. + * Default 4KB: forces SDMA for virtually all multi-block CMD18 reads. + * The PIO multi-block read path has a known race condition on Arasan SDHCI + * controllers (ZynqMP, Versal) where BRR (Buffer Read Ready) is re-checked + * too quickly between blocks under compiler optimization (-Os/-O2), causing + * stale data reads and firmware integrity failures. Using SDMA avoids the + * BRR polling loop entirely. + * Override in target .config if a larger PIO window is acceptable. */ #ifndef SDHCI_DMA_THRESHOLD -#define SDHCI_DMA_THRESHOLD (512U * 1024U) +#define SDHCI_DMA_THRESHOLD (4U * 1024U) #endif /* Disk test block address (platform should override) */ diff --git a/src/sdhci.c b/src/sdhci.c index 222ca5daf9..f54161d027 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -1289,7 +1289,22 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr, #endif /* !SDHCI_SDMA_DISABLED */ } else { - /* Blocking mode - buffer ready flag differs for read vs write */ + /* PIO (Programmed I/O) mode — reads/writes data word-by-word via + * the SRS08 data port register. + * + * CAUTION: On Arasan SDHCI v3.0 (ZynqMP, Versal), multi-block PIO + * reads (CMD18) have a known race condition under compiler + * optimization (-Os/-O2). After reading one block, the BRR (Buffer + * Read Ready) flag in SRS12 may still be set from the previous + * block when the outer loop re-checks it. The optimized code + * re-polls so quickly that BRR has not yet auto-cleared, causing + * the next 512-byte read from SRS08 to return stale/partial data. + * This corrupts the loaded firmware image. + * + * Workaround: Set SDHCI_DMA_THRESHOLD low (default 4KB) so that + * multi-block reads use SDMA instead of this PIO path. The eMMC + * path manually clears BRR between blocks (W1C write below), + * which also avoids the race. */ uint32_t buf_ready_flag = (dir == SDHCI_DIR_READ) ? SDHCI_SRS12_BRR : SDHCI_SRS12_BWR; @@ -1531,15 +1546,21 @@ int sdhci_init(void) } #ifdef DEBUG_SDHCI - { - const char *card_type; -#ifdef DISK_EMMC - card_type = "eMMC"; -#else - card_type = "SD"; -#endif - wolfBoot_printf("sdhci_init: %s status: %d\n", card_type, status); + if (status == 0) { + wolfBoot_printf("SDHCI: DMA (threshold: %dKB, buf boundary: %dKB)\n", + SDHCI_DMA_THRESHOLD / 1024, + (4 << ((SDHCI_DMA_BUFF_BOUNDARY >> 12) & 0x7)) + ); } + + wolfBoot_printf("SDHCI: %s init, status %d\n", + #ifdef DISK_EMMC + "eMMC" + #else + "SD" + #endif + , status + ); #endif return status; diff --git a/src/update_disk.c b/src/update_disk.c index 088dd328e3..4254b17118 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -353,6 +353,7 @@ void RAMFUNCTION wolfBoot_start(void) pB_ver_u = (uint32_t)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); max_ver = (pB_ver_u > pA_ver_u) ? pB_ver_u : pA_ver_u; /* Choose partition with higher version */ From ee943dd8889cd604965ad98334b7d25dd777efb5 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 23 Apr 2026 10:40:26 -0700 Subject: [PATCH 3/5] Removed the redundant "FDT: Setting bootargs" print --- hal/versal.c | 1 - hal/zynq.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hal/versal.c b/hal/versal.c index baf8aadd7a..a99729c7e6 100644 --- a/hal/versal.c +++ b/hal/versal.c @@ -1284,7 +1284,6 @@ int hal_dts_fixup(void* dts_addr) if (off >= 0) { /* Set bootargs property */ - wolfBoot_printf("FDT: Setting bootargs: %s\n", LINUX_BOOTARGS); fdt_fixup_str(fdt, off, "chosen", "bootargs", LINUX_BOOTARGS); } else { wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off); diff --git a/hal/zynq.c b/hal/zynq.c index 1462e220d3..8fa9bc750c 100644 --- a/hal/zynq.c +++ b/hal/zynq.c @@ -1869,7 +1869,6 @@ int hal_dts_fixup(void* dts_addr) /* Set bootargs property - overrides PetaLinux default root= with * the wolfBoot partition layout. */ - wolfBoot_printf("FDT: Setting bootargs: %s\n", LINUX_BOOTARGS); ret = fdt_fixup_str(fdt, off, "chosen", "bootargs", LINUX_BOOTARGS); if (ret < 0) { wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret); From 80012033980531d987833c1488d5b8e31e4528f0 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Apr 2026 10:30:55 -0700 Subject: [PATCH 4/5] Peer review fixes + ZynqMP robustness improvements hal/zynq.c: - Route IOU_TAPDLY_BYPASS writes through pmu_request at EL<=2 in the <=40 MHz and <=100 MHz branches (previously only done at <=150 MHz); the register is equally unwritable from EL2/EL1 at lower clocks. - Add qspi_flash_reset() (RESET_ENABLE 0x66 + RESET_MEMORY 0x99), called per chip in qspi_init so the flash starts from a known state regardless of what FSBL/BootROM left behind (XIP, 4-byte addr, auto-boot). - Drop unused 'reg' in csu_aes and 'ms' in csu_init so -Werror=unused-variable builds (OPTIMIZATION_LEVEL=0 / DEBUG=1) pass. hal/zynq.ld: - Move wolfBoot ORIGIN from 0x08000000 to 0x10000000. Large FIT images (kernel load=0x00200000, payload >~126 MB) would sweep across 0x08000000 at handoff and overwrite wolfBoot's own code. tools/scripts/zcu102/zcu102-ca53-qspi.cmm: - Rewrite against the Lauterbach TRACE32 ZCU102 QSPI demo: PREPAREONLY entry mode, single/dual toggle, READ_ID_TEST, separate flash dialogs for BOOT.BIN (offset 0) and test-app/image_v1_signed.bin. - Document the ~128 MB TRACE32 temp-memory ceiling on FLASHFILE.Create: larger files must be split externally and loaded in chunks. --- config/examples/zynqmp_sdcard.config | 12 +- docs/Targets.md | 9 +- hal/versal.c | 13 +- hal/zynq.c | 94 +++- hal/zynq.ld | 17 +- include/sdhci.h | 9 +- src/boot_aarch64.c | 2 +- src/boot_aarch64_start.S | 2 +- tools/scripts/nxp_t1040/t1040_debug.cmm | 6 +- tools/scripts/nxp_t1040/t1040_flash.cmm | 14 +- tools/scripts/zcu102/zcu102-ca53-qspi.cmm | 466 +++++++++--------- .../scripts/zcu102/zcu102-debug-wolfboot.cmm | 3 +- 12 files changed, 389 insertions(+), 258 deletions(-) mode change 100755 => 100644 tools/scripts/zcu102/zcu102-ca53-qspi.cmm diff --git a/config/examples/zynqmp_sdcard.config b/config/examples/zynqmp_sdcard.config index 6a87512935..8573dd1edd 100644 --- a/config/examples/zynqmp_sdcard.config +++ b/config/examples/zynqmp_sdcard.config @@ -84,8 +84,12 @@ CFLAGS_EXTRA+=-DBOOT_PART_A=1 CFLAGS_EXTRA+=-DBOOT_PART_B=2 # Disk read chunk size for firmware loading (update_disk.c). 512KB gives the -# best throughput (~1.4s for 32MB). The SDMA engine handles boundary crossings -# every 4KB (SDHCI_DMA_THRESHOLD default) within each 512KB chunk. +# best throughput (~1.4s for 32MB). The SDMA engine handles SDMA buffer +# boundary crossings within each 512KB chunk; this boundary is 4KB by default +# (auto-derived from SDHCI_DMA_THRESHOLD). To reduce boundary IRQs, override +# SDHCI_DMA_BUFF_BOUNDARY independently using the raw register value so the +# override is safe to use in preprocessor #if expressions, e.g.: +# CFLAGS_EXTRA+=-DSDHCI_DMA_BUFF_BOUNDARY=0x7000 # 512KB (SDHCI_SRS01_DMA_BUFF_512KB) CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 # Linux rootfs is on partition 4. Device naming depends on whether both @@ -99,8 +103,8 @@ CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\" # ============================================================================ # Boot Memory Layout # ============================================================================ -# wolfBoot runs from DDR at 0x8000000 (same as U-Boot, loaded via BL31) -WOLFBOOT_ORIGIN=0x8000000 +# wolfBoot runs from DDR at 0x10000000 (loaded by BL31; see hal/zynq.ld). +WOLFBOOT_ORIGIN=0x10000000 # Load Partition to RAM Address (Linux kernel loads here) WOLFBOOT_LOAD_ADDRESS?=0x10000000 diff --git a/docs/Targets.md b/docs/Targets.md index 9e35491e51..0f193afe7d 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -2599,7 +2599,7 @@ qemu-system-aarch64 -machine xlnx-zcu102 -cpu cortex-a53 -serial stdio -display Use `config/examples/zynqmp_sdcard.config`. This uses the Arasan SDHCI controller (SD1 - external SD card slot on ZCU102) and an **MBR** partitioned SD card. -wolfBoot unconditionally flushes the EL2 D-cache/I-cache and disables the EL2 MMU before handoff (see `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`), satisfying the ARM64 Linux boot protocol with no extra config flag required. +On the direct-jump handoff path, wolfBoot flushes the EL2 D-cache/I-cache and disables the EL2 MMU via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S` when `BOOT_EL1` is not enabled and the current exception level is EL2. The ERET-to-EL1 handoff path is different, so this cleanup is not unconditional. **Partition layout** | Partition | Name | Size | Type | Contents | @@ -2705,8 +2705,11 @@ The ZynqMP uses an Arasan SDHCI v3.0 controller. Key considerations: level. `SDHCI_FORCE_CARD_DETECT` is set in the config since FSBL already booted from the same SD card. - **`DISK_BLOCK_SIZE`**: Controls the firmware read chunk size in `update_disk.c` (default - 64KB). This determines the per-read size passed to the SDHCI driver. Must be less than - the SDMA buffer boundary (4KB with the default threshold). + 64KB). This determines the per-read size passed to the SDHCI driver. It does not need + to be smaller than `SDHCI_DMA_BUFF_BOUNDARY`; if a read crosses one or more SDMA buffer + boundaries, the SDHCI driver handles that via the normal SDMA boundary interrupt path. + In practice, this setting is a tradeoff: larger reads may trigger boundary IRQs more + often, while smaller reads reduce crossings but increase request overhead. **Debug** diff --git a/hal/versal.c b/hal/versal.c index a99729c7e6..46d64d46fc 100644 --- a/hal/versal.c +++ b/hal/versal.c @@ -66,7 +66,11 @@ /* Linux kernel command line arguments */ #ifndef LINUX_BOOTARGS #ifndef LINUX_BOOTARGS_ROOT -#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4" +/* Default Versal SD layout: rootfs on partition 2. Configurations that use + * the 4-partition MBR layout with OFP_A/OFP_B slots + * (boot / OFP_A / OFP_B / rootfs, e.g. config/examples/zynqmp_sdcard.config) + * should override LINUX_BOOTARGS_ROOT to "/dev/mmcblk0p4". */ +#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p2" #endif #define LINUX_BOOTARGS \ @@ -1275,10 +1279,11 @@ int hal_dts_fixup(void* dts_addr) /* Expand total size to allow adding/modifying properties */ fdt_set_totalsize(fdt, fdt_totalsize(fdt) + 512); - /* Find /chosen node */ + /* Find /chosen node; create it only if genuinely missing. Any other + * negative return (malformed FDT, etc.) is surfaced directly rather + * than masked by a follow-on fdt_add_subnode() failure. */ off = fdt_find_node_offset(fdt, -1, "chosen"); - if (off < 0) { - /* Create /chosen node if it doesn't exist */ + if (off == -FDT_ERR_NOTFOUND) { off = fdt_add_subnode(fdt, 0, "chosen"); } diff --git a/hal/zynq.c b/hal/zynq.c index 8fa9bc750c..2105d7fa5a 100644 --- a/hal/zynq.c +++ b/hal/zynq.c @@ -504,7 +504,6 @@ static int csu_dma_config(int ch, int doSwap) int csu_aes(int enc, const uint8_t* iv, const uint8_t* in, uint8_t* out, uint32_t sz) { int ret; - uint32_t reg; /* Flush data cache for variables used */ flush_dcache_range((unsigned long)iv, (unsigned long)iv + AES_GCM_TAG_SZ); @@ -601,7 +600,6 @@ int csu_init(void) #endif uint32_t reg1 = pmu_mmio_read(CSU_IDCODE); uint32_t reg2 = pmu_mmio_read(CSU_VERSION); - uint64_t ms; wolfBoot_printf("CSU ID 0x%08x, Ver 0x%08x\n", reg1, reg2 & CSU_VERSION_MASK); @@ -1361,6 +1359,39 @@ static int qspi_exit_4byte_addr(QspiDev_t* dev) } #endif +/* Soft-reset the flash to a known idle state. + * FSBL / BootROM may leave the flash in an unexpected mode (XIP enabled, + * 4-byte addr set, auto-boot probing, etc.). Issue RESET_ENABLE (0x66) + + * RESET_MEMORY (0x99) to bring it back to defaults before first transaction. + * Per Micron MT25Q datasheet: t_SHSL2 ~ 40 us max after RESET_MEMORY. */ +static int qspi_flash_reset(QspiDev_t* dev) +{ + int ret; + uint8_t cmd[4]; /* size multiple of uint32_t */ + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = RESET_ENABLE_CMD; + /* Reset commands are always issued in single-SPI mode regardless of + * dev->mode: the flash's current bus mode is unknown at reset time, and + * the single-SPI opcode is the universal-compatible form. */ + ret = qspi_transfer(dev, cmd, 1, NULL, 0, NULL, 0, 0, + GQSPI_GEN_FIFO_MODE_SPI); +#if defined(DEBUG_ZYNQ) && DEBUG_ZYNQ >= 2 + wolfBoot_printf("Flash Reset Enable: Ret %d\n", ret); +#endif + if (ret == GQSPI_CODE_SUCCESS) { + cmd[0] = RESET_MEMORY_CMD; + ret = qspi_transfer(dev, cmd, 1, NULL, 0, NULL, 0, 0, + GQSPI_GEN_FIFO_MODE_SPI); + #if defined(DEBUG_ZYNQ) && DEBUG_ZYNQ >= 2 + wolfBoot_printf("Flash Reset Memory: Ret %d\n", ret); + #endif + } + /* Allow flash time to complete the reset and become ready. */ + hal_delay_ms(1); + return ret; +} + /* QSPI functions */ void qspi_init(void) { @@ -1444,13 +1475,29 @@ void qspi_init(void) #if (GQSPI_CLK_REF / (2 << GQSPI_CLK_DIV)) <= 40000000 /* 40MHz */ /* At <40 MHz, the Quad-SPI controller should be in non-loopback mode with * the clock and data tap delays bypassed. */ - IOU_TAPDLY_BYPASS |= IOU_TAPDLY_BYPASS_LQSPI_RX; + /* IOU_TAPDLY_BYPASS is not writable from EL2/EL1 without going through PMU. */ + if (current_el() <= 2) { + pmu_request(PM_MMIO_WRITE, IOU_TAPDLY_BYPASS_ADDR, + IOU_TAPDLY_BYPASS_LQSPI_RX, IOU_TAPDLY_BYPASS_LQSPI_RX, + 0, NULL); + } + else { + IOU_TAPDLY_BYPASS |= IOU_TAPDLY_BYPASS_LQSPI_RX; + } GQSPI_LPBK_DLY_ADJ = 0; GQSPI_DATA_DLY_ADJ = 0; #elif (GQSPI_CLK_REF / (2 << GQSPI_CLK_DIV)) <= 100000000 /* 100MHz */ /* At <100 MHz, the Quad-SPI controller should be in clock loopback mode * with the clock tap delay bypassed, but the data tap delay enabled. */ - IOU_TAPDLY_BYPASS |= IOU_TAPDLY_BYPASS_LQSPI_RX; + /* IOU_TAPDLY_BYPASS is not writable from EL2/EL1 without going through PMU. */ + if (current_el() <= 2) { + pmu_request(PM_MMIO_WRITE, IOU_TAPDLY_BYPASS_ADDR, + IOU_TAPDLY_BYPASS_LQSPI_RX, IOU_TAPDLY_BYPASS_LQSPI_RX, + 0, NULL); + } + else { + IOU_TAPDLY_BYPASS |= IOU_TAPDLY_BYPASS_LQSPI_RX; + } GQSPI_LPBK_DLY_ADJ = GQSPI_LPBK_DLY_ADJ_USE_LPBK; GQSPI_DATA_DLY_ADJ = (GQSPI_DATA_DLY_ADJ_USE_DATA_DLY | GQSPI_DATA_DLY_ADJ_DATA_DLY_ADJ(2)); @@ -1485,6 +1532,19 @@ void qspi_init(void) (void)reg_cfg; (void)reg_isr; + /* Issue flash soft reset so we start from a known state regardless of + * whatever mode FSBL/BootROM left the device in. Send to each chip in + * dual-parallel configurations by targeting both chip selects. */ + mDev.mode = GQSPI_GEN_FIFO_MODE_SPI; + mDev.bus = GQSPI_GEN_FIFO_BUS_LOW; + mDev.cs = GQSPI_GEN_FIFO_CS_LOWER; + (void)qspi_flash_reset(&mDev); +#if GQPI_USE_DUAL_PARALLEL == 1 + mDev.bus = GQSPI_GEN_FIFO_BUS_UP; + mDev.cs = GQSPI_GEN_FIFO_CS_UPPER; + (void)qspi_flash_reset(&mDev); +#endif + /* ------ Flash Read ID (retry) ------ */ timeout = 0; while (++timeout < QSPI_FLASH_READY_TRIES) { @@ -1577,6 +1637,10 @@ void hal_init(void) wolfBoot_printf(bootMsg); wolfBoot_printf("Current EL: %d\n", current_el()); +#ifndef WOLFBOOT_REPRODUCIBLE_BUILD + wolfBoot_printf("Build: %s %s\n", __DATE__, __TIME__); +#endif + #if defined(EXT_FLASH) && (EXT_FLASH == 1) qspi_init(); #endif @@ -1809,15 +1873,28 @@ void RAMFUNCTION ext_flash_unlock(void) } +/* The following helpers (hal_get_timer_us, hal_get_dts_address, hal_dts_fixup) + * are only compiled into the wolfBoot binary. The test-app build also links + * hal/zynq.o but must not pull in FDT/MMU-specific code, so __WOLFBOOT gates + * these symbols out of that build. */ #if defined(MMU) && defined(__WOLFBOOT) +/* Fallback timer frequency if CNTFRQ_EL0 is not configured (e.g. boot path + * that did not run ATF/BL31). ZynqMP system counter is 100 MHz. */ +#ifndef ZYNQMP_TIMER_CLK_FREQ +#define ZYNQMP_TIMER_CLK_FREQ 100000000ULL +#endif + /* Get current time in microseconds using ARMv8 generic timer */ 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)); + /* Fall back to a known frequency rather than returning 0, so udelay() + * callers that spin on hal_get_timer_us() advancing remain monotonic + * (matches hal/versal.c). */ if (freq == 0) - return 0; + freq = ZYNQMP_TIMER_CLK_FREQ; /* Use __uint128_t to avoid overflow of (count * 1e6) at long uptimes * (would overflow uint64_t after ~51h at 100MHz). */ return (uint64_t)(((__uint128_t)count * 1000000ULL) / freq); @@ -1856,10 +1933,11 @@ int hal_dts_fixup(void* dts_addr) * the pattern used in hal/versal.c:hal_dts_fixup. */ fdt_set_totalsize(fdt, fdt_totalsize(fdt) + 512); - /* Find /chosen node */ + /* Find /chosen node; create it only if genuinely missing. Any other + * negative return (malformed FDT, etc.) is surfaced directly rather + * than masked by a follow-on fdt_add_subnode() failure. */ off = fdt_find_node_offset(fdt, -1, "chosen"); - if (off < 0) { - /* Create /chosen node if it doesn't exist */ + if (off == -FDT_ERR_NOTFOUND) { off = fdt_add_subnode(fdt, 0, "chosen"); } if (off < 0) { diff --git a/hal/zynq.ld b/hal/zynq.ld index 230b76455c..f710c40836 100644 --- a/hal/zynq.ld +++ b/hal/zynq.ld @@ -13,10 +13,21 @@ MEMORY { /* psu_ddr_0_MEM_0 : ORIGIN = 0x0, LENGTH = 0x80000000 */ /* wolfBoot DDR location (2MB reserved): - * Loaded by FSBL/BL31 to DDR at 0x8000000 (128MB) - * Same address used for both QSPI and SD card boot + * Loaded by FSBL/BL31 to DDR at 0x10000000 (256MB). Must be above + * the OS kernel load region: for FIT images whose "load" address is + * 0x00200000 (typical for AArch64 kernels) and whose payload + * approaches or exceeds ~126MB, the kernel memcpy into 0x00200000.. + * sweeps across 0x08000000 and would clobber wolfBoot if linked + * there. Same address used for QSPI and SD card boot. + * + * WOLFBOOT_LOAD_ADDRESS (signed-image staging base) also defaults to + * 0x10000000 on the SD-card config. The overlap with wolfBoot's own + * 2MB text region is safe because wolfBoot completes the verify and + * jump sequence from I-cache; the I-cache is only invalidated inside + * el2_flush_and_disable_mmu, immediately before control transfers to + * the loaded kernel (which no longer needs wolfBoot's .text). */ - psu_ddr_0_MEM_0 : ORIGIN = 0x8000000, LENGTH = 0x200000 + psu_ddr_0_MEM_0 : ORIGIN = 0x10000000, LENGTH = 0x200000 psu_ddr_1_MEM_0 : ORIGIN = 0x800000000, LENGTH = 0x80000000 psu_ocm_ram_0_MEM_0 : ORIGIN = 0xFFFC0000, LENGTH = 0x40000 psu_qspi_linear_0_MEM_0 : ORIGIN = 0xC0000000, LENGTH = 0x20000000 diff --git a/include/sdhci.h b/include/sdhci.h index 1880361c03..35415026cc 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -57,7 +57,13 @@ #define DISK_TEST_BLOCK_ADDR 149504 /* ~76MB offset */ #endif -/* Auto-select DMA buffer boundary based on threshold */ +/* DMA buffer boundary: how often the SDMA engine pauses to refresh its + * address pointer (handled by sdhci_irq_handler() via SDHCI_SRS12_DMAINT). + * This is a throughput knob and is independent of SDHCI_DMA_THRESHOLD + * (which controls when to switch from PIO to SDMA). Override in target + * .config to match the largest expected single transfer for fewer + * boundary IRQs; otherwise auto-select based on the threshold. */ +#ifndef SDHCI_DMA_BUFF_BOUNDARY #if (SDHCI_DMA_THRESHOLD > (256U * 1024U)) #define SDHCI_DMA_BUFF_BOUNDARY SDHCI_SRS01_DMA_BUFF_512KB #if (SDHCI_DMA_THRESHOLD != (512U * 1024U)) @@ -99,6 +105,7 @@ #warning "SDHCI_DMA_THRESHOLD rounded up to 4KB (minimum)" #endif #endif +#endif /* !SDHCI_DMA_BUFF_BOUNDARY */ /* Timeouts */ #ifndef SDHCI_INIT_TIMEOUT_US diff --git a/src/boot_aarch64.c b/src/boot_aarch64.c index 495a5ae61f..cf8d074796 100644 --- a/src/boot_aarch64.c +++ b/src/boot_aarch64.c @@ -34,7 +34,7 @@ #include "hal/versal.h" #elif defined(TARGET_zynq) #include "hal/zynq.h" -#elif defined(TARGET_ls1028a) +#elif defined(TARGET_nxp_ls1028a) #include "hal/nxp_ls1028a.h" #endif diff --git a/src/boot_aarch64_start.S b/src/boot_aarch64_start.S index 195c8ef03c..544b5e8de9 100644 --- a/src/boot_aarch64_start.S +++ b/src/boot_aarch64_start.S @@ -1362,7 +1362,7 @@ el2_to_el1_boot: * at EL2, so instruction fetch keeps working after SCTLR_EL2.M is * cleared. * - * AAPCS64: clobbers x0-x11; x30 (LR) is preserved because the + * AAPCS64: clobbers x0-x7, x9-x11; x30 (LR) is preserved because the * set/way loop body does not touch it. */ #if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1 diff --git a/tools/scripts/nxp_t1040/t1040_debug.cmm b/tools/scripts/nxp_t1040/t1040_debug.cmm index 4ed918006c..161025835e 100644 --- a/tools/scripts/nxp_t1040/t1040_debug.cmm +++ b/tools/scripts/nxp_t1040/t1040_debug.cmm @@ -18,8 +18,10 @@ ; 3. wolfBoot runs from DDR (0x7FF00000) ; ------------------------------------------------------------------------------ -; Base directory for wolfBoot build output (adjust to match your build path) -&basedir="/home/davidgarske/GitHub/wolfboot-alt" +; Base directory for wolfBoot build output. "." means the TRACE32 current +; working directory - set this to your wolfBoot checkout path if running +; TRACE32 from elsewhere (e.g. "C:/src/wolfBoot" or "/home/user/wolfBoot"). +&basedir="." PRINT "========================================" PRINT "T1040 wolfBoot Debug Session" diff --git a/tools/scripts/nxp_t1040/t1040_flash.cmm b/tools/scripts/nxp_t1040/t1040_flash.cmm index e7163fb345..cac80d7e3f 100644 --- a/tools/scripts/nxp_t1040/t1040_flash.cmm +++ b/tools/scripts/nxp_t1040/t1040_flash.cmm @@ -25,11 +25,15 @@ ; 0xEFFFC000: Stage 1 loader (16 KB, includes reset vector) ; ------------------------------------------------------------------------------ -; Base directory for wolfBoot build output (adjust to match your build path) -&basedir="/home/davidgarske/GitHub/wolfboot-alt" - -; Persistent backup directory (survives make clean) -&backupdir="/home/davidgarske/Projects/NXP/t1040rdb" +; Base directory for wolfBoot build output. "." means the TRACE32 current +; working directory - set this to your wolfBoot checkout path if running +; TRACE32 from elsewhere (e.g. "C:/src/wolfBoot" or "/home/user/wolfBoot"). +&basedir="." + +; Persistent backup directory (survives make clean). Leave as "." to keep +; artifacts alongside the build tree, or point elsewhere to preserve +; signed/backup images across source cleans. +&backupdir="." ; FLASH Number of banks ; The JS28F00AM29EWHA is a single 128MB NOR chip. CPLD virtual banking diff --git a/tools/scripts/zcu102/zcu102-ca53-qspi.cmm b/tools/scripts/zcu102/zcu102-ca53-qspi.cmm old mode 100755 new mode 100644 index cb19d2fc5c..e6049d7f73 --- a/tools/scripts/zcu102/zcu102-ca53-qspi.cmm +++ b/tools/scripts/zcu102/zcu102-ca53-qspi.cmm @@ -1,225 +1,241 @@ -; Zynq UltraScale+ ZCU102 Quad SPI FLASH Programming Script -; -; S(D)RAM : 0xFFFC1000 -; Generic Quad-SPI Controller base: 0xFF0F0000 -; FLASH: MT25QU512 (Micron) -; -; Based on Lauterbach TRACE32 ZCU102 QSPI demo scripts. - -LOCAL &arg1 -ENTRY &arg1 -&arg1=STRing.UPpeR("&arg1") // for example "PREPAREONLY" -&dualqspi=1 ; dual(1) or single(0) - -; Adjust to your TRACE32 installation path -LOCAL &pdd -&pdd="C:/T32/demo/arm" - -SYStem.RESet -SYStem.CPU ZYNQ-ULTRASCALE+-APU -SYStem.MemAccess DAP -CORE.ASSIGN 1. -ETM.OFF -Trace.DISable - -; -; this sequence forces the SoC to use BOOTMODE=JTAG -; a SRST is issued using the debug logic -; we use the first A53 for flash programming -SYStem.Option ResBreak OFF -SYStem.Option EnReset OFF -SYStem.Option TRST OFF -DO &pdd/hardware/zynq_ultrascale/scripts/zynq-ultrascale_reset.cmm OVERRIDE_BOOTMODE=0x0 -SYStem.Mode Prepare -DO &pdd/hardware/zynq_ultrascale/scripts/zynq-ultrascale_kick_bootcore.cmm "A53_X64" -SYStem.Mode.Attach -Break.direct -; - -SYStem.JtagClock CTCK 24MHz - -; -------------------------------------------------------------------------------- -; peripheral initializations - -//Pin muxing MIO configuration MIO[0:12] - -Data.Set A:0xFF5E0068 %LE %Long 0x01010c00 ;QSPI_REF_CTRL - -&addr=0xFF180000 -RePeaT 13. -( - Data.Set A:&addr %LE %Long 0x2 - &addr=&addr+0x4 -) - -Data.Set A:0xFF180204 %LE %Long 0x2240000 - -Data.Set A:0xFF5E0238 %Long 0x17FFFE - -IF &dualqspi==1 - GOSUB READ_ID_TEST_DUAL -ELSE - GOSUB READ_ID_TEST - -Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL -Data.Set A:0xFF0F010C %Long 0x0FBE ; Interrupt disable register -Data.Set A:0xFF0F0128 %Long 0x01 ; thres hold -Data.Set A:0xFF0F012C %Long 0x01 ; thres hold -Data.Set A:0xFF0F0104 %Long 0x0FBE ; Interrupt status register - -Data.Set A:0xFF0F014C %Long 0x7 ; reset tx fifo and gen_fifo.. -WAIT 100.ms -Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode - -;The flash dualport works only under the non-secure mode for the ZYNQ-ULTRASCALE+ -SYStem.MemAccess DAP -Register.Set NS 1. -Register.Set M 0x5 ;EL1h - -; -------------------------------------------------------------------------------- -; Flash Declaration - -Break.RESet - -FLASHFILE.RESet -FLASHFILE.CONFIG 0xFF0F0000 - -IF &dualqspi==1 - FLASHFILE.TARGET 0xFFFC1000++0x2FFF E:0xFFFC4000++0x27FF &pdd/flash/word/spiw4b64_zynqultra.bin /KEEP /STACKSIZE 0x400 /DUALPORT -ELSE - FLASHFILE.TARGET 0xFFFC1000++0x2FFF E:0xFFFC4000++0x27FF &pdd/flash/byte/spi4b64fs_zynqultra.bin /KEEP /STACKSIZE 0x400 /DUALPORT - -FLASHFILE.GETID - -//End of the test prepareonly -IF "&arg1"=="PREPAREONLY" - ENDDO - -; Save Whole Flash -;FLASHFILE.SAVE "flash_dump.bin" 0x0++0x1FFFFFFF - -FLASHFILE.Create 0x0--0x1FFFFFFF 0x20000 Byte - -; Flash BOOT.BIN (wolfBoot) at offset 0x0 -DIALOG.YESNO "Flash wolfBoot BOOT.BIN now?" -ENTRY &programnow -if &programnow -( - FLASHFILE.ReProgram ALL - FLASHFILE.Load "BOOT.BIN" 0x0 - FLASHFILE.ReProgram off -) - -; Flash signed application image at partition offset -DIALOG.YESNO "Flash signed application image now?" -ENTRY &programnow -if &programnow -( - FLASHFILE.ReProgram ALL - FLASHFILE.Load "test-app/image_v1_signed.bin" 0x7000000 - FLASHFILE.ReProgram off -) - -ENDDO - -; -------------------------------------------------------------------------------- -; Subroutines - -READ_ID_TEST: -( - Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL - Data.Set A:0xFF0F010C %Long 0x0FBE ;Interrupt disable register - Data.Set A:0xFF0F0128 %Long 0x01 ;thres hold - Data.Set A:0xFF0F012C %Long 0x01 ;thres hold - Data.Set A:0xFF0F0104 %Long 0x0FBE ;Interrupt status register - - ;read A:0xFF0F0100 -> 0x0 - - Data.Set A:0xFF0f014c %Long 0x7 ; reset tx fifo and gen_fifo.. - WAIT 100.ms - - Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode - - &dat_xfer=0x1<<8. - &spimode=0x1<<10. - &cs_lower=0x1<<12. - &cs_upper=0x1<<13. - &bus_lower=0x1<<14. - &bus_upper=0x2<<14. - &bus_both=0x3<<14. - &transmit=0x1<<16. - &receive=0x1<<17. - &stripe=0x1<<18. - - Data.Set A:0xFF0F0114 %Long 0x1 ;spi enable register , cs low - - Data.Set A:0xFF0F011C %Long 0x9f ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 - PRINT "read 0x" Data.Long(A:0xff0f0120) " (dummy)" - - Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 - PRINT "read 0x" Data.Long(A:0xff0f0120) " (manufacture id)" - - Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 - PRINT "read 0x" Data.Long(A:0xff0f0120) " (device id)" - - Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 - PRINT "read 0x" Data.Long(A:0xff0f0120) - - Data.Set A:0xFF0F0114 %Long 0x0 ;spi disable, cs high - - RETURN -) - - -READ_ID_TEST_DUAL: -( - Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL - Data.Set A:0xFF0F010C %Long 0x0FBE ;Interrupt disable register - Data.Set A:0xFF0F0128 %Long 0x01 ;thres hold - Data.Set A:0xFF0F012C %Long 0x01 ;thres hold - Data.Set A:0xFF0F0104 %Long 0x0FBE ;Interrupt status register - - ;read A:0xFF0F0100 -> 0x0 - - Data.Set A:0xFF0f014c %Long 0x7 ; reset tx fifo and gen_fifo.. - WAIT 100.ms - - Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode - - &dat_xfer=0x1<<8. - &spimode=0x1<<10. - &cs_lower=0x1<<12. - &cs_upper=0x1<<13. - &bus_lower=0x1<<14. - &bus_upper=0x2<<14. - &bus_both=0x3<<14. - &transmit=0x1<<16. - &receive=0x1<<17. - &stripe=0x1<<18. - - Data.Set A:0xFF0F0114 %Long 0x1 ;spi enable register , cs low - - Data.Set A:0xFF0F011C %Long 0x00009f9f ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer - PRINT "read 0x" Data.Long(A:0xff0f0120) - - Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer - PRINT "read 0x" Data.Long(A:0xff0f0120) - - Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer - PRINT "read 0x" Data.Long(A:0xff0f0120) - - Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer - Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer - PRINT "read 0x" Data.Long(A:0xff0f0120) - - Data.Set A:0xFF0F0114 %Long 0x0 ;spi disable, cs high - - RETURN -) +; Zynq UltraScale+ ZCU102 Quad SPI FLASH Programming Script +; +; S(D)RAM : 0xFFFC1000 +; Generic Quad-SPI Controller base: 0xFF0F0000 +; FLASH: MT25QU512 (Micron) +; +; Based on Lauterbach TRACE32 ZCU102 QSPI demo scripts. + +LOCAL &arg1 +ENTRY &arg1 +&arg1=STRing.UPpeR("&arg1") // for example "PREPAREONLY" +&dualqspi=1 ; dual(1) or single(0) + +; TRACE32 demo scripts directory. +; "~~" is the TRACE32 installation path - expands to the correct location +; on Windows (e.g. C:/T32), Linux (e.g. /opt/t32), and macOS automatically, +; so no per-platform edit is needed. Override only if the demo scripts live +; somewhere non-standard: +; &pdd="C:/T32/demo/arm" ; Windows default install +; &pdd="/opt/t32/demo/arm" ; Linux default install +LOCAL &pdd +&pdd="~~/demo/arm" + +SYStem.RESet +SYStem.CPU ZYNQ-ULTRASCALE+-APU +SYStem.MemAccess DAP +CORE.ASSIGN 1. +ETM.OFF +Trace.DISable + +; +; this sequence forces the SoC to use BOOTMODE=JTAG +; a SRST is issued using the debug logic +; we use the first A53 for flash programming +SYStem.Option ResBreak OFF +SYStem.Option EnReset OFF +SYStem.Option TRST OFF +DO &pdd/hardware/zynq_ultrascale/scripts/zynq-ultrascale_reset.cmm OVERRIDE_BOOTMODE=0x0 +SYStem.Mode Prepare +DO &pdd/hardware/zynq_ultrascale/scripts/zynq-ultrascale_kick_bootcore.cmm "A53_X64" +SYStem.Mode.Attach +Break.direct +; + +SYStem.JtagClock CTCK 24MHz + +; -------------------------------------------------------------------------------- +; peripheral initializations + +//Pin muxing MIO configuration MIO[0:12] + +Data.Set A:0xFF5E0068 %LE %Long 0x01010c00 ;QSPI_REF_CTRL + +&addr=0xFF180000 +RePeaT 13. +( + Data.Set A:&addr %LE %Long 0x2 + &addr=&addr+0x4 +) + +Data.Set A:0xFF180204 %LE %Long 0x2240000 + +Data.Set A:0xFF5E0238 %Long 0x17FFFE + +IF &dualqspi==1 + GOSUB READ_ID_TEST_DUAL +ELSE + GOSUB READ_ID_TEST + +Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL +Data.Set A:0xFF0F010C %Long 0x0FBE ; Interrupt disable register +Data.Set A:0xFF0F0128 %Long 0x01 ; thres hold +Data.Set A:0xFF0F012C %Long 0x01 ; thres hold +Data.Set A:0xFF0F0104 %Long 0x0FBE ; Interrupt status register + +Data.Set A:0xFF0F014C %Long 0x7 ; reset tx fifo and gen_fifo.. +WAIT 100.ms +Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode + +;The flash dualport works only under the non-secure mode for the ZYNQ-ULTRASCALE+ +SYStem.MemAccess DAP +Register.Set NS 1. +Register.Set M 0x5 ;EL1h + +; -------------------------------------------------------------------------------- +; Flash Declaration + +Break.RESet + +FLASHFILE.RESet +FLASHFILE.CONFIG 0xFF0F0000 + +IF &dualqspi==1 + FLASHFILE.TARGET 0xFFFC1000++0x2FFF E:0xFFFC4000++0x27FF &pdd/flash/word/spiw4b64_zynqultra.bin /KEEP /STACKSIZE 0x400 /DUALPORT +ELSE + FLASHFILE.TARGET 0xFFFC1000++0x2FFF E:0xFFFC4000++0x27FF &pdd/flash/byte/spi4b64fs_zynqultra.bin /KEEP /STACKSIZE 0x400 /DUALPORT + +FLASHFILE.GETID + +//End of the test prepareonly +IF "&arg1"=="PREPAREONLY" + ENDDO + +; Save Whole Flash +;FLASHFILE.SAVE "flash_dump.bin" 0x0++0x1FFFFFFF + +FLASHFILE.Create 0x0--0x1FFFFFFF 0x20000 Byte + +; NOTE ON LARGE IMAGES (>~100 MB): +; FLASHFILE.Load buffers the full source file into TRACE32 temporary memory +; before any programming. Many installations cap that pool at ~128 MB, so a +; single Load of a file larger than ~100 MB will fail with +; "FATAL ERROR: out of temporary memory" +; Workaround: split the source externally (e.g. `dd bs=1M count=N` and +; `dd bs=1M skip=N`) and issue each Load in its own +; FLASHFILE.ReProgram ALL / off bracket (the temp pool is released between +; brackets - a single bracket spanning multiple large Loads still exhausts +; memory on the second Load). + +; Flash BOOT.BIN (wolfBoot) at offset 0x0 +DIALOG.YESNO "Flash wolfBoot BOOT.BIN now?" +ENTRY &programnow +if &programnow +( + FLASHFILE.ReProgram ALL + FLASHFILE.Load "BOOT.BIN" 0x0 + FLASHFILE.ReProgram off +) + +; Flash signed application image at partition offset +DIALOG.YESNO "Flash signed application image now?" +ENTRY &programnow +if &programnow +( + FLASHFILE.ReProgram ALL + FLASHFILE.Load "test-app/image_v1_signed.bin" 0x7000000 + FLASHFILE.ReProgram off +) + +ENDDO + +; -------------------------------------------------------------------------------- +; Subroutines + +READ_ID_TEST: +( + Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL + Data.Set A:0xFF0F010C %Long 0x0FBE ;Interrupt disable register + Data.Set A:0xFF0F0128 %Long 0x01 ;thres hold + Data.Set A:0xFF0F012C %Long 0x01 ;thres hold + Data.Set A:0xFF0F0104 %Long 0x0FBE ;Interrupt status register + + ;read A:0xFF0F0100 -> 0x0 + + Data.Set A:0xFF0f014c %Long 0x7 ; reset tx fifo and gen_fifo.. + WAIT 100.ms + + Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode + + &dat_xfer=0x1<<8. + &spimode=0x1<<10. + &cs_lower=0x1<<12. + &cs_upper=0x1<<13. + &bus_lower=0x1<<14. + &bus_upper=0x2<<14. + &bus_both=0x3<<14. + &transmit=0x1<<16. + &receive=0x1<<17. + &stripe=0x1<<18. + + Data.Set A:0xFF0F0114 %Long 0x1 ;spi enable register , cs low + + Data.Set A:0xFF0F011C %Long 0x9f ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 + PRINT "read 0x" Data.Long(A:0xff0f0120) " (dummy)" + + Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 + PRINT "read 0x" Data.Long(A:0xff0f0120) " (manufacture id)" + + Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 + PRINT "read 0x" Data.Long(A:0xff0f0120) " (device id)" + + Data.Set A:0xFF0F011C %Long 0x00 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &transmit|&receive|&bus_lower|&cs_lower|&spimode|&dat_xfer|0x1 + PRINT "read 0x" Data.Long(A:0xff0f0120) + + Data.Set A:0xFF0F0114 %Long 0x0 ;spi disable, cs high + + RETURN +) + +READ_ID_TEST_DUAL: +( + Data.Set A:0xFF0F0144 %LE %Long 1 ;enable GQSPI_SEL + Data.Set A:0xFF0F010C %Long 0x0FBE ;Interrupt disable register + Data.Set A:0xFF0F0128 %Long 0x01 ;thres hold + Data.Set A:0xFF0F012C %Long 0x01 ;thres hold + Data.Set A:0xFF0F0104 %Long 0x0FBE ;Interrupt status register + + ;read A:0xFF0F0100 -> 0x0 + + Data.Set A:0xFF0f014c %Long 0x7 ; reset tx fifo and gen_fifo.. + WAIT 100.ms + + Data.Set A:0xFF0F0100 %Long 0x00080010 ;Config register, clk speed control[5:3] , mode==00 [31:30] I/O mode + + &dat_xfer=0x1<<8. + &spimode=0x1<<10. + &cs_lower=0x1<<12. + &cs_upper=0x1<<13. + &bus_lower=0x1<<14. + &bus_upper=0x2<<14. + &bus_both=0x3<<14. + &transmit=0x1<<16. + &receive=0x1<<17. + &stripe=0x1<<18. + + Data.Set A:0xFF0F0114 %Long 0x1 ;spi enable register , cs low + + Data.Set A:0xFF0F011C %Long 0x00009f9f ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer + PRINT "read 0x" Data.Long(A:0xff0f0120) + + Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer + PRINT "read 0x" Data.Long(A:0xff0f0120) + + Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer + PRINT "read 0x" Data.Long(A:0xff0f0120) + + Data.Set A:0xFF0F011C %Long 0x00000000 ; tx, data transfer + Data.Set A:0xFF0F0140 %Long &stripe|&transmit|&receive|&bus_both|&cs_lower|&cs_upper|&spimode|&dat_xfer|0x4 ; tx,4 byte data transfer + PRINT "read 0x" Data.Long(A:0xff0f0120) + + Data.Set A:0xFF0F0114 %Long 0x0 ;spi disable, cs high + + RETURN +) diff --git a/tools/scripts/zcu102/zcu102-debug-wolfboot.cmm b/tools/scripts/zcu102/zcu102-debug-wolfboot.cmm index c0bc0cbabc..2e0edd5951 100644 --- a/tools/scripts/zcu102/zcu102-debug-wolfboot.cmm +++ b/tools/scripts/zcu102/zcu102-debug-wolfboot.cmm @@ -22,7 +22,8 @@ Trace.DISable SYStem.Mode Prepare ;GOSUB DisableWatchdog -;DO "C:/T32/demo/arm/hardware/zynq_ultrascale/scripts/zynq-ultrascale_kick_bootcore.cmm" A53_X64 +; "~~" expands to the TRACE32 install dir on any host OS (Windows/Linux/macOS): +;DO "~~/demo/arm/hardware/zynq_ultrascale/scripts/zynq-ultrascale_kick_bootcore.cmm" A53_X64 SYStem.Mode.Attach From 0cecff09ab7ff2aec933bfe94347eed1286be4ac Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Apr 2026 12:17:28 -0700 Subject: [PATCH 5/5] ZynqMP: revert WOLFBOOT_ORIGIN to 0x8000000 The previous commit moved WOLFBOOT_ORIGIN from 0x8000000 to 0x10000000 to match the linker script, but WOLFBOOT_LOAD_ADDRESS is also 0x10000000. This causes wolfBoot to overwrite itself when loading the firmware image (32MB FIT written to 0x10000000 overwrites wolfBoot's .text at the same address mid-read, hanging the boot). Revert to 0x8000000 (128MB, same as U-Boot) and update linker script to match. WOLFBOOT_LOAD_ADDRESS at 0x10000000 is safely above wolfBoot's 2MB footprint at 0x8000000-0x8200000. --- config/examples/zynqmp_sdcard.config | 6 ++++-- hal/zynq.ld | 19 +++++-------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/config/examples/zynqmp_sdcard.config b/config/examples/zynqmp_sdcard.config index 8573dd1edd..f2401021f3 100644 --- a/config/examples/zynqmp_sdcard.config +++ b/config/examples/zynqmp_sdcard.config @@ -103,8 +103,10 @@ CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\" # ============================================================================ # Boot Memory Layout # ============================================================================ -# wolfBoot runs from DDR at 0x10000000 (loaded by BL31; see hal/zynq.ld). -WOLFBOOT_ORIGIN=0x10000000 +# wolfBoot runs from DDR at 0x8000000 (128MB, same as U-Boot, loaded via BL31). +# Must match the ORIGIN in hal/zynq.ld. WOLFBOOT_LOAD_ADDRESS must be above +# this region (wolfBoot is ~2MB) to avoid self-overwrite during firmware load. +WOLFBOOT_ORIGIN=0x8000000 # Load Partition to RAM Address (Linux kernel loads here) WOLFBOOT_LOAD_ADDRESS?=0x10000000 diff --git a/hal/zynq.ld b/hal/zynq.ld index f710c40836..f4b4b2fcc1 100644 --- a/hal/zynq.ld +++ b/hal/zynq.ld @@ -13,21 +13,12 @@ MEMORY { /* psu_ddr_0_MEM_0 : ORIGIN = 0x0, LENGTH = 0x80000000 */ /* wolfBoot DDR location (2MB reserved): - * Loaded by FSBL/BL31 to DDR at 0x10000000 (256MB). Must be above - * the OS kernel load region: for FIT images whose "load" address is - * 0x00200000 (typical for AArch64 kernels) and whose payload - * approaches or exceeds ~126MB, the kernel memcpy into 0x00200000.. - * sweeps across 0x08000000 and would clobber wolfBoot if linked - * there. Same address used for QSPI and SD card boot. - * - * WOLFBOOT_LOAD_ADDRESS (signed-image staging base) also defaults to - * 0x10000000 on the SD-card config. The overlap with wolfBoot's own - * 2MB text region is safe because wolfBoot completes the verify and - * jump sequence from I-cache; the I-cache is only invalidated inside - * el2_flush_and_disable_mmu, immediately before control transfers to - * the loaded kernel (which no longer needs wolfBoot's .text). + * Loaded by FSBL/BL31 to DDR at 0x8000000 (128MB, same as U-Boot). + * WOLFBOOT_LOAD_ADDRESS (signed-image staging area) must be above + * this region to avoid overwriting wolfBoot during firmware load. + * Must match WOLFBOOT_ORIGIN in the target .config. */ - psu_ddr_0_MEM_0 : ORIGIN = 0x10000000, LENGTH = 0x200000 + psu_ddr_0_MEM_0 : ORIGIN = 0x8000000, LENGTH = 0x200000 psu_ddr_1_MEM_0 : ORIGIN = 0x800000000, LENGTH = 0x80000000 psu_ocm_ram_0_MEM_0 : ORIGIN = 0xFFFC0000, LENGTH = 0x40000 psu_qspi_linear_0_MEM_0 : ORIGIN = 0xC0000000, LENGTH = 0x20000000