From 284978ad4eeb9cf1a042ccff27cb88fe9b939700 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 14:02:34 -0700 Subject: [PATCH 01/13] tools: add an x86 FSP UPD decoder driven by the FspmUpd.h offset comments --- tools/x86_fsp/decode_fsp_upd.py | 204 ++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100755 tools/x86_fsp/decode_fsp_upd.py diff --git a/tools/x86_fsp/decode_fsp_upd.py b/tools/x86_fsp/decode_fsp_upd.py new file mode 100755 index 0000000000..5401bcf31f --- /dev/null +++ b/tools/x86_fsp/decode_fsp_upd.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +"""Decode an Intel FSP UPD block into named fields. + +The UPD region is a flat C struct whose only authoritative layout is the +FspmUpd.h / FspsUpd.h that ships with the FSP, where each member is annotated +with its byte offset. This parses that header and uses it to decode either the +hex block WOLFBOOT_DUMP_FSP_UPD prints over the UART, or a UPD region in a +flash image. --diff reports only the fields that differ between two captures. + +Examples: + decode_fsp_upd.py --header include/x86/fsp/FspmUpd.h --log boot.log + decode_fsp_upd.py --header ... --bin image.bin --at 0x1F3A384 + decode_fsp_upd.py --header ... --diff ours.txt intel.txt +""" + +import argparse +import re +import struct +import sys + +# Offsets in these headers are relative to the start of the outer UPD struct +# (FSPM_UPD), not to the inner config struct, so a single flat table works. +# The declaration must immediately follow its comment, or a struct-typed member +# in between (FSPM_UPD has three) mis-pairs the offset with the next scalar. +DECL = re.compile( + r"/\*\*\s*Offset\s+(0x[0-9A-Fa-f]{4})\s*-?\s*([^\n*]*?)\s*\n" # offset + title + r"(.*?)" # body + r"\*\*/[ \t]*\n" + r"[ \t]*(\w+)[ \t]+(\w+)[ \t]*(?:\[\s*(\d+)\s*\])?[ \t]*;", + re.S) + +WIDTH = {"UINT8": 1, "UINT16": 2, "UINT32": 4, "UINT64": 8, + "INT8": 1, "INT16": 2, "INT32": 4, "INT64": 8} +FMT = {1: "B", 2: " nxt: + problems.append(" overlap: %s ends 0x%04X, next (%s) starts 0x%04X" + % (name, end, fields[i + 1][2], nxt)) + return problems + + +def hex_from_log(path): + """Pull every UPD hex dump out of a wolfBoot console capture.""" + blocks, cur = [], [] + for raw in open(path, "r", errors="replace"): + line = raw.strip() + if re.fullmatch(r"[0-9A-Fa-f]{2,32}", line) and len(line) % 2 == 0: + cur.append(line) + else: + if cur: + blocks.append("".join(cur)) + cur = [] + if cur: + blocks.append("".join(cur)) + # A stray hex-looking log line is not a dump; keep only plausible ones. + return [bytes.fromhex(b) for b in blocks if len(b) >= 256] + + +def value_of(data, off, ctype, count): + w = WIDTH[ctype] + if off + w * count > len(data): + return None + if count == 1: + return struct.unpack_from(FMT[w], data, off)[0] + return [struct.unpack_from(FMT[w], data, off + i * w)[0] for i in range(count)] + + +def fmt_value(v): + if isinstance(v, list): + if len(v) > 16: + return "[" + ", ".join(str(x) for x in v[:16]) + ", ...]" + return "[" + ", ".join(str(x) for x in v) + "]" + return str(v) + + +def decode(fields, data, show_all): + out = [] + for off, ctype, name, count, title in fields: + v = value_of(data, off, ctype, count) + if v is None: + continue + if not show_all and name.startswith(("UnusedUpdSpace", "Reserved")): + continue + out.append("0x%04X %-40s %s" % (off, name, fmt_value(v))) + return out + + +def load_decoded(path): + """Read a previously written decode for --diff.""" + vals = {} + for line in open(path, "r", errors="replace"): + m = re.match(r"0x([0-9A-Fa-f]{4})\s+(\S+)\s+(.*)", line.rstrip()) + if m: + vals[m.group(2)] = (m.group(1), m.group(3)) + return vals + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--header", help="FspmUpd.h / FspsUpd.h to take the layout from") + ap.add_argument("--log", help="wolfBoot console capture containing a hex dump") + ap.add_argument("--bin", help="binary file holding a UPD region") + ap.add_argument("--at", help="offset of the UPD inside --bin (hex or decimal)") + ap.add_argument("--which", type=int, default=-1, + help="which dump from --log to decode (default: the last)") + ap.add_argument("--all", action="store_true", + help="include Reserved/UnusedUpdSpace padding") + ap.add_argument("--diff", nargs=2, metavar=("A", "B"), + help="diff two files previously produced by this tool") + args = ap.parse_args() + + if args.diff: + a, b = load_decoded(args.diff[0]), load_decoded(args.diff[1]) + common = [n for n in a if n in b] + names = [n for n in common if a[n][1] != b[n][1]] + only = [n for n in a if n not in b] + [n for n in b if n not in a] + print("%-40s %-28s %s" % ("FIELD", args.diff[0], args.diff[1])) + for n in sorted(names, key=lambda n: a[n][0]): + print("%-40s %-28s %s" % (n, a[n][1], b[n][1])) + print("\n%d of %d common fields differ" % (len(names), len(common))) + if only: + print("fields present in only one side: %s" % ", ".join(sorted(only))) + return 0 + + if not args.header: + ap.error("--header is required unless --diff is used") + fields = parse_header(args.header) + if not fields: + print("no offset-annotated fields found in %s" % args.header, file=sys.stderr) + return 1 + problems = check_layout(fields) + if problems: + print("WARNING: parsed layout is not self-consistent:", file=sys.stderr) + for p in problems[:10]: + print(p, file=sys.stderr) + + if args.log: + blocks = hex_from_log(args.log) + if not blocks: + print("no UPD hex dump found in %s" % args.log, file=sys.stderr) + return 1 + idx = args.which % len(blocks) + print("# %s: %d dump(s) found, decoding #%d (%d bytes)" + % (args.log, len(blocks), idx, len(blocks[idx]))) + data = blocks[idx] + elif args.bin: + raw = open(args.bin, "rb").read() + at = int(args.at, 0) if args.at else 0 + data = raw[at:at + 0x1000] + print("# %s at 0x%X" % (args.bin, at)) + else: + ap.error("one of --log, --bin or --diff is required") + + sig = data[:8].decode("ascii", "replace") + print("# signature %r size %d bytes" % (sig, len(data))) + for line in decode(fields, data, args.all): + print(line) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From f3dab6193f1d1bce3b8a8d250979124120d0aa05 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 14:56:15 -0700 Subject: [PATCH 02/13] x86 FSP: honour NotifyPhase reset requests, including the Tiger Lake global reset --- include/x86/common.h | 3 +++ include/x86/fsp.h | 12 +++++++++++ src/boot_x86_fsp.c | 3 +-- src/x86/common.c | 27 ++++++++++++++++++++++++ src/x86/fsp.c | 49 +++++++++++++++++++++++++++++++++----------- 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/include/x86/common.h b/include/x86/common.h index 5a40701226..954ae07a6f 100644 --- a/include/x86/common.h +++ b/include/x86/common.h @@ -60,6 +60,9 @@ uint16_t io_read16(uint16_t port); void io_write32(uint16_t port, uint32_t value); uint32_t io_read32(uint16_t port); void reset(uint8_t warm); +#ifdef WOLFBOOT_TGL +int global_reset(void); +#endif void delay(int msec); __attribute__((noreturn)) void panic(void); void cpuid(uint32_t eax_param, diff --git a/include/x86/fsp.h b/include/x86/fsp.h index ef7cf21fca..b404f2689e 100644 --- a/include/x86/fsp.h +++ b/include/x86/fsp.h @@ -29,4 +29,16 @@ int fsp_get_image_revision(struct fsp_info_header *h, int *build, void print_fsp_image_revision(struct fsp_info_header *h); void fsp_init_silicon(void); +/* Reset requests (not failures) from FspMemInit and NotifyPhase. */ +#define FSP_STATUS_RESET_REQUIRED_COLD 0x40000001 +#define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002 +/* Codes 3..8 are platform-defined. On Intel client SoCs FSP-S returns _3 to + * request a global reset (host + CSME), needed for the ChipsetInit sync. */ +#define FSP_STATUS_RESET_REQUIRED_3 0x40000003 +#define FSP_STATUS_RESET_REQUIRED_4 0x40000004 +#define FSP_STATUS_RESET_REQUIRED_5 0x40000005 +#define FSP_STATUS_RESET_REQUIRED_6 0x40000006 +#define FSP_STATUS_RESET_REQUIRED_7 0x40000007 +#define FSP_STATUS_RESET_REQUIRED_8 0x40000008 + #endif /* FSP_H */ diff --git a/src/boot_x86_fsp.c b/src/boot_x86_fsp.c index 5b4596f1e9..79b5694ba5 100644 --- a/src/boot_x86_fsp.c +++ b/src/boot_x86_fsp.c @@ -78,8 +78,7 @@ const uint8_t __attribute__((section(".sig_wolfboot_raw"))) /* offset of the header from the base image */ #define FSP_INFO_HEADER_OFFSET 0x94 #define EFI_SUCCESS 0x0 -#define FSP_STATUS_RESET_REQUIRED_COLD 0x40000001 -#define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002 +/* FSP_STATUS_RESET_REQUIRED_* are defined in x86/fsp.h */ #define MEMORY_4GB (4ULL * 1024 * 1024 * 1024) #define ENDLINE "\r\n" /* Standard PCI capabilities live in conventional config space at 0x40-0xFC, diff --git a/src/x86/common.c b/src/x86/common.c index 7e0db43797..1da8926c5f 100644 --- a/src/x86/common.c +++ b/src/x86/common.c @@ -251,6 +251,33 @@ void reset(uint8_t warm) while(1){}; } +#ifdef WOLFBOOT_TGL +/* PMC ETR3 (PWRMBASE 0xFE000000 + 0x1048). CF9_GLB_RST makes the next 0xCF9 + * full reset a global reset (host + CSME); CF9_LOCK makes ETR3 read-only. */ +#define TGL_PCH_PWRM_BASE 0xFE000000u +#define TGL_PWRM_ETR3 0x1048u +#define ETR3_CF9_GLB_RST (1u << 20) +#define ETR3_CF9_LOCK (1u << 31) + +/* Global reset (host + CSME), which FSP-S needs after a ChipsetInit sync. + * Returns non-zero if ETR3 is locked and the global bit could not be set; + * otherwise does not return. */ +int global_reset(void) +{ + volatile uint32_t *etr3 = + (volatile uint32_t *)(uintptr_t)(TGL_PCH_PWRM_BASE + TGL_PWRM_ETR3); + uint32_t v; + + v = *etr3; + if ((v & ETR3_CF9_LOCK) != 0) { + return -1; + } + *etr3 = v | ETR3_CF9_GLB_RST; + reset(0); + return 0; +} +#endif /* WOLFBOOT_TGL */ + /** * @brief Delay the execution for a specified number of milliseconds. * diff --git a/src/x86/fsp.c b/src/x86/fsp.c index 84787f65bb..bc27fa350a 100644 --- a/src/x86/fsp.c +++ b/src/x86/fsp.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include +#include #include #include #include @@ -99,6 +100,39 @@ void print_fsp_image_revision(struct fsp_info_header *h) wolfBoot_printf("%x.%x.%x build %x\r\n", maj, min, rev, build); } +/* Act on a reset request returned by an FSP NotifyPhase call. WARM/COLD do the + * matching reset; RESET_REQUIRED_3 is the Tiger Lake global reset (host + CSME) + * needed after the FSP-S ChipsetInit sync. A non-success, non-reset status is + * fatal. Returns only on EFI_SUCCESS. */ +static void notify_phase_handle_reset(uint32_t status) +{ + if (status == FSP_STATUS_RESET_REQUIRED_WARM) { + wolfBoot_printf("notify phase: warm reset required\n"); + reset(1); + } + if (status == FSP_STATUS_RESET_REQUIRED_COLD) { + wolfBoot_printf("notify phase: cold reset required\n"); + reset(0); + } + if (status == FSP_STATUS_RESET_REQUIRED_3) { + wolfBoot_printf("notify phase: global reset required\n"); +#ifdef WOLFBOOT_TGL + /* global_reset() returns only if ETR3 is locked and the global bit + * could not be armed. A plain reset would not satisfy the FSP-S + * request and would loop forever, so halt instead (fail-secure). */ + global_reset(); + wolfBoot_printf("ETR3 is locked, cannot arm a global reset\n"); + panic(); +#else + reset(0); +#endif + } + if (status != EFI_SUCCESS) { + wolfBoot_printf("notify phase failed %d\n", status); + panic(); + } +} + void fsp_init_silicon(void) { uint8_t silicon_init_parameter[FSP_S_PARAM_SIZE]; @@ -167,20 +201,11 @@ void fsp_init_silicon(void) notify_phase = _start_fsp_s + notify_phase_off; param.Phase = EnumInitPhaseAfterPciEnumeration; status = x86_run_fsp_32bit(notify_phase, ¶m); - if (status != EFI_SUCCESS) { - wolfBoot_printf("notify phase failed %d\n", status); - panic(); - } + notify_phase_handle_reset(status); param.Phase = EnumInitPhaseReadyToBoot; status = x86_run_fsp_32bit(notify_phase, ¶m); - if (status != EFI_SUCCESS) { - wolfBoot_printf("notify phase failed %d\n", status); - panic(); - } + notify_phase_handle_reset(status); param.Phase = EnumInitPhaseEndOfFirmware; status = x86_run_fsp_32bit(notify_phase, ¶m); - if (status != EFI_SUCCESS) { - wolfBoot_printf("notify phase failed %d\n", status); - panic(); - } + notify_phase_handle_reset(status); } From 78faf3ce3344652590f1af74f633407349d3ea4a Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 14:56:37 -0700 Subject: [PATCH 03/13] x86 FSP: add the 64-bit Linux boot protocol to the bzImage loader --- src/x86/linux_loader.c | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/x86/linux_loader.c b/src/x86/linux_loader.c index 2dcdbdf251..99527c815b 100644 --- a/src/x86/linux_loader.c +++ b/src/x86/linux_loader.c @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA * * - * Linux x86 32bit protocol implementation + * Linux x86 boot protocol implementation (32-bit and 64-bit entry) */ #include @@ -27,6 +27,9 @@ #include #include +#ifdef WOLFBOOT_64BIT +#include +#endif #ifdef WOLFBOOT_FSP #include @@ -35,22 +38,32 @@ #define ENDLINE "\r\n" -#ifdef WOLFBOOT_64BIT -#error "Linux loader 64bit is not supported" -#endif -static void jump_to_linux(uint32_t kernel_addr, struct boot_params *p) -{ - (void)kernel_addr; - (void)p; +/* XLF_KERNEL_64: the kernel has a 64-bit entry point (boot protocol >= 2.12, + * xloadflags bit 0). Without it a 64-bit loader has nothing to jump to. */ +#define XLF_KERNEL_64 (1u << 0) -#if !defined(WOLFBOOT_64BIT) +static void jump_to_linux(uint64_t kernel_addr, struct boot_params *p) +{ +#if defined(WOLFBOOT_64BIT) + /* 64-bit boot protocol: in long mode already, so pass the zero page in RSI, + * clear RDI as the protocol requires, and jump to the 64-bit entry (0x200 + * past the loaded kernel), interrupts off. The kernel sets up its own GDT + * and stack. */ + __asm__ __volatile__("cli\n\t" + "movq %0, %%rsi\n\t" + "xorl %%edi, %%edi\n\t" + "jmp *%1" + : + : "r"(p), "r"(kernel_addr) + : "rsi", "rdi", "memory"); +#else __asm__ __volatile__("movl %0, %%esi\n\t" "xorl %%ebp, %%ebp\n\t" "xorl %%edi, %%edi\n\t" "xorl %%ebx, %%ebx\n\t" "jmp *%1" : - : "r"(p), "r"(kernel_addr) + : "r"((uint32_t)p), "r"((uint32_t)kernel_addr) : "esi", "edi", "ebx"); #endif /* WOLFBOOT_64BIT */ } @@ -145,8 +158,11 @@ void load_linux(uint8_t *linux_image, void *params, const char *cmd_line) uint8_t *image_boot_param; uint16_t end_of_header_off; uint8_t *_cmd_line; - (void)cmd_line; int ret; +#if defined(WOLFBOOT_64BIT) + uint32_t map_size; +#endif + (void)cmd_line; wolfBoot_printf("linux payload" ENDLINE); @@ -185,6 +201,30 @@ void load_linux(uint8_t *linux_image, void *params, const char *cmd_line) memcpy((uint8_t *)KERNEL_LOAD_ADDRESS, linux_image + param_size, kernel_size); +#if defined(WOLFBOOT_64BIT) + /* A 64-bit build needs the kernel's 64-bit entry point. */ + if ((param.hdr.xloadflags & XLF_KERNEL_64) == 0) { + wolfBoot_printf("kernel has no 64-bit entry (xloadflags 0x%x)" ENDLINE, + param.hdr.xloadflags); + wolfBoot_panic(); + } + + /* Identity-map the load region (init_size bytes of scratch), the command + * line and the zero page; the kernel cannot fault these in itself. */ + map_size = param.hdr.init_size; + if (kernel_size > map_size) { + map_size = kernel_size; + } + x86_paging_map_memory(KERNEL_LOAD_ADDRESS, KERNEL_LOAD_ADDRESS, map_size); + x86_paging_map_memory(KERNEL_CMDLINE_ADDRESS, KERNEL_CMDLINE_ADDRESS, 0x1000); + x86_paging_map_memory((uint64_t)(uintptr_t)¶m, + (uint64_t)(uintptr_t)¶m, sizeof(param)); + + /* 64-bit entry point: 0x200 past the loaded protected-mode kernel. */ + wolfBoot_printf("booting (64-bit entry)..." ENDLINE); + jump_to_linux((uint64_t)KERNEL_LOAD_ADDRESS + 0x200, ¶m); +#else wolfBoot_printf("booting..." ENDLINE); jump_to_linux(param.hdr.code32_start, ¶m); +#endif } From e22e989c4dacffebdad6765adc970365bbf017f3 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 14:56:37 -0700 Subject: [PATCH 04/13] x86 FSP: initialise hobList before FspMemInit --- src/boot_x86_fsp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/boot_x86_fsp.c b/src/boot_x86_fsp.c index 79b5694ba5..b681918b42 100644 --- a/src/boot_x86_fsp.c +++ b/src/boot_x86_fsp.c @@ -533,7 +533,9 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp, struct stage2_ptr_holder stage2_holder; struct stage2_parameter temp_params; uint8_t *fsp_m_base, done = 0; - struct efi_hob *hobList, *it; + /* FspMemInit writes hobList only on EFI_SUCCESS; init so the reset-required + * and error paths never carry a stale pointer. */ + struct efi_hob *hobList = NULL, *it; memory_init_cb MemoryInit; uint64_t top_address = MEMORY_4GB; uint32_t new_stack; From e46fc4f6d75ad5eab7134baae76c566525114150 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 14:56:37 -0700 Subject: [PATCH 05/13] x86 FSP: measure the disk OS image into a configurable PCR --- options.mk | 3 +++ src/update_disk.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/options.mk b/options.mk index 469bc5214b..a99c556876 100644 --- a/options.mk +++ b/options.mk @@ -113,6 +113,9 @@ ifeq ($(MEASURED_BOOT),1) WOLFTPM:=1 CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT" CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_A=$(MEASURED_PCR_A)" + ifneq ($(MEASURED_PCR_OS),) + CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_OS=$(MEASURED_PCR_OS)" + endif ifeq ($(MEASURED_BOOT_APP_PARTITION),1) CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT_APP_PARTITION" endif diff --git a/src/update_disk.c b/src/update_disk.c index 53d91ac253..443c4c570c 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -43,6 +43,11 @@ #include "spi_flash.h" #include "printf.h" #include "wolfboot/wolfboot.h" +#include "tpm.h" +#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) && \ + (WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ) +#include +#endif #include "disk.h" #ifdef DISK_BOOT_CONFIRM #include "disk_trailer.h" @@ -614,6 +619,22 @@ void RAMFUNCTION wolfBoot_start(void) uintptr_t bl31_entry = 0; #endif char part_name[4] = {'P', ':', 'X', '\0'}; +#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) + int measure_ret; +#if defined(WOLFBOOT_SKIP_BOOT_VERIFY) +#error "measured boot: WOLFBOOT_MEASURED_PCR_OS needs the OS digest, which WOLFBOOT_SKIP_BOOT_VERIFY does not produce" +#endif +#if WOLFBOOT_SHA_DIGEST_SIZE < WOLFBOOT_TPM_PCR_DIG_SZ +#error "measured boot: image digest narrower than the PCR bank is not supported" +#endif +#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ +#if WOLFBOOT_TPM_PCR_DIG_SZ != 32 +#error "measured boot: the re-hash path only implements a SHA-256 PCR bank" +#endif + uint8_t os_pcr[WOLFBOOT_TPM_PCR_DIG_SZ]; + wc_Sha256 os_sha; +#endif +#endif BENCHMARK_DECLARE(); #ifdef DISK_ENCRYPT @@ -985,6 +1006,39 @@ void RAMFUNCTION wolfBoot_start(void) } } #endif +#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) + /* Measure the verified OS image into its own PCR, separate from the + * firmware measurement in WOLFBOOT_MEASURED_PCR_A. PCR4 is the TCG slot for + * the boot payload the boot manager launches. Re-hash to the PCR bank + * algorithm when the image digest is wider, so a SHA-256 bank is not + * extended with a truncated wider digest. */ + /* sha_hash is set only by a successful verify; NULL under + * WOLFBOOT_SKIP_BOOT_VERIFY. Fail-secure rather than dereference it. */ + if (os_image.sha_hash == NULL) { + wolfBoot_printf("No OS digest available to measure\r\n"); + wolfBoot_panic(); + } +#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ + if (wc_InitSha256(&os_sha) != 0 || + wc_Sha256Update(&os_sha, os_image.sha_hash, + WOLFBOOT_SHA_DIGEST_SIZE) != 0 || + wc_Sha256Final(&os_sha, os_pcr) != 0) { + wolfBoot_printf("Failed to re-hash the OS digest\r\n"); + wolfBoot_panic(); + } + measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS, os_pcr, + __LINE__); +#else + measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS, + os_image.sha_hash, __LINE__); +#endif + if (measure_ret != 0) { + /* Fail-secure, as stage1 does for its own measurement: a working + * TPM that cannot record the OS measurement must not boot it. */ + wolfBoot_printf("Failed to measure the OS image into its PCR\r\n"); + wolfBoot_panic(); + } +#endif /* WOLFBOOT_MEASURED_BOOT && WOLFBOOT_MEASURED_PCR_OS */ load_address = (uint32_t*)os_image.fw_base; From 54807371f0cfcfb895bce8c1c0b112c4d554f6b3 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 15:28:06 -0700 Subject: [PATCH 06/13] tpm: build wolfBoot_print_hexstr for measured boot, not only seal or keystore --- src/tpm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tpm.c b/src/tpm.c index 9a24b97b8d..59f1221503 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -56,7 +56,10 @@ int NOINLINEFUNCTION wolfBoot_constant_compare(const uint8_t* a, const uint8_t* return (diff != 0U) ? 1 : 0; } +#endif +#if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) || \ + defined(WOLFBOOT_MEASURED_BOOT) void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, unsigned long maxLine) { From 8131d19adb354902ec2d5b7b1de3baaf1a637bad Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 16 Sep 2026 15:49:07 -0700 Subject: [PATCH 07/13] x86 FSP: load an optional initrd alongside the Linux bzImage payload --- include/stage2_params.h | 3 + include/x86/linux_loader.h | 3 +- src/boot_x86_fsp_payload.c | 3 +- src/update_disk.c | 5 ++ src/x86/linux_loader.c | 92 ++++++++++++++++++++- tools/unit-tests/Makefile | 6 ++ tools/unit-tests/unit-linux-loader-initrd.c | 78 +++++++++++++++++ 7 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 tools/unit-tests/unit-linux-loader-initrd.c diff --git a/include/stage2_params.h b/include/stage2_params.h index 69608cd7f2..ef3c6e2c81 100644 --- a/include/stage2_params.h +++ b/include/stage2_params.h @@ -28,6 +28,9 @@ struct stage2_parameter { uint32_t hobList; uint32_t page_table; uint32_t tolum; + /* Verified payload length, set by the disk loader before do_boot so the + * Linux loader can bound the signed container header against the image. */ + uint32_t payload_size; #ifdef WOLFBOOT_TPM_SEAL uint32_t tpm_policy; uint16_t tpm_policy_size; diff --git a/include/x86/linux_loader.h b/include/x86/linux_loader.h index 5edc0ef89c..7ad0dda037 100644 --- a/include/x86/linux_loader.h +++ b/include/x86/linux_loader.h @@ -120,6 +120,7 @@ struct boot_params { uint8_t _pad9[276]; } __attribute__((packed)); -void load_linux(uint8_t *linux_image, void *params, const char *cmd_line); +void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, + const char *cmd_line); #endif /* LINUX_LOADER_H */ diff --git a/src/boot_x86_fsp_payload.c b/src/boot_x86_fsp_payload.c index a0a1f384e5..d7fb793ea1 100644 --- a/src/boot_x86_fsp_payload.c +++ b/src/boot_x86_fsp_payload.c @@ -105,7 +105,8 @@ void do_boot(const uint32_t *app) stage2_params = stage2_get_parameters(); #if defined(WOLFBOOT_LINUX_PAYLOAD) mptable_setup(); - load_linux((uint8_t *)app, stage2_params, cmdline); + load_linux((uint8_t *)app, stage2_params->payload_size, stage2_params, + cmdline); #elif defined(WOLFBOOT_ELF) int r; uint64_t e; diff --git a/src/update_disk.c b/src/update_disk.c index 443c4c570c..08f31d519e 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -1222,6 +1222,11 @@ void RAMFUNCTION wolfBoot_start(void) zynqmp_atf_handoff(bl31_entry, (uintptr_t)load_address, (uintptr_t)dts_addr, ZYNQMP_ATF_EL2); } +#endif +#ifdef WOLFBOOT_FSP + /* Hand the verified payload length to the Linux loader (via do_boot) so it + * can bound the signed container header against the image. */ + stage2_params->payload_size = (uint32_t)os_image.fw_size; #endif do_boot((uint32_t*)load_address #if defined(MMU) || defined(WOLFBOOT_FDT) diff --git a/src/x86/linux_loader.c b/src/x86/linux_loader.c index 99527c815b..ef7327c850 100644 --- a/src/x86/linux_loader.c +++ b/src/x86/linux_loader.c @@ -38,6 +38,17 @@ #define ENDLINE "\r\n" +/* Optional container so one signed image can carry a kernel plus an initrd: + * a linux_payload_hdr, then the bzImage, then the initrd. Without the magic the + * image is a bare bzImage (no initrd), so existing payloads still boot. */ +#define LINUX_PAYLOAD_MAGIC 0x3150584Cu /* "LXP1" */ +struct linux_payload_hdr { + uint32_t magic; + uint32_t kernel_size; + uint32_t initrd_size; + uint32_t reserved; +}; + /* XLF_KERNEL_64: the kernel has a 64-bit entry point (boot protocol >= 2.12, * xloadflags bit 0). Without it a 64-bit loader has nothing to jump to. */ #define XLF_KERNEL_64 (1u << 0) @@ -151,9 +162,39 @@ static int linux_kernel_size(uint32_t syssize, uint32_t load_limit, return 0; } -void load_linux(uint8_t *linux_image, void *params, const char *cmd_line) +/* Pick the initrd load address: as high as possible, page-aligned, below the + * smaller of the kernel's initrd_addr_max and the top of usable low RAM + * (ram_limit, i.e. tolum; 0 if unknown), without overlapping the kernel. + * Rejects a zero size and any size that cannot fit, so the placement can never + * underflow past the fit test. Returns 0 and *out on success, -1 otherwise. */ +static int linux_initrd_place(uint32_t initrd_addr_max, uint64_t ram_limit, + uint32_t initrd_size, uint64_t kernel_end, + uint64_t *out) +{ + uint64_t limit; /* first byte past the highest allowed placement */ + + if (initrd_size == 0) + return -1; + limit = (initrd_addr_max != 0) ? ((uint64_t)initrd_addr_max + 1) + : 0x38000000ULL; /* pre-2.03 default + 1 */ + if (ram_limit != 0 && ram_limit < limit) + limit = ram_limit; + if ((uint64_t)initrd_size > limit) + return -1; + *out = (limit - initrd_size) & ~0xfffULL; + if (*out < kernel_end) + return -1; + return 0; +} + +void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, + const char *cmd_line) { struct boot_params param = { 0 }; + struct linux_payload_hdr *phdr; + uint8_t *kernel_image; + uint8_t *initrd_image = NULL; + uint32_t initrd_size = 0; uint32_t kernel_size, param_size, load_limit; uint8_t *image_boot_param; uint16_t end_of_header_off; @@ -166,8 +207,28 @@ void load_linux(uint8_t *linux_image, void *params, const char *cmd_line) wolfBoot_printf("linux payload" ENDLINE); - image_boot_param = linux_image + 0x1f1; - end_of_header_off = *(linux_image + 0x201) + 0x202; + /* Unwrap the optional kernel+initrd container; a bare bzImage has no magic. */ + kernel_image = linux_image; + phdr = (struct linux_payload_hdr *)linux_image; + if (phdr->magic == LINUX_PAYLOAD_MAGIC) { + /* Bound the trusted header fields against the verified image so a + * malformed container cannot make the kernel or initrd reads run past + * it (or wrap the pointer arithmetic). Sum in 64-bit. */ + uint64_t need = (uint64_t)sizeof(*phdr) + phdr->kernel_size + + phdr->initrd_size; + if (image_size == 0 || phdr->kernel_size == 0 || + need > (uint64_t)image_size) { + wolfBoot_printf("invalid linux payload container" ENDLINE); + wolfBoot_panic(); + } + kernel_image = linux_image + sizeof(*phdr); + initrd_image = kernel_image + phdr->kernel_size; + initrd_size = phdr->initrd_size; + wolfBoot_printf("initrd: %d bytes" ENDLINE, initrd_size); + } + + image_boot_param = kernel_image + 0x1f1; + end_of_header_off = *(kernel_image + 0x201) + 0x202; memcpy((uint8_t*)¶m.hdr, image_boot_param, sizeof(struct setup_header)); @@ -198,9 +259,32 @@ void load_linux(uint8_t *linux_image, void *params, const char *cmd_line) wolfBoot_printf("invalid kernel size" ENDLINE); wolfBoot_panic(); } - memcpy((uint8_t *)KERNEL_LOAD_ADDRESS, linux_image + param_size, + memcpy((uint8_t *)KERNEL_LOAD_ADDRESS, kernel_image + param_size, kernel_size); + /* Place the initrd high in usable low RAM, copy it there, and hand its + * address to the kernel via the ramdisk fields. */ + if (initrd_size != 0) { + uint64_t initrd_addr; + /* The kernel occupies init_size (decompression + BSS + heap), which is + * normally larger than the compressed kernel_size; floor above both. */ + uint32_t kernel_span = (param.hdr.init_size > kernel_size) + ? param.hdr.init_size : kernel_size; + if (linux_initrd_place(param.hdr.initrd_addr_max, (uint64_t)load_limit, + initrd_size, + (uint64_t)KERNEL_LOAD_ADDRESS + kernel_span, + &initrd_addr) != 0) { + wolfBoot_printf("initrd does not fit in usable RAM" ENDLINE); + wolfBoot_panic(); + } +#if defined(WOLFBOOT_64BIT) + x86_paging_map_memory(initrd_addr, initrd_addr, initrd_size); +#endif + memcpy((uint8_t *)(uintptr_t)initrd_addr, initrd_image, initrd_size); + param.hdr.ramdisk_image = (uint32_t)initrd_addr; + param.hdr.ramdisk_size = initrd_size; + } + #if defined(WOLFBOOT_64BIT) /* A 64-bit build needs the kernel's 64-bit entry point. */ if ((param.hdr.xloadflags & XLF_KERNEL_64) == 0) { diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index e3ba1b043f..0ccce661ad 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -222,6 +222,7 @@ ENABLE_32BIT_TESTS ?= $(HAVE_M32) ifeq ($(ENABLE_32BIT_TESTS),1) TESTS+=unit-linux-loader-e820 TESTS+=unit-linux-loader-syssize +TESTS+=unit-linux-loader-initrd TESTS+=unit-sama5d3-ext-read else $(info Skipping 32-bit x86 unit tests (linux-loader, sama5d3-ext-read): 'gcc -m32' unavailable (set ENABLE_32BIT_TESTS=1 to force)) @@ -797,6 +798,11 @@ unit-linux-loader-syssize: ../../include/target.h unit-linux-loader-syssize.c -g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ -DWOLFBOOT_LOAD_BASE=0x100000 +unit-linux-loader-initrd: ../../include/target.h unit-linux-loader-initrd.c + gcc -m32 -o $@ unit-linux-loader-initrd.c -I. -I../../src -I../../include \ + -g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ + -DWOLFBOOT_LOAD_BASE=0x100000 + unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOAD_BASE=0x100000 -DWOLFBOOT_FSP \ -DUCODE0_ADDRESS=0 -ffunction-sections -fdata-sections $(LDFLAGS) \ diff --git a/tools/unit-tests/unit-linux-loader-initrd.c b/tools/unit-tests/unit-linux-loader-initrd.c new file mode 100644 index 0000000000..8794ea857a --- /dev/null +++ b/tools/unit-tests/unit-linux-loader-initrd.c @@ -0,0 +1,78 @@ +/* unit-linux-loader-initrd.c + * + * Tests linux_initrd_place(), the pure helper that chooses the initrd load + * address for the Linux bzImage loader. It must place the initrd as high as + * possible, page-aligned, below the smaller of the kernel's initrd_addr_max and + * the top of usable low RAM, without overlapping the kernel, and must never + * underflow past its own fit check. + * + * Built for x86 32bit (the only target supported by linux_loader.c), without + * the check framework, since 32bit libcheck is not generally available. + */ + +#include +#include +#include +#include + +#include "x86/hob.h" +#include "x86/linux_loader.h" + +#include "../../src/x86/hob.c" +#include "../../src/x86/linux_loader.c" + +static int fail(const char *msg) +{ + printf("FAIL: %s\n", msg); + return 1; +} + +int main(void) +{ + uint64_t out; + + /* Zero size is rejected. */ + if (linux_initrd_place(0x7fffffffu, 0, 0, 0x100000u, &out) == 0) + return fail("zero size accepted"); + + /* Size larger than the limit is rejected (no underflow). */ + if (linux_initrd_place(0x00000fffu, 0, 0x2000u, 0x1000u, &out) == 0) + return fail("size larger than limit accepted"); + + /* Normal fit: placed as high as possible below initrd_addr_max+1, + * page-aligned. limit 0x80000000 - 0x100000 -> 0x7ff00000. */ + out = 0; + if (linux_initrd_place(0x7fffffffu, 0, 0x100000u, 0x200000u, &out) != 0) + return fail("valid placement rejected"); + if (out != 0x7ff00000ull) + return fail("wrong high placement"); + if ((out & 0xfffu) != 0) + return fail("placement not page aligned"); + + /* initrd_addr_max == 0 falls back to the pre-2.03 default 0x38000000. */ + out = 0; + if (linux_initrd_place(0, 0, 0x1000u, 0x100000u, &out) != 0) + return fail("default-max placement rejected"); + if (out != 0x37fff000ull) + return fail("wrong default-max placement"); + + /* ram_limit (tolum) below initrd_addr_max clamps the placement. */ + out = 0; + if (linux_initrd_place(0x7fffffffu, 0x10000000ull, 0x1000u, 0x100000u, + &out) != 0) + return fail("ram-limited placement rejected"); + if (out != 0x0ffff000ull) + return fail("ram_limit not honoured"); + + /* A placement that would land below the kernel end is rejected. */ + if (linux_initrd_place(0x00300000u, 0, 0x100000u, 0x00280000u, &out) == 0) + return fail("kernel-overlapping placement accepted"); + + /* Size exactly equal to the limit yields address 0, rejected by the + * kernel-end floor. */ + if (linux_initrd_place(0x00000fffu, 0, 0x1000u, 0x1000u, &out) == 0) + return fail("zero-address placement accepted"); + + printf("PASS\n"); + return 0; +} From ae05b0d02311bd4c010f42f89132acb0111f1f8e Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 17 Sep 2026 11:31:37 -0700 Subject: [PATCH 08/13] docs: document the x86 FSP Linux bzImage/initrd payload, OS PCR measurement, and UPD decoder --- docs/Targets.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/docs/Targets.md b/docs/Targets.md index c91d0f4d71..7a7710f767 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -8542,6 +8542,52 @@ Note: - This feature requires `NASM` to be installed on the machine building wolfBoot. +### Booting a Linux bzImage payload (with an optional initrd) + +Set `LINUX_PAYLOAD=1` to boot a signed Linux `bzImage` from the disk A/B slot +instead of an ELF/Multiboot2 image. Both the 32-bit and the 64-bit (`64BIT=1`) +boot protocols are supported; a 64-bit build requires a kernel with a 64-bit +entry point (`xloadflags` bit 0). + +The kernel must be able to reach its root filesystem. A monolithic kernel with +the storage and filesystem drivers built in needs nothing further. To boot a +modular distribution kernel that relies on an initramfs, wrap the kernel and +the initrd into a single signed image using the container header below, then +sign that image as usual (all fields little-endian): + +``` +magic u32 0x3150584C ("LXP1") +kernel_size u32 exact bzImage byte length +initrd_size u32 exact initrd byte length +reserved u32 0 + + +``` + +wolfBoot detects the magic, loads the kernel, places the initrd below the +kernel's `initrd_addr_max` (and below the top of usable RAM), and hands its +address to the kernel. An image without the magic is treated as a bare bzImage +with no initrd, so existing payloads are unaffected. + +### Measuring the OS image into a PCR + +With `MEASURED_BOOT=1`, set `MEASURED_PCR_OS=` to extend the verified disk OS +image digest into PCR ``, in addition to the firmware measurement in +`MEASURED_PCR_A`. PCR 4 follows the TCG PC Client convention for the boot +payload the boot manager launches. The digest is re-hashed into the PCR bank +algorithm when it is wider than the bank. A failed extend is fail-secure: the +image is not booted. + +### Debugging the FSP UPD configuration + +`tools/x86_fsp/decode_fsp_upd.py` decodes an FSP UPD block into named fields, +using the offset comments that ship in `FspmUpd.h` / `FspsUpd.h`. It accepts +either the hex block a bootloader prints over the debug UART or a UPD region +lifted from a flash image, and `--diff` reports only the fields that differ +between two captures. This is useful when tuning the memory or silicon +configuration for a new board. + + ### Running on 64-bit QEMU Two example configuration files are available: `config/examples/x86_fsp_qemu.config` and `config/examples/x86_fsp_qemu_seal.config`. From de8a1cdd8b4c0f198b2945fa31679727dbd21742 Mon Sep 17 00:00:00 2001 From: David Garske Date: Sat, 19 Sep 2026 13:15:10 -0700 Subject: [PATCH 09/13] x86/FSP: select the FSP debug UART controller with X86_UART_NUMBER --- src/x86/tgl_fsp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/x86/tgl_fsp.c b/src/x86/tgl_fsp.c index 357646b0d6..011794d496 100644 --- a/src/x86/tgl_fsp.c +++ b/src/x86/tgl_fsp.c @@ -158,6 +158,15 @@ SI_PCH_DEVICE_INTERRUPT_CONFIG mPchHDevIntConfig[] = { {30, 0, SiPchIntA, 16}, }; +/* Which PCH LPSS SerialIo UART the FSP brings up for debug output. This is a + * controller index, not an address: the FSP maps whichever one is selected at + * PcdSerialIoUartDebugMmioBase, which we set from X86_UART_BASE. The two are + * independent, and getting the index wrong yields a silent console on a board + * whose UART base is perfectly correct. */ +#ifndef X86_UART_NUMBER +#define X86_UART_NUMBER 0 +#endif + #if defined(BUILD_LOADER_STAGE1) #define FIT_NUM_ENTRIES 2 __attribute__((__section__(".boot"))) const struct fit_table_entry fit_table[FIT_NUM_ENTRIES] = @@ -421,7 +430,10 @@ static int fsp_set_memory_cfg(FSPM_UPD *udp) mem_cfg->PcieClkSrcUsage[15] = 128; mem_cfg->PcieRpEnableMask = 1520787455; mem_cfg->PcdDebugInterfaceFlags = 16; - mem_cfg->SerialIoUartDebugControllerNumber = 0; + /* FSP-M and FSP-S each carry their own debug UART selector; both must be + * set from X86_UART_NUMBER or the console changes between phases - output + * appears for part of the boot and then stops, reading as a hang. */ + mem_cfg->SerialIoUartDebugControllerNumber = X86_UART_NUMBER; mem_cfg->MrcSafeConfig = 1; mem_cfg->TcssItbtPcie0En = 0; mem_cfg->TcssItbtPcie1En = 0; @@ -557,7 +569,7 @@ static void fsp_set_silicon_cfg(FSPS_UPD *fsps) upd->SerialIoUartTxPinMuxPolicy[4] = 0; upd->SerialIoUartTxPinMuxPolicy[5] = 0; upd->SerialIoUartTxPinMuxPolicy[6] = 0; - upd->SerialIoDebugUartNumber = 0; + upd->SerialIoDebugUartNumber = X86_UART_NUMBER; upd->SerialIoI2cMode[0] = 0; upd->SerialIoI2cMode[1] = 0; upd->SerialIoI2cMode[2] = 0; @@ -956,7 +968,7 @@ int fsp_machine_update_s_parameters(uint8_t *default_s_params) upd->EnableMultiPhaseSiliconInit = 0; upd->SerialIoUartMode[1] = upd->SerialIoUartMode[2] = 0x1; - upd->SerialIoDebugUartNumber = 0x0; + upd->SerialIoDebugUartNumber = X86_UART_NUMBER; memset(upd->PcieRpHotPlug, 0, sizeof(upd->PcieRpHotPlug)); memset(upd->CpuPcieRpHotPlug, 0, sizeof(upd->CpuPcieRpHotPlug)); From d1b5ec5e1082f9058fa44fbf839f89a83b79a521 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 25 Sep 2026 10:55:46 -0700 Subject: [PATCH 10/13] x86 FSP: address review - shared fsp_handle_reset, UART number/mode, always re-hash OS digest --- include/x86/fsp.h | 4 ++++ src/boot_x86_fsp.c | 11 ++--------- src/update_disk.c | 18 +++++------------- src/x86/fsp.c | 40 +++++++++++++++++++++++++--------------- src/x86/tgl_fsp.c | 8 +++++++- 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/include/x86/fsp.h b/include/x86/fsp.h index b404f2689e..5b234636f7 100644 --- a/include/x86/fsp.h +++ b/include/x86/fsp.h @@ -29,6 +29,10 @@ int fsp_get_image_revision(struct fsp_info_header *h, int *build, void print_fsp_image_revision(struct fsp_info_header *h); void fsp_init_silicon(void); +/* Act on a reset request from any FSP entry point; returns for a non-reset + * status so the caller still checks for EFI_SUCCESS. */ +void fsp_handle_reset(uint32_t status); + /* Reset requests (not failures) from FspMemInit and NotifyPhase. */ #define FSP_STATUS_RESET_REQUIRED_COLD 0x40000001 #define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002 diff --git a/src/boot_x86_fsp.c b/src/boot_x86_fsp.c index b681918b42..1c13dc0c5e 100644 --- a/src/boot_x86_fsp.c +++ b/src/boot_x86_fsp.c @@ -601,15 +601,8 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp, MemoryInit = (memory_init_cb)(fsp_m_base + fsp_m_info_header->FspMemoryInitEntryOffset); status = MemoryInit((void *)udp_m_parameter, &hobList); - if (status == FSP_STATUS_RESET_REQUIRED_WARM) { - wolfBoot_printf("warm reset required" ENDLINE); - reset(1); - } - else if (status == FSP_STATUS_RESET_REQUIRED_COLD) { - wolfBoot_printf("cold reset required" ENDLINE); - reset(0); - } - else if (status != EFI_SUCCESS) { + fsp_handle_reset(status); + if (status != EFI_SUCCESS) { wolfBoot_printf("failed: 0x%x" ENDLINE, status); panic(); } diff --git a/src/update_disk.c b/src/update_disk.c index 08f31d519e..4c1264165b 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -44,8 +44,7 @@ #include "printf.h" #include "wolfboot/wolfboot.h" #include "tpm.h" -#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) && \ - (WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ) +#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) #include #endif #include "disk.h" @@ -627,13 +626,11 @@ void RAMFUNCTION wolfBoot_start(void) #if WOLFBOOT_SHA_DIGEST_SIZE < WOLFBOOT_TPM_PCR_DIG_SZ #error "measured boot: image digest narrower than the PCR bank is not supported" #endif -#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ #if WOLFBOOT_TPM_PCR_DIG_SZ != 32 -#error "measured boot: the re-hash path only implements a SHA-256 PCR bank" +#error "measured boot: the OS re-hash path only implements a SHA-256 PCR bank" #endif uint8_t os_pcr[WOLFBOOT_TPM_PCR_DIG_SZ]; wc_Sha256 os_sha; -#endif #endif BENCHMARK_DECLARE(); @@ -1009,16 +1006,15 @@ void RAMFUNCTION wolfBoot_start(void) #if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) /* Measure the verified OS image into its own PCR, separate from the * firmware measurement in WOLFBOOT_MEASURED_PCR_A. PCR4 is the TCG slot for - * the boot payload the boot manager launches. Re-hash to the PCR bank - * algorithm when the image digest is wider, so a SHA-256 bank is not - * extended with a truncated wider digest. */ + * the boot payload the boot manager launches. The authenticated digest is + * always re-hashed into the PCR bank algorithm and that value extended, so + * the measurement is derived the same way whatever the image hash width. */ /* sha_hash is set only by a successful verify; NULL under * WOLFBOOT_SKIP_BOOT_VERIFY. Fail-secure rather than dereference it. */ if (os_image.sha_hash == NULL) { wolfBoot_printf("No OS digest available to measure\r\n"); wolfBoot_panic(); } -#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ if (wc_InitSha256(&os_sha) != 0 || wc_Sha256Update(&os_sha, os_image.sha_hash, WOLFBOOT_SHA_DIGEST_SIZE) != 0 || @@ -1028,10 +1024,6 @@ void RAMFUNCTION wolfBoot_start(void) } measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS, os_pcr, __LINE__); -#else - measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS, - os_image.sha_hash, __LINE__); -#endif if (measure_ret != 0) { /* Fail-secure, as stage1 does for its own measurement: a working * TPM that cannot record the OS measurement must not boot it. */ diff --git a/src/x86/fsp.c b/src/x86/fsp.c index bc27fa350a..0ed59ec439 100644 --- a/src/x86/fsp.c +++ b/src/x86/fsp.c @@ -100,22 +100,23 @@ void print_fsp_image_revision(struct fsp_info_header *h) wolfBoot_printf("%x.%x.%x build %x\r\n", maj, min, rev, build); } -/* Act on a reset request returned by an FSP NotifyPhase call. WARM/COLD do the - * matching reset; RESET_REQUIRED_3 is the Tiger Lake global reset (host + CSME) - * needed after the FSP-S ChipsetInit sync. A non-success, non-reset status is - * fatal. Returns only on EFI_SUCCESS. */ -static void notify_phase_handle_reset(uint32_t status) +/* Act on a reset request returned by any FSP entry point (FspMemInit, + * FspSiliconInit or NotifyPhase). WARM/COLD do the matching reset; + * RESET_REQUIRED_3 is the Tiger Lake global reset (host + CSME) needed after the + * FSP-S ChipsetInit sync. Any other status - EFI_SUCCESS or a real error - is + * left to the caller, so this returns in those cases. */ +void fsp_handle_reset(uint32_t status) { if (status == FSP_STATUS_RESET_REQUIRED_WARM) { - wolfBoot_printf("notify phase: warm reset required\n"); + wolfBoot_printf("fsp: warm reset required\n"); reset(1); } if (status == FSP_STATUS_RESET_REQUIRED_COLD) { - wolfBoot_printf("notify phase: cold reset required\n"); + wolfBoot_printf("fsp: cold reset required\n"); reset(0); } if (status == FSP_STATUS_RESET_REQUIRED_3) { - wolfBoot_printf("notify phase: global reset required\n"); + wolfBoot_printf("fsp: global reset required\n"); #ifdef WOLFBOOT_TGL /* global_reset() returns only if ETR3 is locked and the global bit * could not be armed. A plain reset would not satisfy the FSP-S @@ -127,10 +128,6 @@ static void notify_phase_handle_reset(uint32_t status) reset(0); #endif } - if (status != EFI_SUCCESS) { - wolfBoot_printf("notify phase failed %d\n", status); - panic(); - } } void fsp_init_silicon(void) @@ -186,6 +183,7 @@ void fsp_init_silicon(void) wolfBoot_printf("call silicon..."); silicon_init = _fsp_s_base_start + silicon_init_off; status = x86_run_fsp_32bit(silicon_init, silicon_init_parameter); + fsp_handle_reset(status); if (status != EFI_SUCCESS) { wolfBoot_printf("silicon init failed returned %d\n", status); panic(); @@ -201,11 +199,23 @@ void fsp_init_silicon(void) notify_phase = _start_fsp_s + notify_phase_off; param.Phase = EnumInitPhaseAfterPciEnumeration; status = x86_run_fsp_32bit(notify_phase, ¶m); - notify_phase_handle_reset(status); + fsp_handle_reset(status); + if (status != EFI_SUCCESS) { + wolfBoot_printf("notify phase (after pci enum) failed %d\n", status); + panic(); + } param.Phase = EnumInitPhaseReadyToBoot; status = x86_run_fsp_32bit(notify_phase, ¶m); - notify_phase_handle_reset(status); + fsp_handle_reset(status); + if (status != EFI_SUCCESS) { + wolfBoot_printf("notify phase (ready to boot) failed %d\n", status); + panic(); + } param.Phase = EnumInitPhaseEndOfFirmware; status = x86_run_fsp_32bit(notify_phase, ¶m); - notify_phase_handle_reset(status); + fsp_handle_reset(status); + if (status != EFI_SUCCESS) { + wolfBoot_printf("notify phase (end of firmware) failed %d\n", status); + panic(); + } } diff --git a/src/x86/tgl_fsp.c b/src/x86/tgl_fsp.c index 011794d496..4b6f88a96f 100644 --- a/src/x86/tgl_fsp.c +++ b/src/x86/tgl_fsp.c @@ -210,7 +210,7 @@ FSPT_UPD TempRamInitParams = { }, .FsptConfig = { .PcdSerialIoUartDebugEnable = 1, - .PcdSerialIoUartNumber = 0, + .PcdSerialIoUartNumber = X86_UART_NUMBER, .PcdSerialIoUartMode = 1, .PcdSerialIoUartBaudRate = 115200, .PcdPciExpressBaseAddress = PCI_ECAM_BASE, @@ -534,6 +534,10 @@ static void fsp_set_silicon_cfg(FSPS_UPD *fsps) upd->SerialIoUartMode[4] = 0; upd->SerialIoUartMode[5] = 0; upd->SerialIoUartMode[6] = 0; + /* Whatever X86_UART_NUMBER selects must be enabled. Leave a board-tuned + * mode (e.g. UART0's SkipInit) alone, but never leave the debug UART off. */ + if (upd->SerialIoUartMode[X86_UART_NUMBER] == 0) + upd->SerialIoUartMode[X86_UART_NUMBER] = 1; /* SerialIoUartPci */ upd->SerialIoUartAutoFlow[0] = 0; upd->SerialIoUartAutoFlow[1] = 0; upd->SerialIoUartAutoFlow[2] = 0; @@ -968,6 +972,8 @@ int fsp_machine_update_s_parameters(uint8_t *default_s_params) upd->EnableMultiPhaseSiliconInit = 0; upd->SerialIoUartMode[1] = upd->SerialIoUartMode[2] = 0x1; + if (upd->SerialIoUartMode[X86_UART_NUMBER] == 0) + upd->SerialIoUartMode[X86_UART_NUMBER] = 0x1; /* enable the debug UART */ upd->SerialIoDebugUartNumber = X86_UART_NUMBER; memset(upd->PcieRpHotPlug, 0, sizeof(upd->PcieRpHotPlug)); From 14322d71f01ea84aa22e40800964d30e3fca0aee Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 28 Sep 2026 10:50:55 -0700 Subject: [PATCH 11/13] x86 FSP: reject kernel/initrd load targets that overlap wolfBoot or the payload --- src/x86/linux_loader.c | 49 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/x86/linux_loader.c b/src/x86/linux_loader.c index ef7327c850..6a571725ab 100644 --- a/src/x86/linux_loader.c +++ b/src/x86/linux_loader.c @@ -187,6 +187,34 @@ static int linux_initrd_place(uint32_t initrd_addr_max, uint64_t ram_limit, return 0; } +extern uint8_t _start_wolfboot[]; +extern uint8_t _end_wb[]; + +/* True if [a, a+alen) and [b, b+blen) overlap; zero-length ranges never do. */ +static int ranges_overlap(uint64_t a, uint64_t alen, uint64_t b, uint64_t blen) +{ + if (alen == 0 || blen == 0) + return 0; + return (a < b + blen) && (b < a + alen); +} + +/* Reject a kernel/initrd load target that would land on wolfBoot's own image + * (code, rodata, data, bss) or on the verified payload being copied from. + * Returns nonzero on conflict. */ +static int linux_load_conflicts(uint64_t dst, uint64_t len, + const uint8_t *payload, uint32_t payload_len) +{ + uint64_t wb = (uint64_t)(uintptr_t)_start_wolfboot; + uint64_t wb_len = (uint64_t)(uintptr_t)_end_wb - wb; + + if (ranges_overlap(dst, len, wb, wb_len)) + return 1; + if (ranges_overlap(dst, len, (uint64_t)(uintptr_t)payload, + (uint64_t)payload_len)) + return 1; + return 0; +} + void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, const char *cmd_line) { @@ -196,6 +224,7 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, uint8_t *initrd_image = NULL; uint32_t initrd_size = 0; uint32_t kernel_size, param_size, load_limit; + uint32_t kernel_span; uint8_t *image_boot_param; uint16_t end_of_header_off; uint8_t *_cmd_line; @@ -259,6 +288,16 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, wolfBoot_printf("invalid kernel size" ENDLINE); wolfBoot_panic(); } + /* The kernel occupies init_size at runtime (decompression + BSS + heap), + * normally larger than the copied kernel_size; guard the larger span. */ + kernel_span = (param.hdr.init_size > kernel_size) + ? param.hdr.init_size : kernel_size; + /* Refuse a load target that overlaps wolfBoot's own image or the payload. */ + if (linux_load_conflicts((uint64_t)KERNEL_LOAD_ADDRESS, kernel_span, + linux_image, image_size)) { + wolfBoot_printf("kernel load target overlaps wolfBoot or payload" ENDLINE); + wolfBoot_panic(); + } memcpy((uint8_t *)KERNEL_LOAD_ADDRESS, kernel_image + param_size, kernel_size); @@ -266,10 +305,6 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, * address to the kernel via the ramdisk fields. */ if (initrd_size != 0) { uint64_t initrd_addr; - /* The kernel occupies init_size (decompression + BSS + heap), which is - * normally larger than the compressed kernel_size; floor above both. */ - uint32_t kernel_span = (param.hdr.init_size > kernel_size) - ? param.hdr.init_size : kernel_size; if (linux_initrd_place(param.hdr.initrd_addr_max, (uint64_t)load_limit, initrd_size, (uint64_t)KERNEL_LOAD_ADDRESS + kernel_span, @@ -277,6 +312,12 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, wolfBoot_printf("initrd does not fit in usable RAM" ENDLINE); wolfBoot_panic(); } + /* Refuse a target that overlaps wolfBoot's own image or the payload. */ + if (linux_load_conflicts(initrd_addr, initrd_size, + linux_image, image_size)) { + wolfBoot_printf("initrd load target overlaps wolfBoot or payload" ENDLINE); + wolfBoot_panic(); + } #if defined(WOLFBOOT_64BIT) x86_paging_map_memory(initrd_addr, initrd_addr, initrd_size); #endif From 569a7bfcff8270974f1c86840d02fa29879ff216 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 29 Sep 2026 07:41:28 -0700 Subject: [PATCH 12/13] x86 FSP: split kernel copy vs decompress overlap checks and gate on boot protocol 2.10 --- src/x86/linux_loader.c | 50 ++++++++++++++++----- tools/unit-tests/Makefile | 3 +- tools/unit-tests/unit-linux-loader-initrd.c | 32 +++++++++++++ 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/x86/linux_loader.c b/src/x86/linux_loader.c index 6a571725ab..04b22fbf5e 100644 --- a/src/x86/linux_loader.c +++ b/src/x86/linux_loader.c @@ -223,8 +223,7 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, uint8_t *kernel_image; uint8_t *initrd_image = NULL; uint32_t initrd_size = 0; - uint32_t kernel_size, param_size, load_limit; - uint32_t kernel_span; + uint32_t kernel_size, param_size, load_limit, kernel_avail; uint8_t *image_boot_param; uint16_t end_of_header_off; uint8_t *_cmd_line; @@ -238,6 +237,7 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, /* Unwrap the optional kernel+initrd container; a bare bzImage has no magic. */ kernel_image = linux_image; + kernel_avail = image_size; phdr = (struct linux_payload_hdr *)linux_image; if (phdr->magic == LINUX_PAYLOAD_MAGIC) { /* Bound the trusted header fields against the verified image so a @@ -251,6 +251,7 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, wolfBoot_panic(); } kernel_image = linux_image + sizeof(*phdr); + kernel_avail = phdr->kernel_size; initrd_image = kernel_image + phdr->kernel_size; initrd_size = phdr->initrd_size; wolfBoot_printf("initrd: %d bytes" ENDLINE, initrd_size); @@ -261,6 +262,14 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, memcpy((uint8_t*)¶m.hdr, image_boot_param, sizeof(struct setup_header)); + /* pref_address and init_size are boot-protocol 2.10+ fields; refuse an + * older kernel rather than trust uninitialised header bytes. (The 64-bit + * boot path already implies >= 2.12, but check explicitly at point of use.) */ + if (param.hdr.version < 0x020a) { + wolfBoot_printf("linux boot protocol < 2.10 unsupported" ENDLINE); + wolfBoot_panic(); + } + ret = linux_boot_params_fill_memory_map(¶m, (struct efi_hob*) params); @@ -288,14 +297,25 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, wolfBoot_printf("invalid kernel size" ENDLINE); wolfBoot_panic(); } - /* The kernel occupies init_size at runtime (decompression + BSS + heap), - * normally larger than the copied kernel_size; guard the larger span. */ - kernel_span = (param.hdr.init_size > kernel_size) - ? param.hdr.init_size : kernel_size; - /* Refuse a load target that overlaps wolfBoot's own image or the payload. */ - if (linux_load_conflicts((uint64_t)KERNEL_LOAD_ADDRESS, kernel_span, + /* The kernel bytes read for the copy must stay within the verified image. */ + if ((uint64_t)param_size + kernel_size > (uint64_t)kernel_avail) { + wolfBoot_printf("linux kernel extent exceeds image" ENDLINE); + wolfBoot_panic(); + } + /* wolfBoot writes the compressed image to [KERNEL_LOAD_ADDRESS, +kernel_size) + * while it is still running: that must not land on wolfBoot or the payload. + * linux_kernel_size already bounded it below the top of usable low RAM. */ + if (linux_load_conflicts((uint64_t)KERNEL_LOAD_ADDRESS, kernel_size, linux_image, image_size)) { - wolfBoot_printf("kernel load target overlaps wolfBoot or payload" ENDLINE); + wolfBoot_printf("kernel copy target overlaps wolfBoot or payload" ENDLINE); + wolfBoot_panic(); + } + /* The kernel then decompresses itself into [pref_address, +init_size). That + * may overlap wolfBoot (already handed off) but must stay within usable RAM + * and, below, clear of the initrd. */ + if (load_limit != 0 && + (uint64_t)param.hdr.pref_address + param.hdr.init_size > load_limit) { + wolfBoot_printf("kernel decompress extent exceeds usable RAM" ENDLINE); wolfBoot_panic(); } memcpy((uint8_t *)KERNEL_LOAD_ADDRESS, kernel_image + param_size, @@ -305,9 +325,17 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, * address to the kernel via the ramdisk fields. */ if (initrd_size != 0) { uint64_t initrd_addr; + /* The initrd must sit above both the compressed copy at + * KERNEL_LOAD_ADDRESS and the kernel's runtime decompress region at + * pref_address, so the kernel clobbers neither the copy it reads nor + * the initrd it consumes. */ + uint64_t kernel_top = (uint64_t)KERNEL_LOAD_ADDRESS + kernel_size; + uint64_t decompress_top = (uint64_t)param.hdr.pref_address + + param.hdr.init_size; + if (decompress_top > kernel_top) + kernel_top = decompress_top; if (linux_initrd_place(param.hdr.initrd_addr_max, (uint64_t)load_limit, - initrd_size, - (uint64_t)KERNEL_LOAD_ADDRESS + kernel_span, + initrd_size, kernel_top, &initrd_addr) != 0) { wolfBoot_printf("initrd does not fit in usable RAM" ENDLINE); wolfBoot_panic(); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 0ccce661ad..a14f9ade49 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -801,7 +801,8 @@ unit-linux-loader-syssize: ../../include/target.h unit-linux-loader-syssize.c unit-linux-loader-initrd: ../../include/target.h unit-linux-loader-initrd.c gcc -m32 -o $@ unit-linux-loader-initrd.c -I. -I../../src -I../../include \ -g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ - -DWOLFBOOT_LOAD_BASE=0x100000 + -DWOLFBOOT_LOAD_BASE=0x100000 \ + -Wl,--defsym,_end_wb=_start_wolfboot+0x100000 unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOAD_BASE=0x100000 -DWOLFBOOT_FSP \ diff --git a/tools/unit-tests/unit-linux-loader-initrd.c b/tools/unit-tests/unit-linux-loader-initrd.c index 8794ea857a..dd810df416 100644 --- a/tools/unit-tests/unit-linux-loader-initrd.c +++ b/tools/unit-tests/unit-linux-loader-initrd.c @@ -21,6 +21,12 @@ #include "../../src/x86/hob.c" #include "../../src/x86/linux_loader.c" +/* linux_loader.c references the wolfBoot extent symbols from the linker + * script. Provide a stand-in region for the host test; _end_wb is defined by + * the Makefile via --defsym at _start_wolfboot + this size (1 MB). */ +#define TEST_WB_SIZE 0x100000 +uint8_t _start_wolfboot[TEST_WB_SIZE]; + static int fail(const char *msg) { printf("FAIL: %s\n", msg); @@ -73,6 +79,32 @@ int main(void) if (linux_initrd_place(0x00000fffu, 0, 0x1000u, 0x1000u, &out) == 0) return fail("zero-address placement accepted"); + /* ranges_overlap: adjacency does not overlap, one byte of overlap does, + * and a zero-length range never overlaps. */ + if (ranges_overlap(0x1000, 0x1000, 0x2000, 0x1000) != 0) + return fail("adjacent ranges reported as overlapping"); + if (ranges_overlap(0x1000, 0x1001, 0x2000, 0x1000) == 0) + return fail("one-byte overlap missed"); + if (ranges_overlap(0x2000, 0, 0x2000, 0x1000) != 0) + return fail("zero-length range reported as overlapping"); + + /* linux_load_conflicts: the wolfBoot region is [_start_wolfboot, _end_wb); + * a target inside it, or inside the payload, conflicts; one clear of both + * does not. */ + { + static uint8_t payload[0x1000]; + uint64_t wb = (uint64_t)(uintptr_t)_start_wolfboot; + if (linux_load_conflicts(wb + 0x1000, 0x1000, payload, + (uint32_t)sizeof(payload)) == 0) + return fail("overlap with wolfBoot not caught"); + if (linux_load_conflicts((uint64_t)(uintptr_t)payload, 0x10, payload, + (uint32_t)sizeof(payload)) == 0) + return fail("overlap with payload not caught"); + if (linux_load_conflicts(0x1000, 0x1000, payload, + (uint32_t)sizeof(payload)) != 0) + return fail("false conflict for a clear target"); + } + printf("PASS\n"); return 0; } From b540e4ea561fed9e4de3deb78a0f72fd7a19a69d Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 29 Sep 2026 08:07:35 -0700 Subject: [PATCH 13/13] x86 FSP: fix linux-loader unit-test link for wolfBoot extent symbols and guard the setup-header read --- src/x86/linux_loader.c | 6 ++++++ tools/unit-tests/Makefile | 6 ++++-- tools/unit-tests/unit-linux-loader-e820.c | 5 +++++ tools/unit-tests/unit-linux-loader-syssize.c | 5 +++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/x86/linux_loader.c b/src/x86/linux_loader.c index 04b22fbf5e..02c177529c 100644 --- a/src/x86/linux_loader.c +++ b/src/x86/linux_loader.c @@ -257,6 +257,12 @@ void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, wolfBoot_printf("initrd: %d bytes" ENDLINE, initrd_size); } + /* The setup header lives at offset 0x1f1; refuse an image too small to + * hold it before dereferencing into it. */ + if ((uint64_t)0x1f1 + sizeof(struct setup_header) > (uint64_t)kernel_avail) { + wolfBoot_printf("linux image too small for setup header" ENDLINE); + wolfBoot_panic(); + } image_boot_param = kernel_image + 0x1f1; end_of_header_off = *(kernel_image + 0x201) + 0x202; memcpy((uint8_t*)¶m.hdr, diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index a14f9ade49..675597c549 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -791,12 +791,14 @@ unit-pci-4gib: unit-pci.c ../../src/pci.c unit-linux-loader-e820: ../../include/target.h unit-linux-loader-e820.c gcc -m32 -o $@ unit-linux-loader-e820.c -I. -I../../src -I../../include \ -g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ - -DWOLFBOOT_LOAD_BASE=0x100000 + -DWOLFBOOT_LOAD_BASE=0x100000 \ + -Wl,--defsym,_end_wb=_start_wolfboot+0x100000 unit-linux-loader-syssize: ../../include/target.h unit-linux-loader-syssize.c gcc -m32 -o $@ unit-linux-loader-syssize.c -I. -I../../src -I../../include \ -g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ - -DWOLFBOOT_LOAD_BASE=0x100000 + -DWOLFBOOT_LOAD_BASE=0x100000 \ + -Wl,--defsym,_end_wb=_start_wolfboot+0x100000 unit-linux-loader-initrd: ../../include/target.h unit-linux-loader-initrd.c gcc -m32 -o $@ unit-linux-loader-initrd.c -I. -I../../src -I../../include \ diff --git a/tools/unit-tests/unit-linux-loader-e820.c b/tools/unit-tests/unit-linux-loader-e820.c index 02a6403063..8651f01214 100644 --- a/tools/unit-tests/unit-linux-loader-e820.c +++ b/tools/unit-tests/unit-linux-loader-e820.c @@ -19,6 +19,11 @@ #include "../../src/x86/hob.c" #include "../../src/x86/linux_loader.c" +/* linux_loader.c references the wolfBoot extent symbols from the linker + * script; define a stand-in so this test links. _end_wb is provided by the + * Makefile via --defsym. */ +uint8_t _start_wolfboot[0x100000]; + /* More descriptors than the table can hold, to force the overflow path. */ #define N_ENTRIES 200 #define CANARY_LEN 2048 diff --git a/tools/unit-tests/unit-linux-loader-syssize.c b/tools/unit-tests/unit-linux-loader-syssize.c index da988a4045..492fb38664 100644 --- a/tools/unit-tests/unit-linux-loader-syssize.c +++ b/tools/unit-tests/unit-linux-loader-syssize.c @@ -24,6 +24,11 @@ #include "../../src/x86/hob.c" #include "../../src/x86/linux_loader.c" +/* linux_loader.c references the wolfBoot extent symbols from the linker + * script; define a stand-in so this test links. _end_wb is provided by the + * Makefile via --defsym. */ +uint8_t _start_wolfboot[0x100000]; + /* A generous low-memory window (256 MiB) used as the destination limit. */ #define LOAD_LIMIT 0x10000000u