Commit Graph

3738 Commits (master)

Author SHA1 Message Date
Daniele Lacamera faaad372cc Address minor PR 882 review nits
Cap the library fw_size clamp at UINT32_MAX (a > 4 GiB file would
otherwise truncate to a small value), say 'restore' not 'store' in the
scatter-restore error message, and print stdout as well as stderr in
the compile-check scripts on failure.
2026-09-04 16:39:26 +02:00
Daniele Lacamera b0209d0bf6 image: use uint64_t for ELF scatter program-header locals
paddr/filesz/offset were unsigned long, which is 32-bit on the
ELF-scatter targets (aurix-tc375, sim32). ELF64 program headers
truncate before the segment guards run, so filesz > UINT32_MAX is
never true and an out-of-range paddr wraps in-range. Use uint64_t to
match wolfBoot_check_flash_image_elf so the guards see untruncated
values.
2026-09-04 16:39:26 +02:00
Daniele Lacamera 01a71c7d39 x86_64_efi: return after LoadImage-failure panic under UNIT_TEST
panic() returns under UNIT_TEST, so the LoadImage-failure site fell
through to StartImage on a failed load. Add the return to match the
zero-size guard; on target panic() never returns, so behavior is
unchanged.
2026-09-04 15:52:27 +02:00
Daniele Lacamera 58e79daec7 F-11047: image: drop incorrect boot-partition destination check
The ELF scatter destination is the exec region, which sits outside the
boot partition that stores the signed ELF. Bounding it to the boot
partition rejected every legitimate segment (aurix exec is below boot,
sim scatter is above it) and bricked corruption recovery. The paddr is
covered by the image signature verified before this restore path and the
overflow check keeps it from wrapping, so no destination bound is needed;
this also matches the check function, which bounds no destination.
2026-09-04 15:52:19 +02:00
Daniele Lacamera 62918f2e90 F-12107: pkcs11 nsc: zero work buffer before it can be freed
If the snap allocation fails but the work allocation succeeds, the
initialisation loop never runs and nsc_tmpl_free() would release
indeterminate work[].pValue pointers. Zero work[] right after the
allocation (before the NULL check) so every path that reaches the
free hands out initialised, NULL pValue entries.
2026-09-04 14:53:15 +02:00
Daniele Lacamera d64f15af05 F-7392: stm32h7: document inherent OTP immutability in set_readonly
The H7 OTP memory is one-time programmable, so the keystore and UDS
are permanent once written; the H7 has no OTP block-lock register
(unlike the H5), so there is no write-protection step to perform.
Replace the misleading TODO with the reason the no-op is correct.
2026-09-04 14:33:29 +02:00
Daniele Lacamera b9068ce162 F-12105: tpm: keep keystore size as int in load_pubkey
keystore_get_size() returns -1 on invalid or oversized OTP slot
data; storing it in uint16_t made -1 become 65535, which passed the
hdrSz <= 0 check and was fed to the ECC/RSA parser as a 65535-byte
read from the keystore buffer. Keep it as int and reject values
<= 0 or above KEYSTORE_PUBKEY_SIZE.
2026-09-04 14:06:33 +02:00
Daniele Lacamera b5ab88a50a F-9744: store: reject negative length before unsigned arithmetic
wolfPKCS11/PSA_Store_Read/Write added int len to unsigned
in_buffer_offset before validation: a sufficiently negative len
wrapped, hit the truncation branch, and was replaced by the remaining
object or capacity bytes, bypassing Write's later len < 0 guard.
Reject len < 0 first in all four functions (post-clamp guard kept).
2026-09-04 13:56:55 +02:00
Daniele Lacamera a6628a324a F-12108: image: clamp sha block/peek to fw_size
get_sha_block() accepted offset == fw_size and always read
WOLFBOOT_SHA_BLOCK_SIZE bytes; wolfBoot_peek_image() always reported
the full block size. Both could hand callers a window past the end
of the image. Reject offset >= fw_size, clamp the external read to
the bytes remaining, and report the clamped size (0 when no bytes
remain). Hash callers already clamp their input, so this is API
hardening; pinned by test_peek_image_bounds (internal + ext cases).
2026-09-04 13:40:37 +02:00
Daniele Lacamera 0e2a463750 F-11047: image: validate ELF scatter segments before flash writes
wolfBoot_load_flash_image_elf() ignored read_flash_fwimage failures
and used the program-header fields unchecked, so a failed read
consumed indeterminate stack data and a malformed (but signed) ELF
could drive an out-of-bounds source read and erase/write at an
unintended destination.

Check the ELF header and program header reads, then validate each
PT_LOAD segment before copying: file_size fits a 32-bit length, the
source stays inside the manifest image, the paddr range fits the
destination address width, and the destination stays inside the boot
partition (mirrors the sibling check-function validation). Check the
copy result.

New unit tests in unit-image-elf-scatter.c cover the load path: a
valid restore (positive control) and rejections for source past
fw_size, paddr range overflow, destination outside the boot
partition, and a program header that cannot be read. The first three
fail pre-fix (the mock flash layer also catches the two
out-of-range destinations with its own address check); the phdr
read-failure case consumed uninitialized stack data pre-fix
(valgrind: conditional jump on uninitialised value at the
is_loadable check).
2026-09-04 11:53:12 +02:00
Daniele Lacamera 55711243c6 F-11026: image: declare wolfHSM DER sig length as word32
wc_ecc_rs_raw_to_sig() takes a word32* outlen, but the wolfHSM
client/server path in wolfBoot_verify_signature_ecc() declared the
buffer length as size_t and cast the pointer. On a 64-bit
big-endian target the API reads the high (zero) half, so the DER
conversion sees outlen 0 and the write-back lands in the wrong
half of the size_t.

Declare tmpSigSz as word32 and pass &tmpSigSz directly; this also
matches the word32 sigLen of wc_ecc_verify_hash() below.

Add a host compile check for the WOLFBOOT_ENABLE_WOLFHSM_CLIENT
build of image.c, which unit CI did not cover (only the PIC32CZ
cross build).
2026-09-04 11:24:13 +02:00
Daniele Lacamera 785a285ca7 F-11025: update_flash: document deliberately inert PCR extend block
The PCR-extension block in wolfBoot_unlock_disk() is guarded with
!defined(ARCH_SIM) while the function itself only builds for
ARCH_SIM, so it can never compile. The exclusion is deliberate
(eb2978ab: do not extend the unseal PCR on the simulator, or the
secret becomes un-unsealable), not an oversight.

Clarify the comment with the intended build scope instead of
removing the block: the code and the WOLFBOOT_NO_UNSEAL_PCR_EXTEND
option exist for the day the unlock-disk path is ported to a
non-sim target.
2026-09-04 11:13:32 +02:00
Daniele Lacamera eb3d587fe2 F-9746: x86_64_efi: fix do_boot prototype mismatch, drop unused param
boot_x86_64.c declared x86_64_efi_do_boot(uint8_t *) while the HAL
defines (uint32_t *, uint8_t *): the linker connected the incompatible
pair and the call was undefined behavior, mostly latent because the
second parameter was discarded. Match the AArch64 sibling: single
const uint32_t *boot_addr in the declaration, definition and call,
and remove the unused dts_address parameter.
2026-09-04 10:57:08 +02:00
Daniele Lacamera d244fd9b7e F-9745: x86_64_efi: inclusive mem path end, reject zero-size image
The UEFI MEMMAP_DEVICE_PATH EndingAddress is inclusive (last valid
byte), but x86_64_efi_do_boot() set it to boot_addr + size, describing
every image as one byte longer than it is; the AArch64 sibling already
uses size - 1. A zero-size image would underflow that computation and
hand an empty range to LoadImage, so reject it up front, as the
AArch64 sibling does. The local panic() returns to the unit test under
UNIT_TEST so the zero-size path is observable.
2026-09-04 10:44:34 +02:00
Daniele Lacamera a2d5543ebb F-9740: update_disk: FSP low-mem rejection falls back to other slot
The WOLFBOOT_FSP low-memory size check was the only per-slot rejection
in the boot retry loop that used a bare break, so an image whose
header-declared fw_size exceeded the tolum window aborted the boot
instead of trying the other slot. Every other rejection in the same
loop switches partitions and retries; match that.
2026-09-04 10:32:13 +02:00
Daniele Lacamera 77fe733e7f F-7391: library hal: reject undersized files, bound hash to loaded size
main() loaded a file of any size and parsed it as a manifest: header
fields (size, TLVs) were read past the end of the heap allocation, and
a header claiming a larger fw_size drove the image hash over an
unbounded range. Reject files smaller than IMAGE_HEADER_SIZE before
parsing and clamp fw_size to the bytes actually loaded.
2026-09-04 10:24:11 +02:00
Daniele Lacamera 88af5b6472 F-7390: x86 fsp: drop dead FSP auth scaffolding, fix comment
STAGE1_AUTH only authenticates the stage2 wolfBoot payload; the FSP-M and
FSP-S blobs are executed unverified. Remove the dead fsp_m/ret
declarations in start(), the orphaned .sig_fsp_s placeholder section
(no stage1 linker script places it), and correct the comment that
claimed the FSPs were authenticated. Add a compile check for the
STAGE1_AUTH variant (unit-x86-fsp-stage1auth-build.py), which the unit
test CI never builds for lack of an i686 toolchain.

Verification: full unit suite 1096 checks, 0 failures; both
STAGE1_AUTH variants of boot_x86_fsp.c compile clean; no references to
sig_fsp_s remain.
2026-09-04 10:00:54 +02:00
Daniele Lacamera 455c1c6cc6 F-7067: elf scatter: fix PART_IS_EXT arg and check load result
In the DISABLE_BACKUP branch of wolfBoot_update() the ELF-scatter restore
block passed the boot struct by value to the pointer-taking PART_IS_EXT
macro, so DISABLE_BACKUP + WOLFBOOT_ELF_FLASH_SCATTER + EXT_FLASH did not
even compile, and the load result was discarded. Mirror the
wolfBoot_start() pattern (PART_IS_EXT(&boot), panic on load failure), drop
the dead base local, and add a compile check for the combination
(unit-elf-scatter-db-build.py) guarding the one test that needs a
DISABLE_BACKUP-excluded symbol.

Verification: full unit suite 1096 checks, 0 failures; new test fails
pre-fix (struct vs pointer compile error), passes post-fix.
2026-09-04 09:52:22 +02:00
Daniele Lacamera f598b3fd41 F-6878: delta: reject non-multiple SECTOR_SIZE/DELTA_BLOCK_SIZE at build
The per-sector fill loop in wolfBoot_delta_update() advances in
DELTA_BLOCK_SIZE steps, so a WOLFBOOT_SECTOR_SIZE that is not a multiple
of DELTA_BLOCK_SIZE writes past the one-sector SWAP partition and
misaligns the resume path. Enforce the invariant with a #error and add a
negative build test (unit-delta-sector-align.py).

Verification: full unit suite 1096 checks, 0 failures; new test fails
pre-fix (misaligned config built), passes post-fix (build rejected).
2026-09-04 09:46:27 +02:00
David Garske 6883532da8
Merge pull request #880 from danielinux/fenrir-fixes-2026-09-01
Fenrir fixes 2026 09 01
2026-09-03 07:51:13 -07:00
Daniele Lacamera 931d224652 F-12065: unit test: clear the short-read mock before the assertion
PR review (wolfSSL/wolfBoot#880, Fenrir bot) flagged that
test_noramboot_ext_flash_short_read_rejected set the shared mock
globals mock_ext_flash_short_len/mock_ext_flash_short_bytes and only
cleared them after the ck_assert. The suite runs CK_NOFORK, so a
failing assertion longjmps out of the test and leaves every full-size
ext_flash_read truncated for test_noramboot_highversion_rollback_denied,
which then fails for an unrelated reason.

Clear the mock globals immediately after wolfBoot_start() returns,
before the assertion: the assert only checks wolfBoot_staged_ok, which
is set during wolfBoot_start(), so the ordering changes nothing about
what is tested and the mock state can no longer leak into the next
test on a failure path.

Verification: full unit suite in wolfboot-ci-sim (make -C
tools/unit-tests; make run) exit 0; unit-update-ram-noramboot 3/3.
2026-09-03 08:54:51 +02:00
David Garske 99725d278c
Merge pull request #878 from bigbrett/tc3-tweaks
tc3 quickfixes
2026-09-02 14:08:40 -07:00
Daniele Lacamera 80e92ea728 F-12065: update_ram: type-safe short-read check for -Wsign-compare
The F-12065 fix compared the int return of ext_flash_read() directly
against the uint32_t os_image.fw_size. That int-vs-unsigned comparison
triggers -Wsign-compare, which is a hard error under the default
-Werror -Wextra for every EXT_FLASH+NO_XIP update_ram target (e.g.
zynqmp) at -O0 and on host x86_64 gcc.

Check the error range explicitly and cast for the size comparison,
matching the established pattern in src/disk_fs.c (ret < 0 check
followed by (uint32_t)ret != len). Semantics are unchanged: negative
returns and positive short reads are both rejected.

Verified: host gcc -Werror -Wextra -fsyntax-only warns on the old
line and is clean on this one; unit-update-ram-noramboot 3/3.
2026-09-02 18:21:54 +02:00
Daniele Lacamera fa0c329a02 F-12064: p1021: drop already-delivered pages of a bad block
When the bad-block marker is found on the block's second page, the
first page has already been copied to the caller's buffer and counted
in pos; the skip advanced only the source address, so the bad block's
page stayed in the output and the read returned len with the bad
block's content mixed into the image.

Record the output position at the start of each erase block and, on a
bad block, rewind both pos and the data pointer to it before advancing
the source address. This preserves the data = original + pos
invariant (no out-of-bounds write) and discards the bad block's
delivered pages.

test_bad_marker_second_page_dropped now asserts the bad block is fully
skipped (output starts at the next block's first page); it fails
against the previous code.
2026-09-02 13:17:48 +02:00
Daniele Lacamera a504552883 unit test: match wolfBoot_get_dts_size mock to the 2-arg signature
95227f82 (fdt: rewrite device tree parser with capacity bound and full
validation) changed wolfBoot_get_dts_size() to take a capacity argument
but left the mock in unit-update-disk-fs.c with the old 1-arg
signature, so the test no longer compiles (conflicting types). Update
the mock; behavior is unchanged (always -1, no DTS in this test).
2026-09-02 12:19:13 +02:00
Daniele Lacamera 634d67904c F-12061: unit test: keep the XIP fast-path source in the flash model
PR review (wolfSSL/wolfBoot#880, Fenrir bot) flagged that
test_aligned_page_multiple_write_xip filled its 512-byte source at
offset 3 * FLASH_PAGE_SIZE (768) of the 1024-byte flash model, so
both the fixture and the HAL's XIP staging read 256 bytes past the
modeled region; it only passed because mmap rounds the mapping up to
a full page.

Move the source to pages 2-3 (offset 2 * FLASH_PAGE_SIZE) with the
destination at pages 0-1: non-overlapping, fully inside the model.

Verification: unit-rp2350-flash-write 6/6, full unit suite green.
2026-09-02 12:15:52 +02:00
Daniele Lacamera 5e80903103 F-12104: kontron tgl: correct the SPIBAR FREG/FPR register offsets
PR review (wolfSSL/wolfBoot#880, Fenrir bot) flagged that the
register offsets introduced by the F-12104 fix are wrong, and the
Linux kernel's Intel PCH SPI driver (drivers/spi/spi-intel.c)
confirms it:

  FDATA(n) = 0x10 + 4n  -> 0x48 is FDATA14, a scratch data register
  FRACC    = 0x50       -> not FREG0
  FREG(n)  = 0x54 + 4n  -> FREG0 = 0x54, FREG1 = 0x58
  FPR0-4   = 0x84-0x9C  (BXT/CNL protection-range base)

Two consequences. First, the BIOS range source: Intel flash region
numbering is region 0 = flash descriptor, region 1 = BIOS, and the
kernel driver's partition code reflects that ("start from the
mandatory descriptor region", then iterate FREG(1..)). The original
pre-F-12104 code read FREG1 (0x58) for the BIOS range and was right
on that point; the F-12104 fix regressed it to FREG0 (0x50), which
is the FRACC register. Restore FREG1.

Second, FPR0: the original 0x48 came from the buggy PCI-config-space
write path and is a FDATA scratch register in the SPIBAR map, so the
readback check passed on a register that never programs protection.
FPR0 is 0x84 (BXT/CNL PR base; JSL is not in the kernel's platform
table but follows the same-generation layout). HSFSTS_CTL at 0x04
and the RPE (bit 15) / WPE (bit 31) / base / limit fields match the
kernel's PR_ definitions and are unchanged.

unit-kontron-tgl-spi.c: mirror the corrected offsets (FREG0 decoy at
0x54, FREG1 BIOS source at 0x58, FPR0 at 0x84) and assert FPR0
carries the FREG1 range; the test runs the real extracted
tgl_lock_bios_region(), so it fails if the HAL offsets drift.

Verification: unit-kontron-tgl-spi 4/4, full unit suite green, sim
build green, cstyle clean on changed hunks.
2026-09-02 12:15:51 +02:00
Daniele Lacamera 65cab0a0a7 F-12104: unit test: model the TGL SPI MMIO as 32-bit registers
The Kontron TGL SPI regression test kept the MMIO shadow in a
uint8_t array and reached into it through uint32_t * casts. The
accesses are all 32-bit at 4-aligned register offsets (0x04, 0x48,
0x50, 0x58), so the model is now a uint32_t array indexed by
offset/4: no casts, no alignment or strict-aliasing doubt, and the
redundant byte-clear before the 32-bit FREG0 store is gone.

Verification: unit-kontron-tgl-spi 4/4, full unit suite green,
cstyle clean.
2026-09-02 12:15:51 +02:00
Daniele Lacamera 1c57f8c06b F-12114: pkcs11: wipe the PIN even when no session was established
pkcs11_pin is pre-populated from the compile-time credential
(ENCRYPT_PKCS11_PIN), so the RAM copy exists from image load, not
from a successful C_Login. pkcs11_crypto_deinit() only wiped it
inside the encrypt_initialized branch, so on a target where init
never completed the credential stayed in retained memory after the
pre-handoff path ran.

Move pkcs11_pin_wipe() out of the branch: the token interaction
(C_CloseSession) stays conditional on an established session, the
credential wipe is unconditional. deinit only runs on the terminal
pre-handoff paths, so this cannot break the init retry in
wolfBoot_initialize_encryption, which runs at decryption time,
well before handoff.

test_pkcs11_deinit_no_session now re-populates the pin and asserts
every byte is zero after deinit without init (plus no C_CloseSession
and repeat-call safety). Pre-fix it failed with "pkcs11_pin byte 0
not wiped" (1/2); post-fix 2/2.

Verification: unit-pkcs11-pin-zeroize 2/2, full unit suite green,
sim build green, cstyle clean on changed hunks.
2026-09-02 12:15:51 +02:00
Daniele Lacamera b548341e9b F-12114: pkcs11: wipe the login PIN on all pre-handoff paths
pkcs11_crypto_deinit() - the only caller of pkcs11_pin_wipe() - was
invoked from the update_flash path alone (src/update_flash.c:1715).
On the RAMBOOT, hwswap and disk pre-handoff paths the PKCS#11 login
credential stayed in retained bootloader memory after handoff.

Add the same #ifdef ENCRYPT_PKCS11 deinit block after the WOLFHSM
cleanup in src/update_ram.c, src/update_flash_hwswap.c and
src/update_disk.c, in the same position as the existing update_flash
call (before hal_flash_protect/hal_prepare_boot). update_ram.c and
update_flash_hwswap.c did not include encrypt.h, where
pkcs11_crypto_deinit() is declared - add the include (update_flash.c
and update_disk.c already had it). The deinit is a no-op when crypto
was never initialized, so the calls are safe on every build.

Verification: full build with PKCS11 enabled (sim config +
CFLAGS_EXTRA: ENCRYPT_PKCS11, EXT_ENCRYPTED, EXT_FLASH,
WOLFCRYPT_SECURE_MODE, SECURE_PKCS11, WOLFPKCS11_USER_SETTINGS +
mechanism/sizes/PIN) compiles all sources cleanly; the link stops on
pre-existing externals (token library + secure-mode wolfssl objects
that a real target's link config supplies) - a control build of the
unpatched tree fails identically with the same undefined-symbol set.
sim and kontron_vx3060_s2 builds green (PKCS11 disabled, hunks
inactive). cstyle clean on the changed hunks.
2026-09-02 12:15:51 +02:00
Daniele Lacamera 6dabdad01e F-12114: pkcs11: wipe the login PIN in crypto deinit
pkcs11_pin is a file-scope copy of the compile-time credential
passed to C_Login() for the token holding the firmware-decryption
key. pkcs11_crypto_deinit() runs on the pre-handoff path but only
closed the session, leaving the credential in retained bootloader
memory where a post-handoff attacker could recover it and
authenticate to the token.

Wipe the copy (volatile zeroize) after the final C_CloseSession().
No re-init path exists after deinit in the product flow (init is
only called from the verification paths), so wiping inside the
deinit is safe.

Add unit-pkcs11-pin-zeroize: a full init/deinit cycle with a
stubbed PKCS#11 backend that asserts the pin copy is all zero
after deinit and the session was closed, plus a no-session deinit
safety case.

Verification: unit-pkcs11-pin-zeroize 1/2 pre-fix (pin byte 0 not
wiped), 2/2 post-fix; full unit suite green; sim build green;
kontron_vx3060_s2 CI build green.
2026-09-02 12:15:51 +02:00
Daniele Lacamera 0da41145ea F-12066: pci: restore original COMMAND and clear windows on bridge error
pci_program_bridge() used orig_cmd both as the saved COMMAND
register value and as the accumulator for the decode bits enabled
while programming. Error paths after a window was programmed
(post-enum MMIO or IO alignment failures) restored that mutated
value and left the programmed bridge windows active, so the bridge
kept decoding address ranges the allocator rollback had just
returned.

Keep the two values separate: saved_cmd holds the original
register content for the error path, new_cmd accumulates the
decode bits and is written on success (seeded from saved_cmd, so
the success path preserves the bits it did not manage, exactly as
before). The error path now disables every bridge window
(prefetch, MMIO, IO) before restoring saved_cmd.

Test: test_program_bridge_oom_late_restore programs a prefetch
window behind the bridge, then exhausts the MMIO pool so the
post-enum MMIO alignment fails. Pre-fix the restored COMMAND was
0x0006 (original 0x0004 plus the MEM_SPACE bit for the discarded
window) and the prefetch window stayed programmed
(0x9000-0x900F); post-fix the original COMMAND is restored and
all windows are disabled.
2026-09-02 12:15:51 +02:00
Daniele Lacamera 7736d975f1 F-12060: multiboot2: terminate the boot info tag list with an end tag
mb2_build_boot_info_header() emitted the requested tags and set
total_size without appending the Multiboot2 end tag (type 0,
size 8). A strict consumer walking the output section sees no
terminator inside total_size (or a zero-sized pseudo-terminator
only if the destination buffer happens to be zero-filled), so
the handoff is structurally invalid.

Reserve eight bytes for the end tag after the requested tags,
write {type 0, flags 0, size 8}, and include it in total_size.
The write is bounds-checked against the caller's max_size like
the other tag builders, and idx is already 8-byte aligned since
every tag size is a multiple of 8.

Tests: the existing layout assertions now expect the end tag
inside total_size (basic mem info: 24 -> 32, mem map with one
entry: 48 -> 56), and a new consumer-style test walks the
generated output section the way a strict Multiboot2 consumer
would: both requested tags found, well-formed end tag (type 0,
size 8), and the end tag is the last structure in total_size.
Pre-fix: 3 failures (total_size short by 8, walk finds no end
tag); post-fix: 36/36.
2026-09-02 12:15:51 +02:00
Daniele Lacamera c55795b5a4 F-12065: update_ram: reject short ext flash reads on the RAM load
The non-RAMBOOT copy-to-RAM path only rejected negative
ext_flash_read() results. Backends return the number of bytes
read on success (filesystem.c forwards XFREAD's count), so a
positive short read was accepted and boot continued with a
truncated RAM image.

Require the read to return exactly os_image.fw_size; any other
result aborts the boot, matching the header-copy check already
in the same file (ret != IMAGE_HEADER_SIZE).

Note: the RAMBOOT image-load path (WOLFBOOT_USE_RAMBOOT) has the
same `ret < 0` pattern on its img_size read; outside this
finding's scope, left as-is.

Test: unit-update-ram-noramboot gains a short-read case (mock
ext_flash_read withholds 1 byte from the full-image copy only).
Pre-fix the truncated image staged for boot (staged_ok == 1);
post-fix the boot is aborted with "Error loading image ...
(ret 5299)".
2026-09-02 12:15:51 +02:00
Daniele Lacamera 4c447f4bf9 F-12104: Kontron TGL: apply the SPI BIOS-region lock through the BAR
tgl_lock_bios_region() wrote the protected range and the FLOCKDN
value through PCI configuration space (offsets 0x48 and 0x04, the
status/command dword) instead of the SPI controller's
memory-mapped registers at the BAR0 base, and took the range from
FREG1 (non-BIOS) instead of FREG0 (BIOS). The lock now writes
FPR0 and BIOS/H SFSTS/CTL through mmio_write32(), verifies both
by readback, and returns an error if the bits do not stick.

The helper had no callers: no hal_flash_protect() override
existed, so the weak no-op default ran before handoff and the
BIOS region stayed writable. Add the override routing to
tgl_lock_bios_region().

Including <hal.h> for the hook signature also exposes the
hal_flash_write/hal_flash_erase stubs as mismatching the HAL
contract; fix their address parameter to haladdr_t.

Add unit-kontron-tgl-spi: runs the extracted
tgl_lock_bios_region() and hal_flash_protect() against mocked
PCI config space and an MMIO array at the BAR address (build
fails pre-fix - hal_flash_protect undefined - 4/4 pass
post-fix).
2026-09-02 12:15:51 +02:00
Daniele Lacamera 20c9031965 F-12064: P1021: check bad-block markers per erase block
ext_flash_read() initialized its bad-block page counter once per
request, so the marker was inspected only on the first two pages
read and a bad erase block later in the request was delivered as
valid data. Restart the counter at the start of each erase block.

The skip path also rewound the logical position to a block
boundary without rewinding the output pointer, so a marker found
after some pages had been delivered continued the read past the
end of the caller's buffer. pos and data already agree
(data = original + pos) after any delivered pages, so the skip
only advances the source address.

Add unit-p1021-read-badblock: runs the extracted
ext_flash_read() against a mocked ELBC on a simulated NAND with
three 16 KiB blocks (2/5 checks fail pre-fix, 5/5 pass
post-fix).
2026-09-02 12:15:51 +02:00
Daniele Lacamera 6792b5da01 F-12063: STM32WB: require a full double word in the fast write path
The double-word fast path was selected on 'len - i > 3' but always
reads and programs two 32-bit words, so an aligned 4-7 byte tail
read up to four bytes past the caller's buffer and programmed them
into flash. Require at least eight remaining bytes; shorter tails
fall to the RMW branch, which rewrites the unit with the
out-of-range bytes read back from flash.

Add unit-stm32wb-write: runs the extracted hal_flash_write()
against a host register file with stale destination flash and a
source canary (3/5 checks fail pre-fix, 5/5 pass post-fix).
2026-09-02 12:15:51 +02:00
Daniele Lacamera 40d448b771 F-12062: STM32L4: require a full double word in the fast write path
The double-word fast path of hal_flash_write() was selected on
"len - i > 3" but always reads and programs two 32-bit words, so
an aligned 4-7 byte tail read up to four bytes past the caller's
buffer and programmed them into flash. Require at least eight
remaining bytes before taking the fast path; shorter tails fall to
the RMW branch, which rewrites the unit with the out-of-range bytes
read back from flash. Same fix as the STM32G4 twin (F-11023).

Add unit-stm32l4-write: runs the extracted hal_flash_write()
against a host register/flash model with a canary after the source
(3/5 checks fail pre-fix, 5/5 pass post-fix).
2026-09-02 12:15:51 +02:00
Daniele Lacamera 5c22c271e8 F-12061: RP2350: RMW partial pages in hal_flash_write
flash_range_program() requires a page-aligned address and a
page-multiple length (pico-sdk ROM, invalid_params_if on both). The
partition-state path without NVM_FLASH_WRITEONCE issues 1-byte
(trailer) and 4-byte (magic) writes that violated the contract on
every state transition.

Keep the direct-program fast path for page-aligned page-multiple
writes; otherwise read the page back from XIP, merge the write, and
program the full page. The AND program keeps the trailer flag
accumulation intact.

Add unit-rp2350-flash-write: runs the extracted hal_flash_write
against a mock flash_range_program() that enforces the ROM contract
(4/6 checks fail pre-fix, 6/6 pass post-fix).
2026-09-02 12:15:51 +02:00
David Garske 95227f823c fdt: rewrite device tree parser with capacity bound and full validation 2026-09-02 10:48:52 +02:00
David Garske 664b09a075 update_disk: load signed image from a file when a filesystem is present 2026-09-02 10:47:33 +02:00
David Garske 1091fc6d45 ext4: add read-only extent-based ext4 filesystem support 2026-09-02 10:47:33 +02:00
David Garske f06f081a20 disk_fs: add read-only FAT32 filesystem support for disk boot 2026-09-02 10:47:33 +02:00
David Garske 336ee7fd56 gpt: parse MBR and GPT byte-wise so disk boot works big-endian 2026-09-02 10:47:33 +02:00
Brett Nicholas 1c2d5ee2c5 add missing DMA objects 2026-09-01 09:19:36 -06:00
Brett Nicholas 626db410be static library docs 2026-09-01 09:19:36 -06:00
Marco Oliverio 9fc5f75234 ci: add pic32cz stub build 2026-08-28 13:11:26 +02:00
Marco Oliverio 11257c8006 image: abide r/s unused var in wolfHSM build 2026-08-28 13:11:26 +02:00
Marco Oliverio 70919f3107 image.c: mldsa: don't discard return value 2026-08-27 11:34:48 +02:00
David Garske 9bf7d21971
Merge pull request #874 from danielinux/fenrir-fixes-2026-08-26
Fenrir fixes 2026 08 26
2026-08-26 18:06:18 -04:00