diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 36152ec8..86eac215 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -477,7 +477,7 @@ void hal_init(void) * inner retry inside mpfs_ddr_init() exhausts (typically because * MTC wedged after the first failure), come back here for a full * controller re-init. Empirical: per-attempt failure rate ~30%, so - * 3 outer attempts cover ~99.7% of boots. */ + * MPFS_DDR_MAX_OUTER_RETRY (6) outer attempts cover ~99.9% of boots. */ for (outer_retry = 0; outer_retry < MPFS_DDR_MAX_OUTER_RETRY; outer_retry++) { if (outer_retry > 0) { @@ -752,12 +752,18 @@ static int mpfs_dts_fixup_inplace(void* dts_addr) * through the PDMA master. A DDR source is read via its non-cached alias so * PDMA sees real DDR; mpfs_pdma_memcpy remaps the dst 0x8x->0xCx and flushes * L2. Chunked + WDT-petted for kernel-sized copies. */ -void wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len) +int wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len) { uintptr_t d = (uintptr_t)dst; uintptr_t s = (uintptr_t)src; + volatile const uint8_t *ncd; + const uint8_t *ncs; uint32_t off = 0; uint32_t chunk; + uint32_t k; + int retry; + int mism; + int rc = 0; if ((s & 0xF0000000UL) == 0x80000000UL) { s |= 0x40000000UL; /* non-cached source alias */ @@ -767,17 +773,51 @@ void wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len) if (chunk > (1024U * 1024U)) { chunk = 1024U * 1024U; } - (void)mpfs_pdma_memcpy((void *)(d + off), - (const void *)(s + off), chunk); - /* Refresh all five MSS watchdogs (they always count and reset the - * chip and cannot be disabled) during the multi-MB kernel copy. */ - MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU; - MSS_WDT_REFRESH(MSS_WDT_U54_1_BASE) = 0xDEADC0DEU; - MSS_WDT_REFRESH(MSS_WDT_U54_2_BASE) = 0xDEADC0DEU; - MSS_WDT_REFRESH(MSS_WDT_U54_3_BASE) = 0xDEADC0DEU; - MSS_WDT_REFRESH(MSS_WDT_U54_4_BASE) = 0xDEADC0DEU; + /* mpfs_pdma_memcpy always returns 0, so the read-back verify below is + * the authoritative success check for this chunk. The PDMA->DDR write + * intermittently drops a block, so re-PDMA on a mismatch (same pattern + * as sdhci_platform_block_copy). A DDR destination (0x8xxxxxxx) is + * read back through its non-cached alias (| 0x40000000) so we compare + * what actually landed in DDR, not stale L2; this makes the caller's + * fail-closed rc real for the signature-uncovered kernel/dtb copies. + * A non-DDR destination (e.g. an L2 scratch buffer) lands directly, so + * a single copy suffices. */ + mism = 1; + for (retry = 0; retry < 8 && mism != 0; retry++) { + (void)mpfs_pdma_memcpy((void *)(d + off), + (const void *)(s + off), chunk); + /* Refresh all five MSS watchdogs (they always count and reset the + * chip and cannot be disabled) during the multi-MB kernel copy + * and its read-back verify. */ + MSS_WDT_REFRESH(MSS_WDT_E51_BASE) = 0xDEADC0DEU; + MSS_WDT_REFRESH(MSS_WDT_U54_1_BASE) = 0xDEADC0DEU; + MSS_WDT_REFRESH(MSS_WDT_U54_2_BASE) = 0xDEADC0DEU; + MSS_WDT_REFRESH(MSS_WDT_U54_3_BASE) = 0xDEADC0DEU; + MSS_WDT_REFRESH(MSS_WDT_U54_4_BASE) = 0xDEADC0DEU; + if ((d & 0xF0000000UL) != 0x80000000UL) { + mism = 0; /* non-DDR dst lands on the first copy */ + break; + } + __asm__ volatile("fence iorw,iorw" ::: "memory"); + ncd = (volatile const uint8_t *)((d + off) | 0x40000000UL); + ncs = (const uint8_t *)(s + off); + mism = 0; + for (k = 0; k < chunk; k++) { + if (ncd[k] != ncs[k]) { + mism = 1; + break; + } + } + } + if (mism != 0) { + /* Copy could not be verified within the retry budget; remember the + * failure so the caller fails closed rather than boot corrupt, + * no-longer-signature-covered data. */ + rc = -1; + } off += chunk; } + return rc; } /* L2 round-trip wrapper around mpfs_dts_fixup_inplace(). The dtb lives in DDR @@ -816,7 +856,11 @@ int hal_dts_fixup(void* dts_addr) /* fixup in the CPU-writable L2 buffer */ ret = mpfs_dts_fixup_inplace(l2_dtb); /* L2 -> DDR via PDMA (expanded totalsize) */ - wolfBoot_fit_memcpy(dts_addr, l2_dtb, (uint32_t)fdt_totalsize(l2_dtb)); + if (wolfBoot_fit_memcpy(dts_addr, l2_dtb, + (uint32_t)fdt_totalsize(l2_dtb)) != 0) { + wolfBoot_printf("FDT: dtb copy-back to DDR failed\n"); + return -1; + } return ret; } #else diff --git a/include/hal.h b/include/hal.h index 289b76d6..0fb700dd 100644 --- a/include/hal.h +++ b/include/hal.h @@ -36,8 +36,10 @@ extern "C" { #if defined(MMU) || defined(WOLFBOOT_FDT) extern void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset); /* Weak copy hook for FIT subimages (kernel/dtb): default memcpy; boards where - * CPU writes to the load address don't land override it with a DMA copy. */ -extern void wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len); + * CPU writes to the load address don't land override it with a DMA copy. + * Returns 0 on success or a negative value if the copy failed, so callers can + * fail closed instead of running on stale data. */ +extern int wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len); #else extern void do_boot(const uint32_t *app_offset); #endif diff --git a/options.mk b/options.mk index 90e9951a..195163ff 100644 --- a/options.mk +++ b/options.mk @@ -1440,9 +1440,13 @@ endif # Per-hart secondary stack size: single source of truth shared by the # startup asm (via this -D) and the linker script (via @STACK_SIZE_PER_HART@ # substitution in the LSCRIPT rule). Set in the target .config; defaults to -# 0 (no secondary park/wake stacks). +# 0 (no secondary park/wake stacks). The concept (and the consuming startup +# asm) is RISC-V only, so gate the -D to RISC-V; the default is kept +# unconditional because the LSCRIPT sed always needs a value to substitute. STACK_SIZE_PER_HART ?= 0 -CFLAGS+=-DSTACK_SIZE_PER_HART=$(STACK_SIZE_PER_HART) +ifneq (,$(filter RISCV RISCV64,$(ARCH))) + CFLAGS+=-DSTACK_SIZE_PER_HART=$(STACK_SIZE_PER_HART) +endif CFLAGS+=$(CFLAGS_EXTRA) OBJS+=$(OBJS_EXTRA) diff --git a/src/boot_aarch64.c b/src/boot_aarch64.c index 6cf3a2ac..4e6c9e2e 100644 --- a/src/boot_aarch64.c +++ b/src/boot_aarch64.c @@ -148,7 +148,7 @@ int WEAKFUNCTION hal_dts_fixup(void* dts_addr) * */ -#ifdef MMU +#if defined(MMU) || defined(WOLFBOOT_FDT) void RAMFUNCTION do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) #else void RAMFUNCTION do_boot(const uint32_t *app_offset) diff --git a/src/boot_arm32.c b/src/boot_arm32.c index 3df45163..1ab63868 100644 --- a/src/boot_arm32.c +++ b/src/boot_arm32.c @@ -55,7 +55,7 @@ void boot_entry_C(void) * */ -#ifdef MMU +#if defined(MMU) || defined(WOLFBOOT_FDT) void RAMFUNCTION do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) #else void RAMFUNCTION do_boot(const uint32_t *app_offset) diff --git a/src/boot_ppc.c b/src/boot_ppc.c index 6b19e713..9a984df8 100644 --- a/src/boot_ppc.c +++ b/src/boot_ppc.c @@ -409,7 +409,7 @@ void RAMFUNCTION wolfBoot_os64bit_jump(os64bit_entry_t entry, } #endif /* ENABLE_OS64BIT */ -#ifdef MMU +#if defined(MMU) || defined(WOLFBOOT_FDT) void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) #else void do_boot(const uint32_t *app_offset) diff --git a/src/boot_x86_64.c b/src/boot_x86_64.c index 715f2b68..865d8665 100644 --- a/src/boot_x86_64.c +++ b/src/boot_x86_64.c @@ -36,7 +36,7 @@ extern unsigned int *END_STACK; extern void RAMFUNCTION x86_64_efi_do_boot(uint8_t *kernel); -#ifdef MMU +#if defined(MMU) || defined(WOLFBOOT_FDT) void RAMFUNCTION do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) #else void RAMFUNCTION do_boot(const uint32_t *app_offset) diff --git a/src/fdt.c b/src/fdt.c index ca52c2fe..e949747a 100644 --- a/src/fdt.c +++ b/src/fdt.c @@ -1135,11 +1135,14 @@ int fit_load_ramdisk(void* fit, const char* ramdisk_node, void* dts_addr) /* Weak copy hook for FIT subimages (kernel/dtb). Default is a plain memcpy; * boards where CPU writes to the load destination do not land (e.g. the * PolarFire MPFS250 DDR, where cached writes thrash L2 Scratch) override this - * with a DMA-based copy (see hal/mpfs250.c). */ -void __attribute__((weak)) wolfBoot_fit_memcpy(void *dst, const void *src, + * with a DMA-based copy (see hal/mpfs250.c). Returns 0 on success or a + * negative value if the copy failed, so callers can fail closed rather than + * run on a partially written destination. */ +int __attribute__((weak)) wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len) { memcpy(dst, src, len); + return 0; } /* Inner implementation shared by fit_load_image_ex and fit_load_image_to. @@ -1236,7 +1239,11 @@ static void* fit_load_image_inner(void* fdt, const char* image, int* lenp, } wolfBoot_printf("Loading Image %s: %p -> %p " "(%d bytes)\n", image, data, load, len); - wolfBoot_fit_memcpy(load, data, (uint32_t)len); + if (wolfBoot_fit_memcpy(load, data, (uint32_t)len) != 0) { + wolfBoot_printf("FIT: copy of %s to %p failed\n", + image, load); + return NULL; + } } /* No per-image hash-1 re-verification here. Per the diff --git a/src/update_disk.c b/src/update_disk.c index 862322e2..d30a37f7 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -631,7 +631,10 @@ void RAMFUNCTION wolfBoot_start(void) dts_addr = (uint8_t*)WOLFBOOT_LOAD_DTS_ADDRESS; wolfBoot_printf("Loading DTS: %p -> %p (%d bytes)\n", dts_ptr, dts_addr, dts_size); - wolfBoot_fit_memcpy(dts_addr, dts_ptr, dts_size); + if (wolfBoot_fit_memcpy(dts_addr, dts_ptr, dts_size) != 0) { + wolfBoot_printf("FIT: failed to load DTS\r\n"); + wolfBoot_panic(); + } } } #ifdef WOLFBOOT_FIT_RAMDISK