diff --git a/hal/nxp_t2080.c b/hal/nxp_t2080.c index 7681076d..66a52244 100644 --- a/hal/nxp_t2080.c +++ b/hal/nxp_t2080.c @@ -678,12 +678,10 @@ static int hal_fman_init(void) return -1; } - /* Validate the container before uploading (the T10xx - * qe_check_firmware() path checks the same things): version, - * microcode count, self-consistent length, every code range inside - * the declared image, and the whole image inside the remaining NOR - * bank. 64-bit arithmetic so the sums cannot wrap. Fail closed: - * FMan stays unconfigured on any mismatch. */ + /* Validate before uploading, as the T10xx qe_check_firmware() path + * does: version, count, self-consistent length, every code range + * inside the image, the image inside the NOR bank. 64-bit so the + * sums cannot wrap; FMan stays unconfigured on any mismatch. */ if (hdr->version != 1) { wolfBoot_printf("FMAN: version %d unsupported\n", hdr->version); return -1; diff --git a/hal/x86_64_efi.c b/hal/x86_64_efi.c index f1c29dc6..c9c7eb2d 100644 --- a/hal/x86_64_efi.c +++ b/hal/x86_64_efi.c @@ -246,9 +246,8 @@ static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename, if (file == NULL) return -1; - /* FileSize() is 64-bit; the rest of the loader (header parsing, LoadImage - * size) works in uint32_t. Reject a size that would not fit rather than - * silently truncating it into *sz. */ + /* FileSize() is 64-bit but the loader works in uint32_t; reject a + * size that would not fit rather than truncating it. */ filesz = FileSize(file); if (filesz == 0 || filesz > 0xFFFFFFFFULL) { wolfBoot_printf("Invalid file size: 0x%lx\n", (unsigned long)filesz); @@ -273,12 +272,9 @@ static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename, return -1; } - /* EFI_FILE_PROTOCOL.Read() takes a UINTN *BufferSize (64 bits on - * x86-64) and writes the bytes actually read through it. Passing the - * caller's uint32_t *sz would let an ordinary read store eight bytes - * through a four-byte stack object, and the 64-bit read of - * *BufferSize would see adjacent stack bytes. Same contract as the - * AArch64 sibling. */ + /* Read() takes a UINTN *BufferSize (64-bit here) and writes the + * byte count back through it; passing the caller's uint32_t *sz + * would store eight bytes through a four-byte object. */ readsz = (UINTN)*sz; status = uefi_call_wrapper(file->Read, 3, file, &readsz, (void *)(uintptr_t)*_addr); diff --git a/include/user_settings.h b/include/user_settings.h index 762bdb23..96605cec 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -58,23 +58,18 @@ extern int tolower(int c); #endif #ifdef USE_FAST_MATH - /* WC_NO_HARDEN is intended only for verify-only builds: the image - * verification path performs public-key operations only, so timing - * resistance and hardening are not required there. Software DICE - * (WOLFCRYPT_TZ_PSA without WOLFBOOT_DICE_HW) is not verify-only: - * it signs the attestation claims with the private IAK via - * wc_CoseSign1_Sign_ex() and must keep the timing hardening, so - * exclude it. Hardware DICE (WOLFBOOT_DICE_HW) keeps the signing - * in the crypto engine and stays verify-only in fast math. */ + /* WC_NO_HARDEN suits verify-only builds, which do public-key + * operations only. Software DICE (WOLFCRYPT_TZ_PSA without + * WOLFBOOT_DICE_HW) signs the attestation claims with the private + * IAK, so it is excluded; hardware DICE keeps signing in the crypto + * engine and stays verify-only. */ # if !defined(WOLFCRYPT_TZ_PSA) || defined(WOLFBOOT_DICE_HW) # define WC_NO_HARDEN # else - /* Turn the hardening on rather than only dropping - * WC_NO_HARDEN: tfm.c never tests WC_NO_HARDEN, so removing it - * changes no code -- it only un-silences the advisory in - * settings.h, which -Werror then turns into a build failure. - * TFM_TIMING_RESISTANT is what actually makes tfm.c constant - * time (ECC_TIMING_RESISTANT is already set with HAVE_ECC). */ + /* tfm.c never tests WC_NO_HARDEN, so dropping it alone changes + * no code and only un-silences an advisory that -Werror turns + * into a build failure. TFM_TIMING_RESISTANT is what makes + * tfm.c constant time. */ # define TFM_TIMING_RESISTANT # endif #endif diff --git a/src/libwolfboot.c b/src/libwolfboot.c index ace53440..78f46b36 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -256,14 +256,11 @@ static const uint32_t wolfboot_magic_trail = WOLFBOOT_MAGIC_TRAIL; static uint8_t NVM_CACHE[NVM_CACHE_SIZE] XALIGNED(16); static int nvm_cached_sector = 0; -/* Scrub the staging buffer without depending on wolfCrypt. ForceZero() - * would drag and - * into every NVM_FLASH_WRITEONCE build of this file, including the two - * that cannot supply them: tools/check_config (no wolfSSL include - * path) and the STM32Cube test-app (no stm32*_hal_conf.h). A volatile - * byte loop is also the right shape for a RAMFUNCTION caller, since - * ForceZero() itself lives in flash and must not be called while the - * flash is being programmed. */ +/* Scrub the staging buffer without depending on wolfCrypt: ForceZero() + * would pull wolfSSL headers into every NVM_FLASH_WRITEONCE build, + * including tools/check_config and the STM32Cube test-app, which cannot + * supply them. A volatile byte loop also suits the RAMFUNCTION callers, + * since ForceZero() lives in flash. */ static void RAMFUNCTION nvm_cache_scrub(void) { volatile uint8_t *p = (volatile uint8_t *)NVM_CACHE; diff --git a/src/sdhci.c b/src/sdhci.c index cc659bdb..ceac876b 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -1541,15 +1541,11 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr, } } - /* Check for errors. - * - * An earlier failure (e.g. the SDMA wait above timing out) must survive - * this block. A wolfBoot-side wait timeout does not necessarily set an - * SRS12 error bit, so without preserving `status` the CMD12 / wait-busy - * results below would overwrite it and sdhci_transfer() would report - * success for a transfer that never completed. The caller then uses a - * partially filled buffer, which surfaces much later as a bogus image - * integrity failure rather than as the I/O error it actually is. */ + /* Check for errors. An earlier failure (e.g. the SDMA wait timing + * out) must survive this block: a wolfBoot-side timeout need not set + * an SRS12 error bit, so without preserving `status` the CMD12 and + * wait-busy results below would report success for a transfer that + * never completed. */ reg = SDHCI_REG(SDHCI_SRS12); if ((reg & SDHCI_SRS12_ERR_STAT) == 0) { /* If multi-block, send CMD12 to stop transfer. This is issued even diff --git a/tools/unit-tests/unit-aurix-erased-fill.c b/tools/unit-tests/unit-aurix-erased-fill.c index 361f0760..d719f572 100644 --- a/tools/unit-tests/unit-aurix-erased-fill.c +++ b/tools/unit-tests/unit-aurix-erased-fill.c @@ -1,23 +1,16 @@ /* unit-aurix-erased-fill.c * - * Regression test: cacheSector() and - * programBytesToErasedFlash() in hal/aurix_tc3xx.c filled their - * uint32_t page buffers with the byte-valued FLASH_BYTE_ERASED, so in - * the default (non-inverted) build every 32-bit word became - * 0x000000FF instead of 0xFFFFFFFF. A one-byte write into an - * erased page then programmed zeroes into the unrelated bytes of the - * same word, and the sector-cache path corrupted logically-erased - * pages during read-modify-write. - * - * The test extracts the real cacheSector() and - * programBytesToErasedFlash() from hal/aurix_tc3xx.c (generated by - * the Makefile) and runs them against an emulated tc3_flash_* API on - * a host flash array. The assertions use FLASH_BYTE_ERASED / - * FLASH_WORD_ERASED from include/wolfboot/wolfboot.h, so the same - * source is built twice: default polarity (erased = 0xFF, where the - * pre-fix bug shows) and WOLFBOOT_FLAGS_INVERT (erased = 0x00, where - * the fix must not change the fill value). + * Regression test: cacheSector() and programBytesToErasedFlash() in + * hal/aurix_tc3xx.c filled uint32_t page buffers with the byte-valued + * FLASH_BYTE_ERASED, so each word became 0x000000FF instead of + * 0xFFFFFFFF. A one-byte write into an erased page then programmed + * zeroes into the rest of the word. * + * Extracts both functions (generated by the Makefile) and runs them + * against an emulated tc3_flash_* API. Built twice, once per flag + * polarity: default (erased = 0xFF), where the bug shows, and + * WOLFBOOT_FLAGS_INVERT (erased = 0x00), where the fill must not + * change. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-efi-x86-open-image.c b/tools/unit-tests/unit-efi-x86-open-image.c index 2a783cb3..b4f96248 100644 --- a/tools/unit-tests/unit-efi-x86-open-image.c +++ b/tools/unit-tests/unit-efi-x86-open-image.c @@ -1,23 +1,17 @@ /* unit-efi-x86-open-image.c * - * Regression test: open_kernel_image() in hal/x86_64_efi.c passed - * the caller's uint32_t *sz directly as the BufferSize argument of - * EFI_FILE_PROTOCOL.Read(). That parameter is a UINTN, which is 64 bits on - * x86-64, and the UEFI spec requires the firmware to write the number of - * bytes actually read back through it. An ordinary successful read therefore - * stored eight bytes through the four-byte object, corrupting the stack bytes - * immediately after kernel_size/update_size in efi_main on every image load. - * - * The fix passes a local UINTN to Read() and copies the completed byte count - * back into *sz, exactly as the AArch64 sibling (hal/aarch64_efi.c) does. - * - * hal/x86_64_efi.c is normally only built by the CMake x86_64_efi target - * (gnu-efi headers, -lgnuefi -lefi), so this test doubles as its only host - * build coverage: it includes the HAL file directly and stands in for the - * gnu-efi runtime with host mocks (BS, LibFileInfo, the x86-64 efi_callN() - * trampolines, wolfBoot_start). The mock Read() follows the spec'd BufferSize - * contract, so this test fails against the pre-fix code. + * Regression test: open_kernel_image() in hal/x86_64_efi.c passed the + * caller's uint32_t *sz as the BufferSize argument of + * EFI_FILE_PROTOCOL.Read(). That is a UINTN -- 64 bits on x86-64 -- + * and the firmware writes the byte count back through it, so every + * successful read stored eight bytes through a four-byte object and + * corrupted the stack after it. The fix passes a local UINTN and copies + * the count back, as the AArch64 sibling does. * + * hal/x86_64_efi.c is normally built only by the CMake x86_64_efi + * target, so this doubles as its host build coverage: it includes the + * HAL directly and mocks the gnu-efi runtime. The mock Read() follows + * the spec'd BufferSize contract, so it fails against the pre-fix code. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-ls1028a-xspi-write.c b/tools/unit-tests/unit-ls1028a-xspi-write.c index ad06fed0..258ca467 100644 --- a/tools/unit-tests/unit-ls1028a-xspi-write.c +++ b/tools/unit-tests/unit-ls1028a-xspi-write.c @@ -1,28 +1,17 @@ /* unit-ls1028a-xspi-write.c * - * Regression test: hal_flash_write() and ext_flash_write() in - * hal/nxp_ls1028a.c issue xspi_write_en() once before xspi_flash_write(), - * but xspi_flash_write() issues one Page Program command per 256-byte - * chunk when len > XSPI_IP_BUF_SIZE. NOR flash clears its - * write-enable latch after each program operation, so with a single WEN - * only the first page of the request is actually programmed; the - * remaining Page Programs are rejected by the flash while the wrapper - * still reports success. - * - * The fix issues Write Enable for every page inside xspi_flash_write(), - * after the prior program has completed and before the Page Program - * command. - * - * The LS1028A XSPI driver is plain C register access apart from a few - * bare `asm volatile("...")` barrier statements (DDR init, delay), so - * the test includes the HAL file via nxp_ls1028a_host.c (generated by - * the Makefile, identical except those statements are blanked out), - * rebases XSPI_BASE onto host memory, and emulates the FlexSPI - * controller at the XSPI_IPCMD_START() boundary. The emulated NOR keeps - * a write-enable latch exactly like real NOR: set by WEN, consumed by - * each Page Program / Sector Erase, and a program or erase attempted - * without WEN is rejected. + * Regression test: hal_flash_write()/ext_flash_write() in + * hal/nxp_ls1028a.c issued one Write Enable before xspi_flash_write(), + * which programs a page per chunk. NOR clears the write-enable latch + * after each program, so only the first page landed while the wrapper + * still reported success. The fix enables writes for every page. * + * The HAL is plain register access apart from a few barrier asm + * statements, so the test includes it via nxp_ls1028a_host.c (generated + * by the Makefile with those blanked), rebases XSPI_BASE onto host + * memory, and emulates the controller at the XSPI_IPCMD_START() + * boundary. The emulated NOR keeps a real write-enable latch and a busy + * window, so a dropped WEN or a missing wait shows up as a rejection. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-nvm-cache-scrub.c b/tools/unit-tests/unit-nvm-cache-scrub.c index 5a6c81e5..e279cff8 100644 --- a/tools/unit-tests/unit-nvm-cache-scrub.c +++ b/tools/unit-tests/unit-nvm-cache-scrub.c @@ -1,24 +1,15 @@ /* unit-nvm-cache-scrub.c * - * Regression test: under NVM_FLASH_WRITEONCE the - * partition-trailer read-modify-write helpers stage a full flash - * sector into the file-scope buffer NVM_CACHE. In EXT_ENCRYPTED - * builds that same sector is where the firmware key/nonce live - * (ENCRYPT_CACHE is a macro alias of NVM_CACHE in this - * configuration), and neither trailer_write() nor - * partition_magic_write() scrubbed NVM_CACHE on return - the only - * ForceZero of the buffer is compiled out precisely for - * NVM_FLASH_WRITEONCE. A normal boot therefore leaves the plaintext - * key/nonce in .bss at a fixed address across the handoff to the + * Regression test: under NVM_FLASH_WRITEONCE the trailer helpers stage + * a whole flash sector into NVM_CACHE, which in EXT_ENCRYPTED builds is + * also where the firmware key and nonce live (ENCRYPT_CACHE aliases it). + * Neither trailer_write() nor partition_magic_write() scrubbed it on + * return, leaving the plaintext key in .bss across the handoff to the * application. * - * The test extracts the real trailer_write() and - * partition_magic_write() from src/libwolfboot.c (generated by the - * Makefile) and runs them with a test-owned NVM_CACHE, a staged - * sector image carrying a key/nonce pattern, and stubbed - * hal_flash_write/erase. After each call the whole buffer must be - * scrubbed; pre-fix the staged sector (key bytes included) remained. - * + * Extracts the real helpers (generated by the Makefile) and runs them + * with a test-owned NVM_CACHE, a staged sector carrying a key pattern + * and stubbed flash calls; the buffer must be zero after each call. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-p1021-fcm-bytes.c b/tools/unit-tests/unit-p1021-fcm-bytes.c index 17465d40..9ffdf3c3 100644 --- a/tools/unit-tests/unit-p1021-fcm-bytes.c +++ b/tools/unit-tests/unit-p1021-fcm-bytes.c @@ -3,41 +3,17 @@ * Regression tests for the FCM byte helpers and page loops in * hal/nxp_p1021.c. * - * hal_flash_read_bytes() and hal_flash_write_bytes() loop on - * `while (flash_idx < len)`, comparing the FCM buffer index (which - * hal_flash_set_addr() initializes to the page column, `flash_idx = col`) - * against the caller's relative byte count. With col != 0: - * - * - if len <= col the loop runs zero times, so the page program proceeds - * with stale FCM buffer contents (write) or the caller's buffer is left - * unchanged (read); - * - if len > col only len - col bytes are copied; - * - a final one-to-three byte remainder is copied as a full word, reading - * or writing outside the requested range. - * - * The fix computes an absolute end (flash_idx + len) and copies exactly - * len bytes, handling the tail byte-wise. - * - * the ext_flash_write()/ext_flash_read() page loops size every - * iteration from the total request length instead of the remaining - * length, do not cap it to the page, and program ELBC_FBCR with the - * column offset - so the byte-count field (FBCR[BC], P1021RM 12.3.30 - * bits 20-31 MSB-first, i.e. the low 12 bits) carries the column rather - * than a count: a nonsense transfer size whenever col is non-zero. - * The fix uses chunk = min(len - pos, page_size - col), programs the BC - * field (0 only for a full page from column 0 - the only - * ECC-generating/checking setting) and advances by chunk. - * - * hal/nxp_p1021.c is PowerPC code, but the FCM helpers under test are - * plain byte copies. The test includes the HAL file (via nxp_p1021_host.c, - * generated by the Makefile) and drives the static helpers directly, with - * flash_buf backed by a host array standing in for the eLBC FCM region. - * The host copy differs from the real file only in that the three bare - * `__asm__ __volatile__("..." )` cache-barrier statements (DDR init and - * multi-core boot) are blanked out: they cannot assemble on x86. The - * PowerPC-asm accessors from nxp_ppc.h are shadowed with host versions - * below, so they are never emitted either. + * The helpers looped on `flash_idx < len`, comparing the FCM buffer + * index (which starts at the page column) against a relative byte + * count, so with col != 0 they copied too few bytes and rounded the + * tail up to a whole word. The page loops sized every iteration from + * the total length instead of the remainder, did not cap to the page, + * and programmed FBCR with the column instead of a byte count. * + * The HAL is PowerPC but the helpers are plain byte copies, so the test + * includes it via nxp_p1021_host.c (generated by the Makefile, with the + * cache-barrier asm blanked) and drives the statics directly against a + * host array standing in for the FCM region. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-p1021-qe-firmware.c b/tools/unit-tests/unit-p1021-qe-firmware.c index 22f8bd8a..121c0f26 100644 --- a/tools/unit-tests/unit-p1021-qe-firmware.c +++ b/tools/unit-tests/unit-p1021-qe-firmware.c @@ -1,26 +1,18 @@ /* unit-p1021-qe-firmware.c * * Regression test (bounds part): qe_upload_firmware() in - * hal/nxp_p1021.c validated only the self-declared consistency of the - * QE microcode blob (magic, version, count range, length == computed - * size, optional CRC32). The per-microcode code_offset/count fields and - * the total length were never bounded to the 64 KiB buffer actually - * read from NAND (which, on the shipped p1021 config, sits in the - * update partition). A structurally consistent blob with a large - * code_offset or word count made the upload loop read (and copy into - * QE instruction RAM) arbitrarily far past the buffer. + * hal/nxp_p1021.c checked only the blob's self-consistency (magic, + * version, count, length, optional CRC32). The per-microcode + * code_offset/count and the total length were never bounded to the + * 64 KiB buffer read from NAND, so a structurally valid blob could make + * the upload loop read arbitrarily far past it. * - * Note: the full recommendation also calls for cryptographic - * authentication of the microcode before activation. That is a design - * decision (keys, signed manifest, build flow) beyond a minimal patch - * and is tracked as a note on the finding; this test covers the - * fail-closed bounding that can be fixed in the code. - * - * The test compiles the real hal/nxp_p1021.c (as nxp_p1021_host.c, - * generated by the Makefile) with the PowerPC accessors shadowed by - * host equivalents, and drives qe_upload_firmware() directly with - * crafted blobs. + * Authenticating the microcode before activation is a separate design + * question (keys, signed manifest, build flow); this covers only the + * fail-closed bounding. * + * Compiles the real HAL via nxp_p1021_host.c with the PowerPC accessors + * shadowed, and drives qe_upload_firmware() with crafted blobs. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-sdhci-dma-error.c b/tools/unit-tests/unit-sdhci-dma-error.c index d797a406..d7b56af2 100644 --- a/tools/unit-tests/unit-sdhci-dma-error.c +++ b/tools/unit-tests/unit-sdhci-dma-error.c @@ -1,20 +1,16 @@ /* unit-sdhci-dma-error.c * - * Regression test: a failed SDMA data transfer must be reported as an error. - * - * sdhci_transfer() waits for Transfer Complete on the SDMA path and sets - * status = -1 if that wait times out. The post-transfer "check for errors" - * block then runs unconditionally, and a wolfBoot-side wait timeout does not - * necessarily leave an error bit set in SRS12. Before the fix, the CMD12 - * (stop transmission) result overwrote that -1, so the function returned - * success for a transfer that never moved any data. - * - * The caller then treats a partially filled buffer as a good read. On a - * verified-boot target that surfaces much later as an image integrity - * failure rather than as the I/O error it actually is -- and where - * anti-rollback is enabled, the good lower-versioned slot is refused too, - * leaving nothing bootable. + * Regression test: a failed SDMA data transfer must be reported as an + * error. sdhci_transfer() sets status = -1 when the Transfer Complete + * wait times out, but the error-check block that follows runs + * unconditionally and a wolfBoot-side timeout need not leave an error + * bit in SRS12, so the CMD12 result overwrote the -1 and the function + * returned success for a transfer that moved nothing. * + * The caller then treats a partially filled buffer as a good read, + * which surfaces later as an integrity failure rather than the I/O + * error it is -- and with anti-rollback on, the good lower-versioned + * slot is refused too. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-sdhci-uhs-recover.c b/tools/unit-tests/unit-sdhci-uhs-recover.c index 26783cb4..90e177ab 100644 --- a/tools/unit-tests/unit-sdhci-uhs-recover.c +++ b/tools/unit-tests/unit-sdhci-uhs-recover.c @@ -1,26 +1,12 @@ /* unit-sdhci-uhs-recover.c * - * Regression test: disk_read() called sdhci_uhs_recover() - * after any nonzero status from a data transfer, which permanently - * switched the host to 1.8V signaling (SDHCI_SRS15_V18SE) on a plain - * 3.3V cold boot whenever the first transfer failed for any reason - * (CRC, DMA, media, controller). If the retry also failed, the voltage - * setting was never rolled back and g_uhs_recovered prevented further - * recovery - so a transient error left the host with a signaling level - * the card is not using, for the rest of the boot and the stage that - * follows it. - * - * The test compiles the real src/sdhci.c (as sdhci_host.c, generated by - * the Makefile). The generated copy differs from the real file only in: - * - three `asm volatile` barrier/nop statements blanked (cannot - * assemble on x86); - * - the real sdhci_read() renamed to sdhci_read_hw() so this file can - * provide a scripted sdhci_read() that stands in for the command / - * data-transfer state machine. - * Register access goes through the platform-provided sdhci_reg_read()/ - * sdhci_reg_write(), backed here by a host register array, so the test - * asserts directly on the SDHCI_SRS15 / SDHCI_SRS11 state. + * Regression test: disk_read() called sdhci_uhs_recover() on any read + * error, switching the host to 1.8V signaling permanently even when + * the retry did not help. * + * The controller is scripted through a host register file and + * sdhci_read() returns a per-call result from a test-supplied script, + * so a chunk can fail, succeed on retry, or fail twice. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-sdhci-wait-busy.c b/tools/unit-tests/unit-sdhci-wait-busy.c index d48afa4f..3ae95eba 100644 --- a/tools/unit-tests/unit-sdhci-wait-busy.c +++ b/tools/unit-tests/unit-sdhci-wait-busy.c @@ -1,29 +1,13 @@ /* unit-sdhci-wait-busy.c * - * Regression test: sdhci_wait_busy() had no timeout in - * either its DATA0 polling loop or its repeated CMD13 loop (the - * in-code TODO acknowledged it). A removed card, a controller fault, - * or a card stuck in the programming state left wolfBoot spinning - * forever instead of returning an I/O error. - * - * The test compiles the real src/sdhci.c (as sdhci_host.c, generated - * by the Makefile: asm blanked, sdhci_read renamed to sdhci_read_hw) - * and scripts the controller through the host register file: - * - the DATA0 level (SDHCI_SRS09_DAT0_LVL) stays low for the - * data-line timeout case; - * - the command always "completes" (SDHCI_SRS12_CC preset) with the - * READY_FOR_DATA bit (bit 8) of the response register - * (SDHCI_SRS04) cleared, so every CMD13 reports DEVICE_BUSY. - * The emulated hal_get_timer_us() advances by a settable step per read, - * so the shipped SDHCI_WAIT_BUSY_TIMEOUT_MS is exercised as built -- - * a 1 ms step reaches the 30 s budget in ~30k iterations -- rather than - * against a shrunken stand-in value. Pre-fix, both timeout cases never - * return (the test runner's timeout kills them). - * - * The stub also counts sdhci_platform_wdt_pet() calls: a bounded wait - * this long must service the watchdog or it becomes a reset rather than - * the clean I/O error it is meant to be. + * Regression test: sdhci_wait_busy() had no timeout in either the DAT0 + * or the CMD13 loop, so a stuck or removed card spun forever. * + * The timer stub advances by a settable step, so the shipped + * SDHCI_WAIT_BUSY_TIMEOUT_MS is exercised as built rather than against + * a shrunken stand-in, and it counts sdhci_platform_wdt_pet() calls: a + * wait this long must service the watchdog or it becomes a reset + * instead of the clean I/O error it is meant to be. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-stm32l5-write.c b/tools/unit-tests/unit-stm32l5-write.c index 5ebb9ca1..36fb79b0 100644 --- a/tools/unit-tests/unit-stm32l5-write.c +++ b/tools/unit-tests/unit-stm32l5-write.c @@ -1,27 +1,18 @@ /* unit-stm32l5-write.c * - * Regression test: hal_flash_write() in hal/stm32l5.c - * looped while (i < len) but each iteration unconditionally read both - * 32-bit words of the 8-byte program unit (src[i>>2] and - * src[(i>>2)+1]) and programmed both, regardless of how many bytes - * remained. A write whose length is not a multiple of 8 therefore - * read up to 4 bytes past the caller's buffer (which the NSC update - * path validates for exactly len) and wrote those bytes to flash. + * Regression test: hal_flash_write() in hal/stm32l5.c read both words + * of the 8-byte program unit regardless of the remaining length, so a + * write not a multiple of 8 read up to 4 bytes past the caller's + * buffer and programmed them. * - * The test extracts the real hal_flash_wait_complete(), - * hal_flash_clear_errors() and hal_flash_write() from hal/stm32l5.c - * (generated by the Makefile) and runs them with the FLASH_SR/CR - * registers on a host register file and the destination flash at a - * host memory address pre-filled with stale data. The source buffer - * is followed by a canary: pre-fix, a short write copies the canary - * into the destination flash; post-fix the partial final unit is - * read-modify-written -- bytes outside [i, len) come from the current - * flash content and are stored back unchanged -- so nothing past len - * is read or changed. Both words are always stored inside one PG - * window, because the flash has no 32-bit program mode; that is a - * hardware sequencing property this host model cannot observe, so it - * is asserted by construction in hal/stm32l5.c, not here. + * The fix read-modify-writes the whole unit: bytes outside [i, len) + * come from flash and go back unchanged. Both words are always stored + * in one PG window -- the flash has no 32-bit program mode -- which + * this host model cannot observe, so that is asserted by construction + * in the HAL, not here. * + * Same harness as the STM32U5 twin: extracted functions, registers on + * a host file, stale destination flash, canary after the source. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-stm32u5-write.c b/tools/unit-tests/unit-stm32u5-write.c index 6ccbdd40..630c2883 100644 --- a/tools/unit-tests/unit-stm32u5-write.c +++ b/tools/unit-tests/unit-stm32u5-write.c @@ -1,30 +1,20 @@ /* unit-stm32u5-write.c * - * Regression test: hal_flash_write() in hal/stm32u5.c - * looped while (i < len) but each iteration unconditionally read all - * four 32-bit words of the 16-byte program unit (src[i>>2] through - * src[(i>>2)+3]) and programmed the full quadword regardless of the - * remaining length. A write whose length is not a multiple of 16 - * therefore read up to 12 bytes past the caller's buffer (validated - * for exactly len by the NSC update path) and wrote those bytes to - * flash. + * Regression test: hal_flash_write() in hal/stm32u5.c read all four + * words of the 16-byte program unit regardless of the remaining + * length, so a write not a multiple of 16 read up to 12 bytes past the + * caller's buffer and programmed them. * - * The fix read-modify-writes the whole 128-bit unit: bytes outside - * [i, len) come from the current flash content and are stored back - * unchanged, so the source is never read past len and no flash byte - * past len changes value. All four words are always stored, because - * the controller only starts the program on the fourth -- that part - * is a hardware sequencing property this host model cannot observe, - * so it is asserted by construction in hal/stm32u5.c, not here. - * - * The test extracts the real hal_flash_wait_complete(), - * hal_flash_clear_errors() and hal_flash_write() from hal/stm32u5.c - * (generated by the Makefile) and runs them with the FLASH_NS_SR/CR - * registers on a host register file and the destination flash at a - * 32-bit host address pre-filled with stale data. The source buffer - * is followed by a canary: pre-fix, a short write copies bytes past - * len into the destination flash. + * The fix read-modify-writes the whole unit: bytes outside [i, len) + * come from flash and go back unchanged. All four words are always + * stored -- the controller only starts the program on the fourth -- + * which this host model cannot observe, so that part is asserted by + * construction in the HAL, not here. * + * The real functions are extracted by the Makefile and run with the + * FLASH registers on a host register file and the destination flash + * pre-filled with stale data; a canary after the source buffer catches + * any read past len. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-t10xx-dts-memac.c b/tools/unit-tests/unit-t10xx-dts-memac.c index f1f6a83e..44c02498 100644 --- a/tools/unit-tests/unit-t10xx-dts-memac.c +++ b/tools/unit-tests/unit-t10xx-dts-memac.c @@ -1,19 +1,14 @@ /* unit-t10xx-dts-memac.c * - * Regression test: the fsl,fman-memac loop in - * hal_dts_fixup() (hal/nxp_t10xx.c) read the node's cell-index - * straight from the device tree and used it to index the file-static - * phydevs[5] with no bounds check - unlike the qman-portal loop right - * above it. Standard NXP FMan device trees assign the 10G MACs - * cell-index 8/9, so any such DTB read past the array and wrote the - * result into the kernel device tree as a MAC address. - * - * The test compiles the real src/fdt.c (wolfBoot's libfdt subset) and - * the real hal_dts_fixup() (extracted by the Makefile from - * hal/nxp_t10xx.c, along with its liodn/qman static tables), feeds it - * hand-built DTBs, and checks the resulting local-mac-address - * properties. + * Regression test: the fsl,fman-memac loop in hal_dts_fixup() + * (hal/nxp_t10xx.c) took the node's cell-index straight from the device + * tree and indexed the static phydevs[5] with it, unlike the qman-portal + * loop above. NXP device trees give the 10G MACs cell-index 8/9, so such + * a DTB read past the array and wrote the result back as a MAC address. * + * Compiles the real src/fdt.c and hal_dts_fixup() (extracted by the + * Makefile with its static tables), feeds it hand-built DTBs and checks + * the resulting local-mac-address properties. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-t10xx-qe-firmware.c b/tools/unit-tests/unit-t10xx-qe-firmware.c index 9e4cece1..76782d01 100644 --- a/tools/unit-tests/unit-t10xx-qe-firmware.c +++ b/tools/unit-tests/unit-t10xx-qe-firmware.c @@ -1,20 +1,16 @@ /* unit-t10xx-qe-firmware.c * - * Regression test: qe_check_firmware() in hal/nxp_t10xx.c - * validated the QE microcode header for self-consistency (magic, - * version, count, length == computed size) but never bounded the + * Regression test: qe_check_firmware() in hal/nxp_t10xx.c validated the + * microcode header for self-consistency but never bounded the * per-microcode code_offset against the declared image length. - * qe_upload_firmware() then reads code_offset + 4*count bytes from - * the firmware start and copies them into QE IRAM (and programs the - * microcode's trap table), so a structurally valid blob with an - * out-of-image offset makes wolfBoot copy arbitrary memory into the - * engine and run unauthenticated microcode with serial DMA enabled - * before the image is authenticated. - * - * The test extracts the real qe_* structures and qe_check_firmware() - * from hal/nxp_t10xx.c (generated by the Makefile) and builds the - * blobs in memory. The check-only path needs no register access. + * qe_upload_firmware() then copies code_offset + 4*count bytes into QE + * IRAM and programs the trap table, so a structurally valid blob with + * an out-of-image offset runs unauthenticated microcode with serial DMA + * enabled before the image is authenticated. * + * Extracts the qe_* structures and qe_check_firmware() (generated by + * the Makefile) and builds the blobs in memory; the check-only path + * needs no register access. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-t2080-fman-loader.c b/tools/unit-tests/unit-t2080-fman-loader.c index baf88557..8201d20b 100644 --- a/tools/unit-tests/unit-t2080-fman-loader.c +++ b/tools/unit-tests/unit-t2080-fman-loader.c @@ -1,20 +1,15 @@ /* unit-t2080-fman-loader.c * - * Regression test: hal_fman_init() in hal/nxp_t2080.c - * checked only the NOR window guard and the QEF magic, then copied - * fw->microcode[i].count words from fw + code_offset into FMan - * instruction RAM and marked the engine ready. No version, count, - * length self-consistency, or code-offset validation: a structurally - * valid-looking blob with an out-of-image offset made the loader - * copy arbitrary memory into FMan IRAM (and a count of zero was - * accepted as a successful "upload" of nothing). - * - * The test extracts the real qe_* structures and hal_fman_init() from - * hal/nxp_t2080.c (generated by the Makefile) and runs them against a - * test-owned NOR image (the FMAN slot sits at a nonzero offset in the - * bank, so the remaining-extent check is exercised) and an emulated - * FMan IRAM data register (set32/get32 on a host register array). + * Regression test: hal_fman_init() in hal/nxp_t2080.c checked only the + * NOR window guard and the QEF magic, then copied microcode words into + * FMan instruction RAM and marked the engine ready. With no version, + * count, length or code-offset validation, a valid-looking blob with an + * out-of-image offset copied arbitrary memory into IRAM, and a count of + * zero counted as a successful upload of nothing. * + * Extracts the qe_* structures and hal_fman_init() (generated by the + * Makefile) and runs them against a test-owned NOR image, with the FMAN + * slot at a nonzero bank offset so the extent check is exercised. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-ti-hercules-write.c b/tools/unit-tests/unit-ti-hercules-write.c index d83cec32..52c0ca6b 100644 --- a/tools/unit-tests/unit-ti-hercules-write.c +++ b/tools/unit-tests/unit-ti-hercules-write.c @@ -1,22 +1,16 @@ /* unit-ti-hercules-write.c * - * Regression test: the short-write path of hal_flash_write() - * in hal/ti_hercules.c checked only len < WRITE_BLOCK_SIZE, not whether - * (address % WRITE_BLOCK_SIZE) + len stayed inside the block. A short - * write starting near the end of a block copied past the end of the - * WRITE_BLOCK_SIZE stack staging buffer (memcpy(temp + off, data, len)) - * and programmed only the first block, losing the bytes intended for - * the next block. - * - * hal/ti_hercules.c needs the TI FAPI vendor headers (board build tree) - * and cannot be compiled on the host, but the bug is entirely in the - * staging logic of hal_flash_write(), so the Makefile extracts - * hal_flash_unlock_helper() and hal_flash_write() verbatim from - * hal/ti_hercules.c (ti_hercules_write_extract.h) and runs them here - * against emulated FAPI calls; Fapi_issueProgrammingCommand() performs - * the program into a host flash array, so the test asserts on the - * flash contents. + * Regression test: the short-write path of hal_flash_write() in + * hal/ti_hercules.c checked only len < WRITE_BLOCK_SIZE, not whether + * the write stayed inside the block. A short write near a block end + * copied past the staging buffer and programmed only the first block, + * losing the bytes meant for the next one. * + * The HAL needs the TI FAPI vendor headers and cannot be built on the + * host, but the bug is in the staging logic, so the Makefile extracts + * hal_flash_write() and its unlock helper and runs them against an + * emulated FAPI. The emulated FSM stays busy for a few polls and holds + * the data back until it drains, so a missing wait is observable. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-versal-ext-write.c b/tools/unit-tests/unit-versal-ext-write.c index 5383e02d..a96faac1 100644 --- a/tools/unit-tests/unit-versal-ext-write.c +++ b/tools/unit-tests/unit-versal-ext-write.c @@ -1,23 +1,15 @@ /* unit-versal-ext-write.c * - * Regression test: ext_flash_write() in hal/versal.c - * chunked the request into length-based FLASH_PAGE_SIZE transfers - * (page 0, page 1, ...) without accounting for the start address's - * offset inside the physical page. A write starting mid-page sent a - * full-page Page Program across the boundary; NOR wraps the write - * pointer at the page start, so the excess bytes clobbered the - * beginning of the page. - * - * hal/versal.c cannot be compiled on the host in its entirety for - * this test (the QspiDev_t typedef and the static qspi_* helpers are - * file-local), so the Makefile extracts the QspiDev_t struct and the - * real ext_flash_write() verbatim (versal_ext_write_extract.h) and - * runs them against test-owned qspiDev/qspi_initialized state and - * emulated qspi_write_enable()/qspi_transfer()/qspi_wait_ready()/ - * qspi_write_disable(). The emulated NOR models the real wrap: a - * crossing program overwrites the page start it wraps into, so - * pre-fix the corruption is observable in the flash image. + * Regression test: ext_flash_write() in hal/versal.c chunked by length + * alone, ignoring where the start address sat inside the physical page. + * A write starting mid-page sent a full-page Page Program across the + * boundary, and NOR wraps the write pointer, so the excess clobbered + * the start of the page. * + * The QspiDev_t typedef and the qspi_* helpers are file-local, so the + * Makefile extracts the struct and ext_flash_write() verbatim and runs + * them against emulated qspi_* calls. The emulated NOR models the wrap, + * so pre-fix the corruption is visible in the flash image. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-versal-qspi-dma.c b/tools/unit-tests/unit-versal-qspi-dma.c index 842737e2..29015fb4 100644 --- a/tools/unit-tests/unit-versal-qspi-dma.c +++ b/tools/unit-tests/unit-versal-qspi-dma.c @@ -1,25 +1,15 @@ /* unit-versal-qspi-dma.c * - * Regression test: the DMA RX path of qspi_transfer() in - * hal/versal.c. For an unaligned destination or a length not divisible - * by four, the transfer runs through the 4096-byte dma_tmpbuf. If the - * requested rxLen exceeded the temp buffer, dmaLen was truncated to - * sizeof(dma_tmpbuf) (so only that much was DMA'd), but the code then - * copied the full rxLen out with memcpy(rxData, dmaPtr, rxLen) - reading - * past the end of dma_tmpbuf and reporting the whole read as successful. - * - * The test compiles the real hal/versal.c (as versal_host.c, generated - * by the Makefile). The generated copy differs from the real file only - * in: - * - ARM asm statements blanked (cannot assemble on x86; the cache - * maintenance helpers become no-ops, which is harmless because the - * DMA is emulated below); - * - VERSAL_QSPI_BASE rebased onto g_vqspi_regs (real register - * offsets are preserved); - * - the real qspi_dma_wait() renamed to qspi_dma_wait_hw() so this - * file can provide a qspi_dma_wait() that performs the DMA move the - * hardware would do (flash -> GQSPIDMA_DST, GQSPIDMA_SIZE bytes). + * Regression test: the DMA RX path of qspi_transfer() in hal/versal.c. + * An unaligned or non-multiple-of-four read goes through a 4096-byte + * temp buffer; when rxLen exceeded it, dmaLen was truncated but the + * copy out still used the full rxLen, reading past the buffer and + * reporting success. * + * Compiles the real HAL via versal_host.c (generated by the Makefile), + * which differs only in that ARM asm is blanked or rewritten, the QSPI + * base is rebased onto a host array, and qspi_dma_wait() is renamed so + * this file can supply one that performs the DMA move. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-zynq-erase-loop.c b/tools/unit-tests/unit-zynq-erase-loop.c index deac7d5b..36e8a850 100644 --- a/tools/unit-tests/unit-zynq-erase-loop.c +++ b/tools/unit-tests/unit-zynq-erase-loop.c @@ -1,21 +1,15 @@ /* unit-zynq-erase-loop.c * - * Regression test: ext_flash_erase() in hal/zynq.c initializes - * idx once before the multi-sector while (len > 0) loop and never resets - * it, even though cmd is memset to zero each iteration. From the second - * sector on, the erase command is written at cmd[idx] (past the start of - * the buffer): in three-byte address mode the flash receives leading zero - * bytes before the opcode (malformed command, erase silently fails); in - * four-byte address mode the writes run past the end of the 8-byte cmd - * buffer. The loop also kept going - and could return success - after a - * failed write-enable, transfer, or ready wait. - * - * hal/zynq.c cannot be compiled on the host (it needs the Xilinx SDK - * headers, which are provided by the board build tree), but the bug is - * entirely in the ext_flash_erase() loop, so the Makefile extracts that - * one function verbatim from hal/zynq.c (zynq_erase_extract.h) and runs - * it here against emulated qspi_* functions that record every operation. + * Regression test: ext_flash_erase() in hal/zynq.c set idx once before + * the multi-sector loop and never reset it, though cmd is cleared each + * iteration. From the second sector on the opcode landed at cmd[idx]: + * malformed commands in three-byte address mode, writes past the 8-byte + * buffer in four-byte mode. The loop also continued, and could return + * success, after a failed write-enable, transfer or ready wait. * + * hal/zynq.c needs the Xilinx SDK headers and cannot be built on the + * host, so the Makefile extracts the one function and runs it against + * emulated qspi_* calls that record every operation. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. diff --git a/tools/unit-tests/unit-zynq-ext-write.c b/tools/unit-tests/unit-zynq-ext-write.c index 66a145ca..bc052ad9 100644 --- a/tools/unit-tests/unit-zynq-ext-write.c +++ b/tools/unit-tests/unit-zynq-ext-write.c @@ -1,20 +1,15 @@ /* unit-zynq-ext-write.c * - * Regression test: ext_flash_write() in hal/zynq.c divided - * the request into length-based FLASH_PAGE_SIZE chunks (page 0, page - * 1, ...) without accounting for the start address's offset inside - * the device page. A write starting mid-page sent a full-page Page - * Program across the boundary; NOR wraps the write pointer at the - * page start, so the excess bytes clobbered the beginning of the - * page. - * - * As in unit-zynq-erase-loop, hal/zynq.c cannot be compiled on the - * host (Xilinx SDK headers), so the Makefile extracts ext_flash_write() - * verbatim (zynq_write_extract.h) and runs it against emulated - * qspi_* functions. The emulated NOR models the real wrap: a Page - * Program crossing the boundary overwrites the page start it wraps - * into, so pre-fix the corruption is observable in the flash image. + * Regression test: ext_flash_write() in hal/zynq.c chunked by length + * alone, ignoring the start address's offset inside the device page. A + * write starting mid-page sent a full-page Page Program across the + * boundary, and NOR wraps the write pointer, so the excess clobbered + * the start of the page. * + * As with unit-zynq-erase-loop the HAL cannot be built on the host, so + * the Makefile extracts ext_flash_write() and runs it against emulated + * qspi_* calls. The emulated NOR models the wrap, so pre-fix the + * corruption is visible in the flash image. * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot.