diff --git a/.gitignore b/.gitignore index d33c896740..ad7e19a2d0 100644 --- a/.gitignore +++ b/.gitignore @@ -250,6 +250,7 @@ tools/unit-tests/unit-fwtpm-nv-oob tools/unit-tests/unit-x86-paging-oob tools/unit-tests/unit-ahci-unlock-panic tools/unit-tests/unit-ata-security-passphrase-zeroize +tools/unit-tests/unit-ata-sector-io tools/unit-tests/unit-arm-tee-psa-ipc tools/unit-tests/unit-flash-write-mcxa tools/unit-tests/unit-rw612-flash @@ -625,6 +626,13 @@ tools/unit-tests/unit-hifive1-flash-write tools/unit-tests/unit-hifive1-hfrosccfg tools/unit-tests/unit-keygen-keystore tools/unit-tests/unit-kontron-tgl-spi +tools/unit-tests/unit-fsp-nvs +tools/unit-tests/unit-fsp-nvs-save +tools/unit-tests/unit-tgl-spi-hwseq +tools/unit-tests/unit-x86-apic-ids +tools/unit-tests/unit-x86-acpi +tools/unit-tests/unit-boot-x86-fsp-measure +tools/unit-tests/unit-boot-x86-fsp-payload tools/unit-tests/unit-nrf5340-flash-protect tools/unit-tests/unit-nrf5340-uart-crlf tools/unit-tests/unit-nrf54l-uart-write @@ -670,6 +678,8 @@ tools/unit-tests/keystore_gen tools/unit-tests/kinetis_erase_extract.h tools/unit-tests/kontron_spi_extract.h tools/unit-tests/kontron_spi_fn_extract.h +tools/unit-tests/tgl_spi_hwseq_extract.h +tools/unit-tests/x86_apic_ids_extract.h tools/unit-tests/nrf5340_protect_fn_extract.h tools/unit-tests/nrf54l_uart_write_extract.h tools/unit-tests/p1021_erase_extract.h diff --git a/arch.mk b/arch.mk index 0da7cdd03f..14a292af2a 100644 --- a/arch.mk +++ b/arch.mk @@ -2500,6 +2500,9 @@ ifeq ("${FSP}", "1") OBJS+=src/x86/tgl_fsp.o OBJS+=src/ucode0.o CFLAGS += -DUCODE0_ADDRESS=$(UCODE0_BASE) + ifeq ($(FSP_NVS_CACHE),1) + OBJS+=src/x86/fsp_nvs.o + endif endif else # building wolfBoot @@ -2546,6 +2549,9 @@ ifeq ("${FSP}", "1") endif ifeq ($(FSP_TGL), 1) OBJS+=src/x86/tgl_fsp.o + ifeq ($(FSP_NVS_CACHE),1) + OBJS+=src/x86/fsp_nvs.o + endif endif endif ifeq ($(64BIT),1) diff --git a/config/examples/nai_68int6.config b/config/examples/nai_68int6.config index 286f0301e4..9a3830ba0b 100644 --- a/config/examples/nai_68int6.config +++ b/config/examples/nai_68int6.config @@ -18,7 +18,6 @@ ENCRYPTION=0 WOLFBOOT_NO_PARTITIONS=1 # Disk boot: signed kernels in GPT partitions 3/4, Ubuntu root in partition 2. # The A/B boot-partition indices are set per target in src/update_disk.c. -WOLFBOOT_BOOT_DISK=0 FORCE_32BIT=1 ########## Flash layout (measured from the factory descriptor, 32 MB) ########## @@ -41,6 +40,12 @@ BIOS_REGION_SIZE=0xc00000 UCODE0_BASE=0xffd90000 UCODE0_BIN=src/x86/ucode0.bin +# FSP memory training data cache: 128 KB of the BIOS region just below the +# wolfBoot window, so it survives a reflash of the window. +FSP_NVS_CACHE=1 +FSP_NVS_BASE=0xff9e0000 +FSP_NVS_SIZE=0x20000 + FSP_T_BIN=./src/x86/fsp_t.bin FSP_M_BIN=./src/x86/fsp_m.bin FSP_S_BIN=./src/x86/fsp_s.bin @@ -68,6 +73,15 @@ PCH_HAS_PCR=1 # Generate ACPI tables so Linux gets the interrupt model and PCI routing. ACPI=1 +########## PCIe switch and backplane (stock BIOS defaults) ########## +# Lanes: 8x1, 1x4_4x1, 4x1_1x4, 1x4_1x4 or 1x8. Delays are in seconds. +NAI_68INT6_PCIE_LANES=8x1 +NAI_68INT6_PCIE_NT=0 +NAI_68INT6_BACKPLANE_CLK=0 +NAI_68INT6_PCIE_LINK_DELAY_S=0 +NAI_68INT6_BOOT_DELAY_S=0 +NAI_68INT6_FPGA_READY_DELAY_S=0 + ########## Payload: signed 64-bit Linux bzImage ########## 64BIT=1 ELF=1 diff --git a/docs/Targets.md b/docs/Targets.md index 53026cd31c..36d2deb570 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -9058,7 +9058,7 @@ wolfBoot target `nai_68int6` for the North Atlantic Industries 68INT6, a 3U Open | | | |---|---| | CPU | i7-1185GRE (`06-8C-01`), Tiger Lake UP3, PCH-LP | -| Memory | 2 x 32 GB DDR4-3200 SODIMM, non-ECC | +| Memory | DDR4-3200, soldered down, with In-Band ECC (`FSP_IN_BAND_ECC`) | | BIOS flash | Rev C/D5: 2 x W25R256JV (32 MB), selected by JP36. Next revision: one 64 MB device | | TPM | SLB9670 (SHA-1/256), or SLB9672, which adds a SHA-384 bank | | Storage | Onboard SATA SSD, AHCI at `00:17.0` | @@ -9070,12 +9070,11 @@ wolfBoot target `nai_68int6` for the North Atlantic Industries 68INT6, a 3U Open cp config/examples/nai_68int6.config .config ./tools/scripts/x86_fsp/tgl/tgl_download_fsp.sh # FSP UPD headers, first time only ./tools/scripts/x86_fsp/tgl/nai_extract_fsp.sh <68INT6.bin> # board's own FSP blobs -./tools/scripts/x86_fsp/tgl/assemble_image.sh -k # generate signing keys -make +make # first build generates the signing key ./tools/scripts/x86_fsp/tgl/assemble_image.sh -n <68INT6.bin> ``` -`nai_extract_fsp.sh` takes FSP-T/M/S and the microcode from a 68INT6 BIOS image. Without it the build uses the public FSP, which compiles but does not train memory. The `-n` stitch replaces the top 6 MB (`BOOTLOADER_PARTITION_SIZE`) of the 12 MB BIOS region and keeps the descriptor and CSME byte for byte. The board has no recovery region (recovery is the second flash device), so use `-n`, not `-m`. The result is `final_image.bin`. +The host needs `nasm`, `gcc-multilib` and `python-is-python3` besides the usual build tools. `nai_extract_fsp.sh` takes FSP-T/M/S and the microcode from a 68INT6 BIOS image. Without it the build uses the public FSP, which compiles but does not train memory. The `-n` stitch replaces the top 6 MB (`BOOTLOADER_PARTITION_SIZE`) of the 12 MB BIOS region and keeps the descriptor and CSME byte for byte. The board has no recovery region (recovery is the second flash device). The result is `final_image.bin`. ### Integrity algorithms @@ -9087,7 +9086,7 @@ ECC P-384 and SHA-384 match what Intel Boot Guard uses for the Initial Boot Bloc | Image digest | SHA-384 (`HASH=SHA384`) | | Measured boot PCR | SHA-256 (the SLB9670 has only SHA-1 and SHA-256 banks) | -`wolfBoot_image_measure()` re-hashes the authenticated digest with the PCR bank's algorithm, so one image serves either TPM. `MEASURED_PCR_ALG=SHA384` needs an SLB9672 with its SHA-384 bank allocated (`TPM2_PCR_Allocate` under platform auth, a provisioning step). +Stage 1 re-hashes stage 2's authenticated SHA-384 digest with SHA-256 and extends PCR 0 with the result, as stage 2 does for the OS image in PCR 4, so `tools/scripts/x86_fsp/compute_pcr.py` can predict PCR 0. Only the SHA-256 PCR bank is supported. ### Configuration @@ -9096,10 +9095,14 @@ Notable options in [config/examples/nai_68int6.config](../config/examples/nai_68 | Option | Meaning | |---|---| | `X86_UART_NUMBER=2` | LPSS UART2 for all three FSP phases (Kontron uses 0) | -| `ACPI=1` | Required. Generates ACPI tables (RSDP/XSDT/FADT/MADT/MCFG and a minimal DSDT); the board's PCI interrupt routing is only in the DSDT `_PRT`, since the MP table is the Kontron one | +| `ACPI=1` | Required. Generates ACPI tables (RSDP/XSDT/FADT/MADT/MCFG/TPM2/HPET/DMAR and a minimal DSDT with the TPM device); the board's PCI interrupt routing is only in the DSDT `_PRT`, since the MP table is the Kontron one. The DSDT also declares the board devices on LPSS I2C0 (EEPROMs, RTC/watchdog) and GSPI0 (the PRC, for NAI's `naigspi` driver) | +| `FSP_NVS_CACHE=1`, `FSP_NVS_BASE`, `FSP_NVS_SIZE` | Keeps FSP-M's memory training data (its non-volatile storage HOB) in a 128 KB area of the BIOS region below the wolfBoot window, so later boots skip full memory training. Stage 1 passes a valid copy back through `NvsBufferPtr`; stage 2 rewrites it, before FSP-S and before the BIOS region is locked, only when it changes. The record is keyed on a CRC of the FSP-M image and its UPDs, so a change to either falls back to full training. If FspMemInit fails with a saved record, stage 1 sets a CMOS flag and cold-resets, boots train from scratch until FspMemInit succeeds, and stage 2 replaces the record. `assemble_image.sh -n` blanks the area, so an image built from a board's flash dump does not carry that board's training data | +| `FSP_HYPERTHREADING=1` | Hyper-Threading on (off by default). The MADT and the MP table list the enabled logical processors from the CPUID topology, so either setting works | +| `NAI_68INT6_PCIE_LANES` | PCIe switch lane configuration, strapped on GPP_B21/B22/S4/S7/S6 before memory init: `8x1` (default), `1x4_4x1`, `4x1_1x4`, `1x4_1x4` or `1x8` | +| `NAI_68INT6_PCIE_NT`, `NAI_68INT6_BACKPLANE_CLK` | PCIe switch non-transparent mode (GPP_S2) and the backplane PCIe clock (GPP_A0); both off by default | +| `NAI_68INT6_PCIE_LINK_DELAY_S`, `NAI_68INT6_BOOT_DELAY_S`, `NAI_68INT6_FPGA_READY_DELAY_S` | Waits before CPU PCIe link training (0-5 s), before polling PCIE_RDY (0-255 s), and added to the 5 s PCIE_RDY timeout (0, 5 or 10 s); all 0 by default. Before PCI enumeration wolfBoot polls PCIE_RDY (GPP_C23) every 50 ms, continues with a warning on timeout, then drives GPP_B12 high | | `ANTI_ROLLBACK=1` | Persistent version floor and boot audit counters in TPM NV | | `MEASURED_BOOT=1`, `MEASURED_PCR_OS=4` | Firmware into PCR 0, the verified OS image into PCR 4 | -| `WOLFBOOT_BOOT_DISK=0` | Boot from GPT partitions; the A/B indices (sda3/sda4) are set in `src/update_disk.c` | ### BIOS region lock @@ -9109,7 +9112,7 @@ Before handoff, `hal_flash_protect()` in the shared Tiger Lake HAL (`hal/x86_fsp `ANTI_ROLLBACK=1` keeps a version floor and boot audit counters in a TPM NV index; the backend is `src/boot_state_tpm.c` and the policy is `src/boot_state.c`. The floor is the highest version ever booted, set trust-on-first-use. A validly signed older image still boots and records a rollback event. Only an untrustworthy reference halts the boot; `ANTI_ROLLBACK_HALT_ON_FAILURE=1` also halts when the TPM is unreachable, a write fails or the platform hierarchy cannot be disabled. -TPM NV is used instead of the partition state trailer because the floor must survive a full reimage of the disk or flash. `DISK_BOOT_CONFIRM` still handles per-slot confirmation, and the backend can move to a root-of-trust device later without changing the policy. +TPM NV is used instead of the partition state trailer because the floor must survive a full reimage of the disk or flash. Per-slot boot confirmation is `DISK_BOOT_CONFIRM`, which this configuration leaves off, and the backend can move to a root-of-trust device later without changing the policy. `ALLOW_DOWNGRADE` is left unset, the secure default: if the higher-versioned slot fails to verify, wolfBoot halts (`Rollback to lower version not allowed`) rather than boot the lower one. Building with `ALLOW_DOWNGRADE=1` allows that fall-back and leaves the TPM floor as the only rollback guard. @@ -9117,7 +9120,26 @@ The booted OS cannot lower the floor. The index is platform-created and writable ### Boot, programming and recovery -wolfBoot boots the highest-versioned signed 64-bit `bzImage` that verifies from the two GPT boot partitions. It falls back to the other slot only if that is not a downgrade (see Anti-rollback), and halts if neither boots. Program the flash with a Dediprog SF100 on JP4 while the board is off; the BIOS region lock blocks `flashrom` from the booted OS. JP36 selects which bank boots and which one the programmer sees; move it only with the board off. +wolfBoot boots the highest-versioned signed 64-bit `bzImage` or Multiboot2 ELF that verifies from the two GPT boot partitions. It falls back to the other slot only if that is not a downgrade (see Anti-rollback), and halts if neither boots. Before handoff it sets FULL_RST in the Reset Control register (0xCF9), as the stock BIOS does, so a later reset such as SYS_RESET# from the IPMC is a full reset. A kernel command line signed into the OS image (`sign --cmdline`, the `HDR_CMDLINE` TLV) replaces the built-in one, so changing a kernel argument does not need a reflash. Program the flash with a Dediprog SF100 on JP4 while the board is off; the BIOS region lock blocks `flashrom` from the booted OS. JP36 selects which bank boots and which one the programmer sees; move it only with the board off. + +### Multiboot2 ELF payloads + +The configuration builds both payload paths (`LINUX_PAYLOAD=1`, `ELF=1`, `MULTIBOOT2=1`), and `do_boot()` picks one per image: an image that starts with the ELF magic takes the Multiboot2 path, and anything else boots as Linux. A Multiboot2 RTOS image is signed and written to an A/B slot the same way as a kernel: + +``` +./tools/keytools/sign --ecc384 --sha384 wolfboot_signing_private_key.der +``` + +The two paths differ only in the handoff: + +| | Linux `bzImage` | Multiboot2 ELF | +|---|---|---| +| Entry | 64-bit Linux boot protocol, `boot_params` and the command line | ELF segments loaded at their physical addresses, virtual addresses mapped in wolfBoot's page tables, jump to `e_entry` in 64-bit mode with EAX=`0x36d76289` and EBX pointing to the Multiboot2 information | +| Memory map | E820 table in `boot_params` | Multiboot2 memory map and basic memory information tags, from the FSP HOBs | +| ACPI tables | Generated (`ACPI=1`) | None; they are only built on the Linux path | +| RTC | The legacy CMOS RTC is ignored under the hardware-reduced ACPI tables; the I2C0 RTC is declared in the DSDT | The CMOS RTC is usable directly; I2C0 is enabled for the board RTC | + +Segments are loaded from the verified image only, and a segment that would overlap wolfBoot's own memory (from the stage 2 load address up to 4 GB) is rejected before anything is copied. Everything else is shared: FSP silicon setup (VT-d at the stock BIOS addresses, Hyper-Threading off), the halted PCH TCO watchdog, the BIOS region lock, measured boot, anti-rollback and A/B selection. ## Infineon PSOC Control C3 diff --git a/hal/nai_68int6.c b/hal/nai_68int6.c index 3b90d90981..0bdf148270 100644 --- a/hal/nai_68int6.c +++ b/hal/nai_68int6.c @@ -40,8 +40,16 @@ void hal_init(void) fsp_init_silicon(); } +/* Reset Control (0xCF9) FULL_RST: written alone it starts no reset, but makes + * any later reset a full one, as the stock BIOS does before the OS loader + * (its OS Reset Select default, Cold Reset). That covers SYS_RESET# from the + * IPMC, which is otherwise a warm reset. */ +#define RST_CNT_PORT 0xcf9 +#define RST_CNT_FULL_RST (1 << 3) + void hal_prepare_boot(void) { + io_write8(RST_CNT_PORT, RST_CNT_FULL_RST); } #endif /* __WOLFBOOT */ diff --git a/hal/x86_fsp_tgl.c b/hal/x86_fsp_tgl.c index b0f5889d43..8dae2231e6 100644 --- a/hal/x86_fsp_tgl.c +++ b/hal/x86_fsp_tgl.c @@ -21,6 +21,7 @@ #include "wolfboot/wolfboot.h" #include +#include #ifdef __WOLFBOOT @@ -190,6 +191,156 @@ int hal_flash_protect(haladdr_t address, int len) * hook's address/len are the same range FREG1 describes. */ return tgl_lock_bios_region(); } + +#ifdef WOLFBOOT_FSP_NVS_CACHE +/* Hardware-sequencing flash cycles, used to save the FSP memory training + * data before the BIOS region is locked. Runs from RAM (stage 2) only. */ +#define SPI_BIOS_CNTL (0xdc) +#define SPI_BIOS_CNTL_WPD (1U << 0) +#define SPI_FADDR (0x8) +#define SPI_FDATA0 (0x10) +#define SPI_HSFSTS_FDONE (1U << 0) +#define SPI_HSFSTS_FCERR (1U << 1) +#define SPI_HSFSTS_AEL (1U << 2) +#define SPI_HSFSTS_SCIP (1U << 5) +#define SPI_HSFSTS_FGO (1U << 16) +#define SPI_HSFSTS_FCYCLE_SHIFT (17) +#define SPI_HSFSTS_FDBC_SHIFT (24) +#define SPI_FCYCLE_WRITE (2) +#define SPI_FCYCLE_ERASE_4K (3) +#define SPI_HWSEQ_MAX_DATA (64) +#define SPI_ERASE_SIZE (0x1000) +#define SPI_HWSEQ_TIMEOUT_MS (1000) + +static uint32_t tgl_spi_begin(uint32_t *cmd) +{ + uint32_t spi_bar, cntl; + + spi_bar = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_BAR0_OFFSET); + spi_bar &= PCI_BAR_MASK; + *cmd = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET); + pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET, + *cmd | PCI_COMMAND_MEM_SPACE); + cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL); + pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL, + cntl | SPI_BIOS_CNTL_WPD); + if ((pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL) & + SPI_BIOS_CNTL_WPD) == 0) + return 0; + return spi_bar; +} + +static void tgl_spi_end(uint32_t cmd) +{ + uint8_t cntl; + + cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL); + pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL, + cntl & ~SPI_BIOS_CNTL_WPD); + pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET, cmd); +} + +/* The BIOS region is mapped so that its last byte sits at 4 GB - 1; + * returns the flash linear address of the memory-mapped address, or -1 + * when the range is outside the BIOS region. */ +static int64_t tgl_spi_linear(uint32_t spi_bar, uint32_t address, + uint32_t len) +{ + uint32_t reg, base, top; + uint64_t lin; + + reg = mmio_read32(spi_bar + SPI_FREG1); + base = (reg & SPI_FREG_BASE_MASK) << SPI_FREG_ADDR_SHIFT; + top = (((reg & SPI_FREG_LIMIT_MASK) >> SPI_FREG_LIMIT_SHIFT) + << SPI_FREG_ADDR_SHIFT) | 0xfffU; + if (top < base || len == 0) + return -1; + lin = (uint64_t)top - (0xffffffffULL - address); + if (lin < base || lin + len - 1 > top) + return -1; + return (int64_t)lin; +} + +static int tgl_spi_cycle(uint32_t spi_bar, uint32_t cycle, uint32_t faddr, + uint32_t len) +{ + uint32_t sts = 0; + int ms; + + for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) { + if ((mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL) & + SPI_HSFSTS_SCIP) == 0) + break; + delay(1); + } + if (ms == SPI_HWSEQ_TIMEOUT_MS) + return -1; + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, + SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL); + mmio_write32(spi_bar + SPI_FADDR, faddr); + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, + ((len - 1) << SPI_HSFSTS_FDBC_SHIFT) | + (cycle << SPI_HSFSTS_FCYCLE_SHIFT) | SPI_HSFSTS_FGO); + for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) { + sts = mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL); + if (sts & (SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR)) + break; + delay(1); + } + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, + SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL); + if (ms == SPI_HWSEQ_TIMEOUT_MS || (sts & SPI_HSFSTS_FCERR)) + return -1; + return 0; +} + +int tgl_spi_flash_erase(uint32_t address, uint32_t len) +{ + uint32_t spi_bar, cmd, off; + int64_t lin; + int ret = 0; + + if ((address | len) & (SPI_ERASE_SIZE - 1)) + return -1; + spi_bar = tgl_spi_begin(&cmd); + lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1; + if (lin < 0) + ret = -1; + for (off = 0; ret == 0 && off < len; off += SPI_ERASE_SIZE) + ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_ERASE_4K, + (uint32_t)lin + off, 1); + tgl_spi_end(cmd); + return ret; +} + +int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len) +{ + uint32_t spi_bar, cmd, off, chunk, word, i; + int64_t lin; + int ret = 0; + + spi_bar = tgl_spi_begin(&cmd); + lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1; + if (lin < 0) + ret = -1; + for (off = 0; ret == 0 && off < len; off += chunk) { + /* 64-byte aligned cycles never cross a flash page */ + chunk = SPI_HWSEQ_MAX_DATA - (((uint32_t)lin + off) & + (SPI_HWSEQ_MAX_DATA - 1)); + if (chunk > len - off) + chunk = len - off; + for (i = 0; i < chunk; i += 4) { + word = 0xffffffffU; + memcpy(&word, buf + off + i, (chunk - i < 4) ? chunk - i : 4); + mmio_write32(spi_bar + SPI_FDATA0 + i, word); + } + ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_WRITE, (uint32_t)lin + off, + chunk); + } + tgl_spi_end(cmd); + return ret; +} +#endif /* WOLFBOOT_FSP_NVS_CACHE */ #endif /* WOLFBOOT_TGL */ #endif diff --git a/include/stage2_params.h b/include/stage2_params.h index ef3c6e2c81..2be2f3f169 100644 --- a/include/stage2_params.h +++ b/include/stage2_params.h @@ -31,6 +31,12 @@ struct stage2_parameter { /* 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; + /* Address of the signed HDR_CMDLINE of the verified image, NUL-terminated, + * or 0 when it carries none */ + uint32_t cmdline; +#ifdef WOLFBOOT_FSP_NVS_CACHE + uint32_t fsp_nvs_key; +#endif #ifdef WOLFBOOT_TPM_SEAL uint32_t tpm_policy; uint16_t tpm_policy_size; diff --git a/include/x86/acpi.h b/include/x86/acpi.h index 676353df5b..1c92eb6696 100644 --- a/include/x86/acpi.h +++ b/include/x86/acpi.h @@ -30,8 +30,8 @@ #endif #define ACPI_TABLE_SIZE 0x00010000UL /* 64 KiB */ -/* Build RSDP -> XSDT -> { FADT, MADT, MCFG, DSDT } at ACPI_TABLE_BASE and - * return the RSDP physical address for boot_params.acpi_rsdp_addr. */ +/* Build RSDP -> XSDT -> { FADT, MADT, MCFG, TPM2, HPET, DMAR, DSDT } at + * ACPI_TABLE_BASE and return the RSDP physical address for boot_params.acpi_rsdp_addr. */ uint64_t acpi_setup(void); #endif /* WOLFBOOT_ACPI_H */ diff --git a/include/x86/acpi_dsdt.h b/include/x86/acpi_dsdt.h index 71b4ebbc20..2d84992f75 100644 --- a/include/x86/acpi_dsdt.h +++ b/include/x86/acpi_dsdt.h @@ -22,17 +22,18 @@ * Regenerate: iasl src/x86/acpi_dsdt.asl && * xxd -i -n acpi_dsdt_aml src/x86/acpi_dsdt.aml (append below this header). */ static const unsigned char acpi_dsdt_aml[] = { - 0x44, 0x53, 0x44, 0x54, 0x1e, 0x02, 0x00, 0x00, 0x02, 0x48, 0x57, 0x4f, + 0x44, 0x53, 0x44, 0x54, 0x15, 0x07, 0x00, 0x00, 0x02, 0xfb, 0x57, 0x4f, 0x4c, 0x46, 0x42, 0x54, 0x4e, 0x41, 0x49, 0x54, 0x47, 0x4c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x28, 0x06, 0x23, 0x20, - 0x10, 0x49, 0x1f, 0x5f, 0x53, 0x42, 0x5f, 0x5b, 0x82, 0x41, 0x1f, 0x50, + 0x10, 0x40, 0x6f, 0x5f, 0x53, 0x42, 0x5f, 0x5b, 0x82, 0x42, 0x66, 0x50, 0x43, 0x49, 0x30, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0a, 0x08, 0x08, 0x5f, 0x43, 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0a, 0x03, 0x08, 0x5f, 0x53, 0x45, 0x47, 0x00, 0x08, 0x5f, 0x42, 0x42, 0x4e, 0x00, 0x08, - 0x5f, 0x55, 0x49, 0x44, 0x00, 0x14, 0x46, 0x12, 0x5f, 0x50, 0x52, 0x54, - 0x00, 0xa4, 0x12, 0x4d, 0x11, 0x17, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, + 0x5f, 0x55, 0x49, 0x44, 0x00, 0x14, 0x42, 0x13, 0x5f, 0x50, 0x52, 0x54, + 0x00, 0xa4, 0x12, 0x49, 0x12, 0x18, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, + 0x06, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x14, 0x00, 0x01, 0x00, 0x0a, 0x11, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, @@ -54,19 +55,124 @@ static const unsigned char acpi_dsdt_aml[] = { 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x1e, 0x00, 0x01, 0x00, 0x0a, 0x11, 0x12, 0x0c, 0x04, 0x0c, 0xff, 0xff, 0x1e, 0x00, 0x0a, 0x02, 0x00, 0x0a, 0x24, 0x12, 0x0b, 0x04, 0x0c, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x0a, 0x10, - 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x48, 0x09, 0x0a, 0x94, 0x88, 0x0d, - 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x88, 0x0d, 0x00, 0x01, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, - 0xf7, 0x0c, 0x00, 0x00, 0xf8, 0x0c, 0x88, 0x0d, 0x00, 0x01, 0x0c, 0x03, - 0x00, 0x00, 0x00, 0x0d, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf3, 0x87, 0x17, - 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x87, 0x17, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0x8a, 0x2b, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x79, 0x00 + 0x14, 0x49, 0x04, 0x5f, 0x4f, 0x53, 0x43, 0x0c, 0x70, 0x6b, 0x60, 0x8a, + 0x60, 0x00, 0x43, 0x44, 0x57, 0x31, 0xa0, 0x28, 0x93, 0x68, 0x11, 0x13, + 0x0a, 0x10, 0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40, 0x96, 0x57, + 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66, 0xa0, 0x10, 0x92, 0x93, 0x69, 0x01, + 0x7d, 0x43, 0x44, 0x57, 0x31, 0x0a, 0x08, 0x43, 0x44, 0x57, 0x31, 0xa1, + 0x0c, 0x7d, 0x43, 0x44, 0x57, 0x31, 0x0a, 0x04, 0x43, 0x44, 0x57, 0x31, + 0xa4, 0x60, 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x48, 0x09, 0x0a, 0x94, + 0x88, 0x0d, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x88, 0x0d, 0x00, 0x01, 0x0c, 0x03, 0x00, 0x00, + 0x00, 0x00, 0xf7, 0x0c, 0x00, 0x00, 0xf8, 0x0c, 0x88, 0x0d, 0x00, 0x01, + 0x0c, 0x03, 0x00, 0x00, 0x00, 0x0d, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf3, + 0x87, 0x17, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x87, 0x17, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x8a, 0x2b, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x79, 0x00, 0x5b, 0x82, 0x42, 0x04, 0x50, 0x45, 0x47, 0x30, + 0x08, 0x5f, 0x41, 0x44, 0x52, 0x0c, 0x00, 0x00, 0x06, 0x00, 0x08, 0x5f, + 0x50, 0x52, 0x54, 0x12, 0x2c, 0x04, 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, + 0x00, 0x00, 0x0a, 0x10, 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, 0x01, 0x00, + 0x0a, 0x11, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, 0x0a, 0x02, 0x00, 0x0a, + 0x12, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, 0x0a, 0x03, 0x00, 0x0a, 0x13, + 0x5b, 0x82, 0x42, 0x04, 0x52, 0x50, 0x30, 0x35, 0x08, 0x5f, 0x41, 0x44, + 0x52, 0x0c, 0x00, 0x00, 0x1c, 0x00, 0x08, 0x5f, 0x50, 0x52, 0x54, 0x12, + 0x2c, 0x04, 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, 0x00, 0x00, 0x0a, 0x10, + 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, 0x01, 0x00, 0x0a, 0x11, 0x12, 0x0a, + 0x04, 0x0b, 0xff, 0xff, 0x0a, 0x02, 0x00, 0x0a, 0x12, 0x12, 0x0a, 0x04, + 0x0b, 0xff, 0xff, 0x0a, 0x03, 0x00, 0x0a, 0x13, 0x5b, 0x82, 0x42, 0x04, + 0x52, 0x50, 0x30, 0x39, 0x08, 0x5f, 0x41, 0x44, 0x52, 0x0c, 0x00, 0x00, + 0x1d, 0x00, 0x08, 0x5f, 0x50, 0x52, 0x54, 0x12, 0x2c, 0x04, 0x12, 0x09, + 0x04, 0x0b, 0xff, 0xff, 0x00, 0x00, 0x0a, 0x10, 0x12, 0x09, 0x04, 0x0b, + 0xff, 0xff, 0x01, 0x00, 0x0a, 0x11, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, + 0x0a, 0x02, 0x00, 0x0a, 0x12, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, 0x0a, + 0x03, 0x00, 0x0a, 0x13, 0x5b, 0x82, 0x42, 0x04, 0x52, 0x50, 0x31, 0x30, + 0x08, 0x5f, 0x41, 0x44, 0x52, 0x0c, 0x01, 0x00, 0x1d, 0x00, 0x08, 0x5f, + 0x50, 0x52, 0x54, 0x12, 0x2c, 0x04, 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, + 0x00, 0x00, 0x0a, 0x11, 0x12, 0x09, 0x04, 0x0b, 0xff, 0xff, 0x01, 0x00, + 0x0a, 0x12, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, 0x0a, 0x02, 0x00, 0x0a, + 0x13, 0x12, 0x0a, 0x04, 0x0b, 0xff, 0xff, 0x0a, 0x03, 0x00, 0x0a, 0x10, + 0x5b, 0x82, 0x49, 0x25, 0x49, 0x32, 0x43, 0x30, 0x08, 0x5f, 0x41, 0x44, + 0x52, 0x0c, 0x00, 0x00, 0x15, 0x00, 0x5b, 0x82, 0x43, 0x0c, 0x45, 0x45, + 0x50, 0x32, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0d, 0x31, 0x35, 0x41, 0x43, + 0x30, 0x30, 0x30, 0x32, 0x00, 0x08, 0x5f, 0x43, 0x49, 0x44, 0x0d, 0x50, + 0x52, 0x50, 0x30, 0x30, 0x30, 0x31, 0x00, 0x08, 0x5f, 0x44, 0x44, 0x4e, + 0x0d, 0x4e, 0x41, 0x49, 0x20, 0x4d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x62, + 0x6f, 0x61, 0x72, 0x64, 0x20, 0x45, 0x45, 0x50, 0x52, 0x4f, 0x4d, 0x00, + 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x26, 0x0a, 0x23, 0x8e, 0x1e, 0x00, + 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x06, 0x00, 0xa0, 0x86, 0x01, + 0x00, 0x56, 0x00, 0x5c, 0x5f, 0x53, 0x42, 0x2e, 0x50, 0x43, 0x49, 0x30, + 0x2e, 0x49, 0x32, 0x43, 0x30, 0x00, 0x79, 0x00, 0x08, 0x5f, 0x44, 0x53, + 0x44, 0x12, 0x41, 0x05, 0x02, 0x11, 0x13, 0x0a, 0x10, 0x14, 0xd8, 0xff, + 0xda, 0xba, 0x6e, 0x8c, 0x4d, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, + 0x01, 0x12, 0x39, 0x03, 0x12, 0x1b, 0x02, 0x0d, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x00, 0x0d, 0x61, 0x74, 0x6d, 0x65, + 0x6c, 0x2c, 0x32, 0x34, 0x63, 0x30, 0x32, 0x00, 0x12, 0x0b, 0x02, 0x0d, + 0x73, 0x69, 0x7a, 0x65, 0x00, 0x0b, 0x00, 0x01, 0x12, 0x0e, 0x02, 0x0d, + 0x70, 0x61, 0x67, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x0a, 0x08, 0x5b, + 0x82, 0x46, 0x0d, 0x45, 0x45, 0x50, 0x33, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0d, 0x31, 0x35, 0x41, 0x43, 0x30, 0x30, 0x30, 0x33, 0x00, 0x08, 0x5f, + 0x43, 0x49, 0x44, 0x0d, 0x50, 0x52, 0x50, 0x30, 0x30, 0x30, 0x31, 0x00, + 0x08, 0x5f, 0x44, 0x44, 0x4e, 0x0d, 0x4e, 0x41, 0x49, 0x20, 0x4d, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x45, 0x45, + 0x50, 0x52, 0x4f, 0x4d, 0x20, 0x53, 0x4e, 0x00, 0x08, 0x5f, 0x43, 0x52, + 0x53, 0x11, 0x26, 0x0a, 0x23, 0x8e, 0x1e, 0x00, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x01, 0x06, 0x00, 0xa0, 0x86, 0x01, 0x00, 0x5e, 0x00, 0x5c, + 0x5f, 0x53, 0x42, 0x2e, 0x50, 0x43, 0x49, 0x30, 0x2e, 0x49, 0x32, 0x43, + 0x30, 0x00, 0x79, 0x00, 0x08, 0x5f, 0x44, 0x53, 0x44, 0x12, 0x41, 0x06, + 0x02, 0x11, 0x13, 0x0a, 0x10, 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, + 0x4d, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01, 0x12, 0x49, 0x04, + 0x04, 0x12, 0x1c, 0x02, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, + 0x62, 0x6c, 0x65, 0x00, 0x0d, 0x61, 0x74, 0x6d, 0x65, 0x6c, 0x2c, 0x32, + 0x34, 0x63, 0x73, 0x30, 0x32, 0x00, 0x12, 0x0a, 0x02, 0x0d, 0x73, 0x69, + 0x7a, 0x65, 0x00, 0x0a, 0x10, 0x12, 0x0e, 0x02, 0x0d, 0x70, 0x61, 0x67, + 0x65, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x0a, 0x08, 0x12, 0x0e, 0x02, 0x0d, + 0x72, 0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x01, 0x5b, + 0x82, 0x4a, 0x0a, 0x52, 0x54, 0x43, 0x31, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0d, 0x31, 0x35, 0x41, 0x43, 0x30, 0x30, 0x30, 0x34, 0x00, 0x08, 0x5f, + 0x43, 0x49, 0x44, 0x0d, 0x50, 0x52, 0x50, 0x30, 0x30, 0x30, 0x31, 0x00, + 0x08, 0x5f, 0x44, 0x44, 0x4e, 0x0d, 0x4e, 0x41, 0x49, 0x20, 0x4d, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x57, 0x44, + 0x54, 0x20, 0x52, 0x54, 0x43, 0x00, 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, + 0x26, 0x0a, 0x23, 0x8e, 0x1e, 0x00, 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x01, 0x06, 0x00, 0xa0, 0x86, 0x01, 0x00, 0x68, 0x00, 0x5c, 0x5f, 0x53, + 0x42, 0x2e, 0x50, 0x43, 0x49, 0x30, 0x2e, 0x49, 0x32, 0x43, 0x30, 0x00, + 0x79, 0x00, 0x08, 0x5f, 0x44, 0x53, 0x44, 0x12, 0x37, 0x02, 0x11, 0x13, + 0x0a, 0x10, 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, 0x8a, 0x91, + 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01, 0x12, 0x20, 0x01, 0x12, 0x1d, 0x02, + 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x00, + 0x0d, 0x64, 0x61, 0x6c, 0x6c, 0x61, 0x73, 0x2c, 0x64, 0x73, 0x31, 0x33, + 0x37, 0x34, 0x00, 0x5b, 0x82, 0x4e, 0x0a, 0x53, 0x50, 0x49, 0x30, 0x08, + 0x5f, 0x41, 0x44, 0x52, 0x0c, 0x02, 0x00, 0x1e, 0x00, 0x5b, 0x82, 0x4c, + 0x09, 0x50, 0x52, 0x43, 0x30, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0d, 0x31, + 0x35, 0x41, 0x43, 0x30, 0x30, 0x30, 0x31, 0x00, 0x08, 0x5f, 0x53, 0x54, + 0x52, 0x11, 0x42, 0x05, 0x0a, 0x4e, 0x4e, 0x00, 0x41, 0x00, 0x49, 0x00, + 0x20, 0x00, 0x47, 0x00, 0x53, 0x00, 0x50, 0x00, 0x49, 0x00, 0x20, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x73, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, + 0x67, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x6f, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x29, 0x0a, 0x26, 0x8e, 0x21, 0x00, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x09, 0x00, 0x20, 0xa1, 0x07, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x5f, 0x53, 0x42, 0x2e, 0x50, + 0x43, 0x49, 0x30, 0x2e, 0x53, 0x50, 0x49, 0x30, 0x00, 0x79, 0x00, 0x5b, + 0x82, 0x44, 0x08, 0x54, 0x50, 0x4d, 0x5f, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0d, 0x4d, 0x53, 0x46, 0x54, 0x30, 0x31, 0x30, 0x31, 0x00, 0x08, 0x5f, + 0x53, 0x54, 0x52, 0x11, 0x21, 0x0a, 0x1e, 0x54, 0x00, 0x50, 0x00, 0x4d, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x20, 0x00, 0x44, + 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x00, + 0x00, 0x5b, 0x80, 0x54, 0x50, 0x4d, 0x52, 0x00, 0x0c, 0x00, 0x00, 0xd4, + 0xfe, 0x0b, 0x00, 0x50, 0x5b, 0x81, 0x0b, 0x54, 0x50, 0x4d, 0x52, 0x00, + 0x41, 0x43, 0x43, 0x30, 0x08, 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x11, + 0x0a, 0x0e, 0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0xd4, 0xfe, 0x00, 0x50, + 0x00, 0x00, 0x79, 0x00, 0x14, 0x14, 0x5f, 0x53, 0x54, 0x41, 0x00, 0xa0, + 0x0a, 0x93, 0x41, 0x43, 0x43, 0x30, 0x0a, 0xff, 0xa4, 0x00, 0xa4, 0x0a, + 0x0f }; -static const unsigned int acpi_dsdt_aml_len = 542; +static const unsigned int acpi_dsdt_aml_len = 1813; diff --git a/include/x86/common.h b/include/x86/common.h index 954ae07a6f..6589df2e61 100644 --- a/include/x86/common.h +++ b/include/x86/common.h @@ -60,6 +60,8 @@ 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); +uint8_t cmos_read(uint8_t index); +void cmos_write(uint8_t index, uint8_t value); #ifdef WOLFBOOT_TGL int global_reset(void); #endif @@ -68,6 +70,9 @@ __attribute__((noreturn)) void panic(void); void cpuid(uint32_t eax_param, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); int cpuid_is_1gb_page_supported(void); +int x86_cpu_apic_ids(uint8_t *ids, int max); +int x86_apic_ids_from_topology(uint32_t core_thread_count, uint32_t shift, + uint8_t *ids, int max); void switch_to_long_mode(uint64_t *entry, uint32_t page_table); void x86_log_memory_load(uint32_t start, uint32_t end, const char *name); void hlt(void); diff --git a/include/x86/fsp_config.h b/include/x86/fsp_config.h index 3689c638fd..7882763e3b 100644 --- a/include/x86/fsp_config.h +++ b/include/x86/fsp_config.h @@ -6,9 +6,9 @@ #ifndef FSP_HYPERTHREADING #define FSP_HYPERTHREADING 0 #endif -#if FSP_HYPERTHREADING -/* The MADT and MP table list four single-thread cores (APIC IDs 0/2/4/6). */ -#error "FSP_HYPERTHREADING=1 needs sibling threads in the MADT and MP table" +#if FSP_HYPERTHREADING && !defined(TARGET_nai_68int6) +/* The Kontron MP table lists four single-thread cores (APIC IDs 0/2/4/6). */ +#error "FSP_HYPERTHREADING=1 needs sibling threads in the MP table" #endif #ifndef FSP_INTEL_SPEED_STEP @@ -41,4 +41,79 @@ #define FSP_THERMAL_THROTTLING 1 #endif +/* PCIe switch lane configuration, strapped on GPP_B21/B22 (MUX_SEL1/0) and + * GPP_S4/S7/S6 (BOOTCFG2/1/0); the BIOS default is 8x1 */ +#if defined(NAI_68INT6_PCIE_LANES_1x4_4x1) +#define NAI_PCIE_STRAP_MUX_SEL1 1 +#define NAI_PCIE_STRAP_MUX_SEL0 0 +#define NAI_PCIE_STRAP_BOOTCFG2 0 +#define NAI_PCIE_STRAP_BOOTCFG1 0 +#define NAI_PCIE_STRAP_BOOTCFG0 1 +#elif defined(NAI_68INT6_PCIE_LANES_4x1_1x4) +#define NAI_PCIE_STRAP_MUX_SEL1 0 +#define NAI_PCIE_STRAP_MUX_SEL0 1 +#define NAI_PCIE_STRAP_BOOTCFG2 0 +#define NAI_PCIE_STRAP_BOOTCFG1 1 +#define NAI_PCIE_STRAP_BOOTCFG0 0 +#elif defined(NAI_68INT6_PCIE_LANES_1x4_1x4) +#define NAI_PCIE_STRAP_MUX_SEL1 0 +#define NAI_PCIE_STRAP_MUX_SEL0 0 +#define NAI_PCIE_STRAP_BOOTCFG2 0 +#define NAI_PCIE_STRAP_BOOTCFG1 1 +#define NAI_PCIE_STRAP_BOOTCFG0 1 +#elif defined(NAI_68INT6_PCIE_LANES_1x8) +#define NAI_PCIE_STRAP_MUX_SEL1 0 +#define NAI_PCIE_STRAP_MUX_SEL0 0 +#define NAI_PCIE_STRAP_BOOTCFG2 1 +#define NAI_PCIE_STRAP_BOOTCFG1 0 +#define NAI_PCIE_STRAP_BOOTCFG0 0 +#else /* 8x1 */ +#define NAI_PCIE_STRAP_MUX_SEL1 1 +#define NAI_PCIE_STRAP_MUX_SEL0 1 +#define NAI_PCIE_STRAP_BOOTCFG2 0 +#define NAI_PCIE_STRAP_BOOTCFG1 0 +#define NAI_PCIE_STRAP_BOOTCFG0 0 +#endif + +/* PCIe switch non-transparent mode (GPP_S2) */ +#ifndef NAI_68INT6_PCIE_NT +#define NAI_68INT6_PCIE_NT 0 +#endif +#if NAI_68INT6_PCIE_NT != 0 && NAI_68INT6_PCIE_NT != 1 +#error "NAI_68INT6_PCIE_NT must be 0 or 1" +#endif + +/* Backplane PCIe reference clock (GPP_A0) */ +#ifndef NAI_68INT6_BACKPLANE_CLK +#define NAI_68INT6_BACKPLANE_CLK 0 +#endif +#if NAI_68INT6_BACKPLANE_CLK != 0 && NAI_68INT6_BACKPLANE_CLK != 1 +#error "NAI_68INT6_BACKPLANE_CLK must be 0 or 1" +#endif + +/* Seconds to wait after the straps, before CPU PCIe link training */ +#ifndef NAI_68INT6_PCIE_LINK_DELAY_S +#define NAI_68INT6_PCIE_LINK_DELAY_S 0 +#endif +#if NAI_68INT6_PCIE_LINK_DELAY_S < 0 || NAI_68INT6_PCIE_LINK_DELAY_S > 5 +#error "NAI_68INT6_PCIE_LINK_DELAY_S must be 0 to 5" +#endif + +/* Seconds to wait before polling PCIE_RDY */ +#ifndef NAI_68INT6_BOOT_DELAY_S +#define NAI_68INT6_BOOT_DELAY_S 0 +#endif +#if NAI_68INT6_BOOT_DELAY_S < 0 || NAI_68INT6_BOOT_DELAY_S > 255 +#error "NAI_68INT6_BOOT_DELAY_S must be 0 to 255" +#endif + +/* Extra seconds added to the 5 s PCIE_RDY timeout */ +#ifndef NAI_68INT6_FPGA_READY_DELAY_S +#define NAI_68INT6_FPGA_READY_DELAY_S 0 +#endif +#if NAI_68INT6_FPGA_READY_DELAY_S != 0 && \ + NAI_68INT6_FPGA_READY_DELAY_S != 5 && NAI_68INT6_FPGA_READY_DELAY_S != 10 +#error "NAI_68INT6_FPGA_READY_DELAY_S must be 0, 5 or 10" +#endif + #endif diff --git a/include/x86/fsp_nvs.h b/include/x86/fsp_nvs.h new file mode 100644 index 0000000000..920ab607a3 --- /dev/null +++ b/include/x86/fsp_nvs.h @@ -0,0 +1,48 @@ +/* fsp_nvs.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#ifndef FSP_NVS_H +#define FSP_NVS_H + +#include + +/* Flash record holding the FSP-M non-volatile storage (memory training) + * data. The key covers the FSP-M image and its UPDs, so a change to either + * invalidates the record. */ +#define FSP_NVS_MAGIC 0x5356464BU /* "KFVS" */ + +struct fsp_nvs_record { + uint32_t magic; + uint32_t key; + uint32_t len; + uint32_t crc; + uint32_t reserved[4]; + uint8_t data[]; +} __attribute__((packed)); + +uint32_t fsp_nvs_crc32(uint32_t crc, const uint8_t *buf, uint32_t len); +int fsp_nvs_record_valid(const struct fsp_nvs_record *rec, uint32_t key, + uint32_t area_size); +int fsp_nvs_apply(uint8_t *udp_m, uint32_t udp_size, const uint8_t *fsp_m, + uint32_t fsp_m_size); +void fsp_nvs_skip(int skip); +void fsp_nvs_save(void); + +#endif /* FSP_NVS_H */ diff --git a/include/x86/gpio_config.h b/include/x86/gpio_config.h index 73bcd468fe..d4e03620be 100644 --- a/include/x86/gpio_config.h +++ b/include/x86/gpio_config.h @@ -39,6 +39,10 @@ #define GPIO_GPPC_B9_CFG_OFF (0x790) #define GPIO_GPPC_B10_CFG_OFF (0x7a0) +#define GPIO_GPPC_B15_CFG_OFF (0x7f0) +#define GPIO_GPPC_B16_CFG_OFF (0x800) +#define GPIO_GPPC_B17_CFG_OFF (0x810) +#define GPIO_GPPC_B18_CFG_OFF (0x820) #define GPIO_GPPC_C6_CFG_OFF (0x760) #define GPIO_GPPC_C7_CFG_OFF (0x770) #define GPIO_GPPC_C8_CFG_OFF (0x780) @@ -49,6 +53,8 @@ #define GPIO_GPPC_C13_CFG_OFF (0x7d0) #define GPIO_GPPC_C14_CFG_OFF (0x7e0) #define GPIO_GPPC_C15_CFG_OFF (0x7f0) +#define GPIO_GPPC_C16_CFG_OFF (0x800) +#define GPIO_GPPC_C17_CFG_OFF (0x810) #define GPIO_GPPC_C20_CFG_OFF (0x840) #define GPIO_GPPC_C21_CFG_OFF (0x850) #define GPIO_GPPC_C22_CFG_OFF (0x860) @@ -65,13 +71,30 @@ #define GPIO_GPPC_E12_CFG_OFF (0xb30) #define GPIO_GPPC_E15_CFG_OFF (0xb60) #define GPIO_GPPC_E16_CFG_OFF (0xb70) -#define GPIO_GPPC_F2_CFG_OFF (0xba0) -#define GPIO_GPPC_F4_CFG_OFF (0xbc0) -#define GPIO_GPPC_F5_CFG_OFF (0xbd0) +#define GPIO_GPPC_E19_CFG_OFF (0xba0) +#define GPIO_GPPC_E21_CFG_OFF (0xbc0) +#define GPIO_GPPC_E22_CFG_OFF (0xbd0) #define GPIO_GPPC_F9_CFG_OFF (0x910) #define GPIO_GPPC_A9_CFG_OFF (0xa30) #define GPIO_GPPC_T2_CFG_OFF (0x8c0) #define GPIO_GPPC_T3_CFG_OFF (0x8d0) +#define GPIO_GPPC_A0_CFG_OFF (0x9a0) +#define GPIO_GPPC_B12_CFG_OFF (0x7c0) +#define GPIO_GPPC_B21_CFG_OFF (0x850) +#define GPIO_GPPC_B22_CFG_OFF (0x860) +#define GPIO_GPPC_C23_CFG_OFF (0x870) +#define GPIO_GPPC_S0_CFG_OFF (0x700) +#define GPIO_GPPC_S1_CFG_OFF (0x710) +#define GPIO_GPPC_S2_CFG_OFF (0x720) +#define GPIO_GPPC_S3_CFG_OFF (0x730) +#define GPIO_GPPC_S4_CFG_OFF (0x740) +#define GPIO_GPPC_S5_CFG_OFF (0x750) +#define GPIO_GPPC_S6_CFG_OFF (0x760) +#define GPIO_GPPC_S7_CFG_OFF (0x770) +#define GPIO_GPPC_H6_CFG_OFF (0x7e0) +#define GPIO_GPPC_H7_CFG_OFF (0x7f0) +#define GPIO_GPPC_H8_CFG_OFF (0x800) +#define GPIO_GPPC_H9_CFG_OFF (0x810) #define GPIO_OWN_MASK (0x3) /* struct tgl_gpio, enum gpio_config_flags and struct tgl_gpio_conf are defined @@ -121,8 +144,242 @@ static const struct tgl_gpio_conf gpio_table_tempram[] = { .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, }; -/* Nothing is required between tempram and memory init on this board. */ +/* PCIe switch and backplane straps, in the order the stock BIOS writes + * them, before memory init. */ static const struct tgl_gpio_conf gpio_table_premem[] = { + /* PAD S0: low */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S0_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S1: low */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S1_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S3: low */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S3_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S5: low */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S5_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S2: PCIe switch NT (low = transparent) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S2_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_68INT6_PCIE_NT, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD B12: low until PCIE_RDY, then driven high */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B12_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD B21: PCIe lane strap MUX_SEL1 */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B21_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_PCIE_STRAP_MUX_SEL1, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD B22: PCIe lane strap MUX_SEL0 */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B22_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_PCIE_STRAP_MUX_SEL0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S4: PCIe lane strap BOOTCFG2 */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S4_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_PCIE_STRAP_BOOTCFG2, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S7: PCIe lane strap BOOTCFG1 */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S7_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_PCIE_STRAP_BOOTCFG1, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD S6: PCIe lane strap BOOTCFG0 */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_S6_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_PCIE_STRAP_BOOTCFG0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD A0: backplane PCIe clock (low = off) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_A0_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = NAI_68INT6_BACKPLANE_CLK, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD C23: PCIE_RDY input (high = ready) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_4_PORT_ID, + .cfg_offset = GPIO_GPPC_C23_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_INPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, }; /* The rest of the stock pad settings, applied after silicon init. */ @@ -255,19 +512,20 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | - GPIO_SET_RXEVCONF), + GPIO_SET_RXEVCONF | GPIO_SET_TX), .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_OUTPUT, .gpio_term = GPIO_TERM_NONE, .gpio_interrupt = 0x00, .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, + .gpio_tx = 1, .gpio_reset = GPIO_RESET_PLTRST}, - /* PAD F2 (DW0=0x44001600 DW1=0x00003C3F) */ + /* PAD E19 (DW0=0x44001600 DW1=0x00003C3F) */ {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F2_CFG_OFF, + .cfg_offset = GPIO_GPPC_E19_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | @@ -306,13 +564,14 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | - GPIO_SET_RXEVCONF), + GPIO_SET_RXEVCONF | GPIO_SET_TX), .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_OUTPUT, .gpio_term = GPIO_TERM_NONE, .gpio_interrupt = 0x00, .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, + .gpio_tx = 1, .gpio_reset = GPIO_RESET_PLTRST}, /* PAD E15 (DW0=0x44000B00 DW1=0x0000003B) */ {.gpio = @@ -357,13 +616,14 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | - GPIO_SET_RXEVCONF), + GPIO_SET_RXEVCONF | GPIO_SET_TX), .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_OUTPUT, .gpio_term = GPIO_TERM_NONE, .gpio_interrupt = 0x00, .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, + .gpio_tx = 1, .gpio_reset = GPIO_RESET_PLTRST}, /* PAD A9 (DW0=0x44000B00 DW1=0x00000049) */ {.gpio = @@ -382,11 +642,11 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, - /* PAD F4 (DW0=0x44001600 DW1=0x00003C41) */ + /* PAD E21 (DW0=0x44001600 DW1=0x00003C41) */ {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F4_CFG_OFF, + .cfg_offset = GPIO_GPPC_E21_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | @@ -399,11 +659,11 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, - /* PAD F5 (DW0=0x84000200 DW1=0x00000042) */ + /* PAD E22 (DW0=0x84000200 DW1=0x00000042) */ {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F5_CFG_OFF, + .cfg_offset = GPIO_GPPC_E22_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | @@ -450,7 +710,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, - /* PAD C22 (DW0=0x44000102 DW1=0x00000024) */ + /* PAD C22 (DW0=0x42100102 DW1=0x00003024) */ {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, @@ -462,10 +722,10 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { GPIO_SET_RXEVCONF), .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_INPUT, - .gpio_term = GPIO_TERM_NONE, - .gpio_interrupt = 0x00, + .gpio_term = 0x0C, + .gpio_interrupt = 0x08, .gpio_rxinv = 0x00, - .gpio_rxevconf = 0x02, + .gpio_rxevconf = 0x01, .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, /* PAD B10 (DW0=0x44000300 DW1=0x00000022) */ {.gpio = @@ -552,7 +812,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_PLTRST}, - /* PAD D0 (DW0=0x84000102 DW1=0x0000002C) */ + /* PAD D0 (DW0=0x82100102 DW1=0x0000002C) */ {.gpio = { .comm_port_id = GPIO_COMM_1_PORT_ID, @@ -565,11 +825,11 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_INPUT, .gpio_term = GPIO_TERM_NONE, - .gpio_interrupt = 0x00, + .gpio_interrupt = 0x08, .gpio_rxinv = 0x00, - .gpio_rxevconf = 0x02, + .gpio_rxevconf = 0x01, .gpio_reset = GPIO_RESET_PLTRST}, - /* PAD D2 (DW0=0x84000201 DW1=0x0000002E) */ + /* PAD D2: held low; the OS drives it high for the IPMC */ {.gpio = { .comm_port_id = GPIO_COMM_1_PORT_ID, @@ -578,13 +838,14 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | - GPIO_SET_RXEVCONF), + GPIO_SET_RXEVCONF | GPIO_SET_TX), .gpio_mode = GPIO_MODE_GPIO, .gpio_dir = GPIO_DIR_OUTPUT, .gpio_term = GPIO_TERM_NONE, .gpio_interrupt = 0x00, .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, + .gpio_tx = 0, .gpio_reset = GPIO_RESET_PLTRST}, /* PAD R0 (DW0=0x44000700 DW1=0x0003C058) */ {.gpio = @@ -688,6 +949,181 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_PLTRST}, + /* GSPI0 (B15-B18) and I2C0 (C16/C17) in native mode, as stock. */ + /* PAD B15 (DW0=0x44000601 DW1=0x00000027) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B15_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, + /* PAD B16 (DW0=0x84000601 DW1=0x00000028) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B16_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD B17 (DW0=0x44000702 DW1=0x00010029) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B17_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = 0x03, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, + /* PAD B18 (DW0=0x84000600 DW1=0x0000002A) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_0_PORT_ID, + .cfg_offset = GPIO_GPPC_B18_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD C16 (DW0=0x44000702 DW1=0x0000001E) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_4_PORT_ID, + .cfg_offset = GPIO_GPPC_C16_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = 0x03, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, + /* PAD C17 (DW0=0x44000702 DW1=0x0000001F) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_4_PORT_ID, + .cfg_offset = GPIO_GPPC_C17_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF), + .gpio_mode = GPIO_MODE_NATIVE_1, + .gpio_dir = 0x03, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, + /* PAD H6 (DW0=0x84000200 DW1=0x0000001A) */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_H6_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 0, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD H7 (DW0=0x84000201 DW1=0x0000001B) USB power enable */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_H7_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 1, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD H8 (DW0=0x84000201 DW1=0x0000001C) USB power enable */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_H8_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 1, + .gpio_reset = GPIO_RESET_PLTRST}, + /* PAD H9 (DW0=0x84000201 DW1=0x0000001D) USB power enable */ + {.gpio = + { + .comm_port_id = GPIO_COMM_1_PORT_ID, + .cfg_offset = GPIO_GPPC_H9_CFG_OFF, + }, + .flags = + (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_RESET | + GPIO_SET_TERM | GPIO_SET_INTERRUPT | GPIO_SET_RXINV | + GPIO_SET_RXEVCONF | GPIO_SET_TX), + .gpio_mode = GPIO_MODE_GPIO, + .gpio_dir = GPIO_DIR_OUTPUT, + .gpio_term = GPIO_TERM_NONE, + .gpio_interrupt = 0x00, + .gpio_rxinv = 0x00, + .gpio_rxevconf = 0x02, + .gpio_tx = 1, + .gpio_reset = GPIO_RESET_PLTRST}, }; #else /* TARGET_kontron_vx3060_s2 */ @@ -817,7 +1253,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F2_CFG_OFF, + .cfg_offset = GPIO_GPPC_E19_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), @@ -897,7 +1333,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F4_CFG_OFF, + .cfg_offset = GPIO_GPPC_E21_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), @@ -908,7 +1344,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F5_CFG_OFF, + .cfg_offset = GPIO_GPPC_E22_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), diff --git a/include/x86/mptable.h b/include/x86/mptable.h index 29ad25e53c..c82826110d 100644 --- a/include/x86/mptable.h +++ b/include/x86/mptable.h @@ -155,7 +155,13 @@ enum mp_irq_source_types { #define MP_INTSRC_NUM_ENTRY 17 #define MP_LINTSRC_NUM_ENTRY 0 #define MP_BUS_NUM_ENTRY 2 - #define MP_CPU_NUM_ENTRY 4 + #ifdef TARGET_nai_68int6 + /* one per logical processor, up to 4 cores with HT; unused entries + * are left disabled */ + #define MP_CPU_NUM_ENTRY 8 + #else + #define MP_CPU_NUM_ENTRY 4 + #endif #endif struct mptable { diff --git a/include/x86/tgl_fsp.h b/include/x86/tgl_fsp.h index 5145fbc185..2dfcf380ea 100644 --- a/include/x86/tgl_fsp.h +++ b/include/x86/tgl_fsp.h @@ -25,5 +25,7 @@ int x86_fsp_tgl_init_sata(uint32_t *bar); int tgl_lock_bios_region(void); +int tgl_spi_flash_erase(uint32_t address, uint32_t len); +int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len); #endif /* TGL_FSP_H */ diff --git a/options.mk b/options.mk index 0592944472..8acf6f1c16 100644 --- a/options.mk +++ b/options.mk @@ -63,6 +63,26 @@ ifeq ($(TARGET),nai_68int6) ifneq ($(ACPI),1) $(error nai_68int6 needs ACPI=1: its PCI interrupt routing is only in the DSDT) endif + # PCIe switch straps and start-up delays (see docs/Targets.md) + NAI_68INT6_PCIE_LANES?=8x1 + NAI_68INT6_PCIE_LANE_CONFIGS:=8x1 1x4_4x1 4x1_1x4 1x4_1x4 1x8 + ifeq ($(filter $(NAI_68INT6_PCIE_LANES),$(NAI_68INT6_PCIE_LANE_CONFIGS)),) + $(error NAI_68INT6_PCIE_LANES must be one of: $(NAI_68INT6_PCIE_LANE_CONFIGS)) + endif + CFLAGS+=-DNAI_68INT6_PCIE_LANES_$(NAI_68INT6_PCIE_LANES) + $(foreach option,FSP_HYPERTHREADING NAI_68INT6_PCIE_NT NAI_68INT6_BACKPLANE_CLK \ + NAI_68INT6_PCIE_LINK_DELAY_S NAI_68INT6_BOOT_DELAY_S \ + NAI_68INT6_FPGA_READY_DELAY_S, \ + $(if $($(option)),$(eval CFLAGS+=-D$(option)=$($(option))))) +endif + +# These options need wolfTPM and turn it on, so an explicit WOLFTPM=0 alongside +# one of them is a conflict rather than something to override silently. +ifeq ($(WOLFTPM),0) + ifneq ($(filter 1,$(ANTI_ROLLBACK) $(MEASURED_BOOT) $(WOLFBOOT_TPM_VERIFY) \ + $(WOLFBOOT_TPM_KEYSTORE) $(WOLFBOOT_TPM_SEAL)),) + $(error WOLFTPM=0 conflicts with ANTI_ROLLBACK, MEASURED_BOOT, WOLFBOOT_TPM_VERIFY, WOLFBOOT_TPM_KEYSTORE or WOLFBOOT_TPM_SEAL; set those to 0 too) + endif endif # TPM NV version floor and boot audit counters (see docs/Targets.md). @@ -1937,6 +1957,17 @@ ifeq ($(FSP), 1) # set CFLAGS defines for each x86_fsp option $(foreach option,$(X86_FSP_OPTIONS),$(if $($(option)), $(eval CFLAGS += -D$(option)=$($(option))))) + + ifeq ($(FSP_NVS_CACHE),1) + ifeq ($(and $(FSP_NVS_BASE),$(FSP_NVS_SIZE)),) + $(error FSP_NVS_CACHE=1 needs FSP_NVS_BASE and FSP_NVS_SIZE) + endif + ifeq ($(filter $(TARGET),kontron_vx3060_s2 nai_68int6),) + $(error FSP_NVS_CACHE=1 is only supported on the Tiger Lake targets) + endif + CFLAGS+=-DWOLFBOOT_FSP_NVS_CACHE + CFLAGS+=-DFSP_NVS_BASE=$(FSP_NVS_BASE) -DFSP_NVS_SIZE=$(FSP_NVS_SIZE) + endif endif ifeq ($(FLASH_MULTI_SECTOR_ERASE),1) diff --git a/src/boot_x86_fsp.c b/src/boot_x86_fsp.c index 1c13dc0c5e..79c2ec23fb 100644 --- a/src/boot_x86_fsp.c +++ b/src/boot_x86_fsp.c @@ -42,6 +42,12 @@ #include #include #endif +#ifdef WOLFBOOT_FSP_NVS_CACHE +#include +#endif +#ifdef WOLFBOOT_MEASURED_BOOT +#include +#endif #define WOLFBOOT_X86_STACK_SIZE 0x10000 @@ -262,10 +268,17 @@ static void jump_into_wolfboot(void) #endif /* WOLFBOOT_64BIT */ #if defined(WOLFBOOT_MEASURED_BOOT) -/* The image needs to be already verified */ +#if WOLFBOOT_TPM_PCR_DIG_SZ != 32 +#error "measured boot: the stage 1 re-hash only implements a SHA-256 PCR bank" +#endif +/* The image needs to be already verified. Its authenticated digest is + * re-hashed with SHA-256 and that value extended, as the OS measurement in + * update_disk.c is. */ int wolfBoot_image_measure(uint8_t *image) { struct wolfBoot_image img; + uint8_t digest[SHA256_DIGEST_SIZE]; + wc_Sha256 sha; int ret; memset(&img, 0, sizeof(img)); @@ -279,9 +292,18 @@ int wolfBoot_image_measure(uint8_t *image) return -1; } - wolfBoot_print_hexstr(img.sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, 0); - return wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_A, img.sha_hash, - __LINE__); + ret = wc_InitSha256(&sha); + if (ret != 0) + return -1; + ret = wc_Sha256Update(&sha, img.sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); + if (ret == 0) + ret = wc_Sha256Final(&sha, digest); + wc_Sha256Free(&sha); + if (ret != 0) + return -1; + + wolfBoot_print_hexstr(digest, sizeof(digest), 0); + return wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_A, digest, __LINE__); } #endif /* WOLFBOOT_MEASURED_BOOT */ @@ -542,6 +564,9 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp, uint32_t status; uint16_t type; uint32_t esp; +#if defined(WOLFBOOT_FSP_NVS_CACHE) && defined(BUILD_LOADER_STAGE1) + int nvs_used; +#endif (void)stack_top; (void)timestamp; @@ -586,6 +611,11 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp, if (status != 0) { panic(); } +#if defined(WOLFBOOT_FSP_NVS_CACHE) && defined(BUILD_LOADER_STAGE1) + nvs_used = fsp_nvs_apply(udp_m_parameter, + fsp_m_info_header->CfgRegionSize, fsp_m_base, + fsp_m_info_header->ImageSize); +#endif #if defined(WOLFBOOT_DUMP_FSP_UPD) wolfBoot_printf("Dumping fspm udp (%d bytes)" ENDLINE, (int)fsp_m_info_header->CfgRegionSize); @@ -604,8 +634,17 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp, fsp_handle_reset(status); if (status != EFI_SUCCESS) { wolfBoot_printf("failed: 0x%x" ENDLINE, status); +#if defined(WOLFBOOT_FSP_NVS_CACHE) && defined(BUILD_LOADER_STAGE1) + if (nvs_used) { + fsp_nvs_skip(1); + reset(0); + } +#endif panic(); } +#if defined(WOLFBOOT_FSP_NVS_CACHE) && defined(BUILD_LOADER_STAGE1) + fsp_nvs_skip(0); +#endif wolfBoot_printf("success" ENDLINE); status = get_top_address(&top_address, hobList); diff --git a/src/boot_x86_fsp_payload.c b/src/boot_x86_fsp_payload.c index c976687fc3..0f24447136 100644 --- a/src/boot_x86_fsp_payload.c +++ b/src/boot_x86_fsp_payload.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -94,48 +95,52 @@ void jump(uintptr_t entry) : "g"(entry)); } +#if defined(WOLFBOOT_ELF) +/* wolfBoot's own memory: stage 2, the staged payload after it, and the stack, + * page tables and FSP memory above them, all from the stage 2 load address + * up to 4 GB. */ +#define ELF_RESERVED_START ((uint64_t)WOLFBOOT_LOAD_BASE - IMAGE_HEADER_SIZE) +#define ELF_RESERVED_END (1ULL << 32) + +static int boot_elf_map_cb(uint64_t vaddr, uint64_t paddr, uint32_t size) +{ + if (size != 0 && paddr < ELF_RESERVED_END && + paddr + size > ELF_RESERVED_START) { + wolfBoot_printf("ELF: segment at 0x%x_%x overlaps wolfBoot memory\r\n", + (uint32_t)(paddr >> 32), (uint32_t)paddr); + return -1; + } +#ifdef WOLFBOOT_64BIT + return x86_paging_map_memory(vaddr, paddr, size); +#else + (void)vaddr; + return 0; +#endif +} + /** - * @brief Perform the boot process for the given application. + * @brief Load an ELF image and boot it, through Multiboot2 when enabled. * - * This function performs the boot process for the specified application. - * It either loads and boots a Linux payload or an ELF binary, depending on the - * configuration. - * - * @param app Pointer to the start of the application. + * @param app Pointer to the start of the ELF image. + * @param stage2_params Stage 2 parameters (memory layout and FSP HOBs). */ -void do_boot(const uint32_t *app) +static void boot_elf(const uint32_t *app, + struct stage2_parameter *stage2_params) { - struct stage2_parameter *stage2_params; - -#ifdef WOLFBOOT_TPM - wolfBoot_tpm2_deinit(); -#endif /* WOLFBOOT_TPM */ - - stage2_params = stage2_get_parameters(); -#if defined(WOLFBOOT_LINUX_PAYLOAD) - mptable_setup(); - load_linux((uint8_t *)app, stage2_params->payload_size, stage2_params, - cmdline); -#elif defined(WOLFBOOT_ELF) int r; - uint64_t e; + uint64_t e = 0; uint8_t *mb2_header; - elf_mmu_map_cb mmu_cb = NULL; - -#ifdef WOLFBOOT_64BIT - mmu_cb = x86_paging_map_memory; -#endif /* WOLFBOOT_64BIT */ /* TODO: to remove */ mptable_setup(); x86_paging_dump_info(); - /* Use available low memory as upper bound for ELF image size */ - if (stage2_params->tolum <= (uint32_t)(uintptr_t)app) { - wolfBoot_printf("ELF: app address beyond TOLUM\r\n"); + /* The disk loader records the verified payload size */ + if (stage2_params->payload_size == 0) { + wolfBoot_printf("ELF: no verified payload size\r\n"); panic(); } - r = elf_load_image_mmu((uint8_t *)app, - stage2_params->tolum - (uint32_t)(uintptr_t)app, &e, mmu_cb); + r = elf_load_image_mmu((uint8_t *)app, stage2_params->payload_size, &e, + boot_elf_map_cb); wolfBoot_printf("Elf loaded (ret %d), entry 0x%x_%x\r\n", r, (uint32_t)(e >> 32), (uint32_t)(e)); @@ -144,7 +149,8 @@ void do_boot(const uint32_t *app) panic(); #ifdef WOLFBOOT_MULTIBOOT2 - mb2_header = mb2_find_header((uint8_t*)app, 1024*1024*15); + mb2_header = mb2_find_header((uint8_t*)app, + (int)stage2_params->payload_size); if (mb2_header == NULL) { wolfBoot_printf("No mb2 header found\r\n"); panic(); @@ -163,7 +169,41 @@ void do_boot(const uint32_t *app) #else jump(e); #endif /* WOLFBOOT_MULTIBOOT2 */ +} +#endif /* WOLFBOOT_ELF */ + +/** + * @brief Perform the boot process for the given application. + * + * This function performs the boot process for the specified application. + * It either loads and boots a Linux payload or an ELF binary, depending on the + * configuration. With both built in, an image starting with the ELF magic + * takes the ELF path and anything else boots as Linux. + * + * @param app Pointer to the start of the application. + */ +void do_boot(const uint32_t *app) +{ + struct stage2_parameter *stage2_params; +#ifdef WOLFBOOT_TPM + wolfBoot_tpm2_deinit(); +#endif /* WOLFBOOT_TPM */ + + stage2_params = stage2_get_parameters(); +#if defined(WOLFBOOT_LINUX_PAYLOAD) && defined(WOLFBOOT_ELF) + if (memcmp(app, ELF_IDENT_STR, 4) == 0) { + boot_elf(app, stage2_params); + return; + } +#endif +#if defined(WOLFBOOT_LINUX_PAYLOAD) + mptable_setup(); + load_linux((uint8_t *)app, stage2_params->payload_size, stage2_params, + stage2_params->cmdline != 0 ? + (const char *)(uintptr_t)stage2_params->cmdline : cmdline); +#elif defined(WOLFBOOT_ELF) + boot_elf(app, stage2_params); #else #error "No payload compiled in" #endif /* WOLFBOOT_LINUX_PAYLOAD */ diff --git a/src/update_disk.c b/src/update_disk.c index 815969ad67..3bffddde1f 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -1559,6 +1559,28 @@ void RAMFUNCTION wolfBoot_start(void) boot_state_on_boot(booted_ver); } #endif +#ifdef WOLFBOOT_FSP + /* The image's signed kernel command line, read before the decrypted + * header is wiped */ + stage2_params->cmdline = 0; + { + static char disk_cmdline[256]; + uint8_t *val = NULL; + uint16_t len; + + len = wolfBoot_find_header((uint8_t *)hdr_ptr + IMAGE_HEADER_OFFSET, + HDR_CMDLINE, &val); + if (len > 0 && val != NULL && len < sizeof(disk_cmdline)) { + memcpy(disk_cmdline, val, len); + disk_cmdline[len] = '\0'; + stage2_params->cmdline = (uint32_t)(uintptr_t)disk_cmdline; + } + else if (len > 0) { + wolfBoot_printf("Signed cmdline too long (%d), using built-in\r\n", + (int)len); + } + } +#endif #ifdef DISK_ENCRYPT disk_decrypted_header_clear(dec_hdr); disk_crypto_clear(); diff --git a/src/x86/acpi.c b/src/x86/acpi.c index 8fa4129694..e5928c01e5 100644 --- a/src/x86/acpi.c +++ b/src/x86/acpi.c @@ -21,8 +21,8 @@ * * Minimal ACPI table generation for the x86 FSP (Tiger Lake) target. The FSP * does silicon init only and returns no usable ACPI table set, so wolfBoot - * builds RSDP -> XSDT -> { FADT, MADT, MCFG, DSDT } in low reserved memory and - * points boot_params.acpi_rsdp_addr at the RSDP. The MADT interrupt model and + * builds RSDP -> XSDT -> { FADT, MADT, MCFG, TPM2, HPET, DMAR, DSDT } in low + * reserved memory and points boot_params.acpi_rsdp_addr at the RSDP. The MADT interrupt model and * the DSDT _PRT routing are the values measured on this board; see docs and * acpi_dsdt.asl. Only the DSDT needs AML, embedded here as a precompiled blob. */ @@ -31,13 +31,15 @@ #include #include +#include +#include #include /* Local APIC and IOAPIC as the MP table already encodes them (mptable.c). */ #define ACPI_LAPIC_ADDR 0xFEE00000UL #define ACPI_IOAPIC_ADDR 0xFEC00000UL #define ACPI_IOAPIC_ID 0x02 -#define ACPI_CPU_COUNT 4 +#define ACPI_CPU_MAX 8 /* ECAM base and bus range, matching MCFG to config PCI_ECAM_BASE. */ #ifndef PCI_ECAM_BASE @@ -154,6 +156,71 @@ struct acpi_mcfg { struct acpi_mcfg_alloc alloc; } __attribute__((packed)); +/* TPM2 table for the TPM's TIS (FIFO) interface at 0xFED40000 */ +#define ACPI_TPM2_START_TIS 6 +struct acpi_tpm2 { + struct acpi_header header; + uint16_t platform_class; + uint16_t reserved; + uint64_t control_address; + uint32_t start_method; + uint8_t start_params[12]; +} __attribute__((packed)); + +#define ACPI_HPET_BASE 0xFED00000UL +struct acpi_hpet { + struct acpi_header header; + uint32_t block_id; + struct acpi_gas base; + uint8_t number; + uint16_t min_tick; + uint8_t page_protection; +} __attribute__((packed)); + +/* DMAR: the two Tiger Lake VT-d units at the addresses FSP-M is given, as + * the stock BIOS describes them */ +#define DMAR_GFX_UNIT 0xFED90000UL +#define DMAR_DEFAULT_UNIT 0xFED91000UL +#define DMAR_HAW 38 /* host address width - 1 */ +#define DMAR_FLAGS 0x05 /* INTR_REMAP | X2APIC_OPT_OUT */ +#define DMAR_DRHD 0 +#define DMAR_RMRR 1 +#define DMAR_INCLUDE_PCI_ALL 0x01 +#define DMAR_SCOPE_ENDPOINT 1 +#define DMAR_SCOPE_IOAPIC 3 +#define DMAR_SCOPE_HPET 4 +struct acpi_dmar { + struct acpi_header header; + uint8_t host_address_width; + uint8_t flags; + uint8_t reserved[10]; +} __attribute__((packed)); +struct dmar_drhd { + uint16_t type; + uint16_t length; + uint8_t flags; + uint8_t reserved; + uint16_t segment; + uint64_t base; +} __attribute__((packed)); +struct dmar_rmrr { + uint16_t type; + uint16_t length; + uint16_t reserved; + uint16_t segment; + uint64_t base; + uint64_t limit; +} __attribute__((packed)); +struct dmar_scope { + uint8_t type; + uint8_t length; + uint16_t reserved; + uint8_t enumeration_id; + uint8_t start_bus; + uint8_t dev; + uint8_t fn; +} __attribute__((packed)); + /* HW-reduced ACPI has no FACS, so the FADT leaves firmware_ctrl zero. */ /* ACPI 6.x FADT (fixed layout to the hypervisor id, 276 bytes). */ @@ -216,12 +283,6 @@ struct acpi_fadt { uint64_t hypervisor_vendor_id; } __attribute__((packed)); -/* acpi_processor_id -> Local APIC id: 4 cores with HT off (APIC ids - * 0/2/4/6). Listing absent threads stalls the kernel per missing AP. */ -static const uint8_t acpi_lapic_ids[ACPI_CPU_COUNT] = { - 0, 2, 4, 6 -}; - static void acpi_set_checksum(struct acpi_header *h) { uint8_t *bytes = (uint8_t *)h; @@ -265,18 +326,27 @@ static uint8_t *acpi_build_madt(uint8_t *p) struct madt_ioapic *ioapic; struct madt_iso *iso; struct madt_lapic_nmi *nmi; - uint32_t i; + uint8_t apic_ids[ACPI_CPU_MAX]; + int cpus; + int i; madt->lapic_address = (uint32_t)ACPI_LAPIC_ADDR; madt->flags = MADT_FLAG_PCAT_COMPAT; - for (i = 0; i < ACPI_CPU_COUNT; i++) { + /* Only the processors that are present: an absent one stalls the kernel + * while it waits for that AP */ + cpus = x86_cpu_apic_ids(apic_ids, ACPI_CPU_MAX); + if (cpus == 0) { + apic_ids[0] = (uint8_t)(mmio_read32(ACPI_LAPIC_ADDR + 0x20) >> 24); + cpus = 1; + } + for (i = 0; i < cpus; i++) { lapic = (struct madt_lapic *)e; memset(lapic, 0, sizeof(*lapic)); lapic->type = MADT_TYPE_LAPIC; lapic->length = sizeof(*lapic); lapic->acpi_processor_id = (uint8_t)i; - lapic->apic_id = acpi_lapic_ids[i]; + lapic->apic_id = apic_ids[i]; lapic->flags = MADT_LAPIC_ENABLED; e += sizeof(*lapic); } @@ -341,6 +411,99 @@ static uint8_t *acpi_build_mcfg(uint8_t *p) return p + sizeof(*mcfg); } +static uint8_t *acpi_build_tpm2(uint8_t *p) +{ + struct acpi_tpm2 *tpm2 = (struct acpi_tpm2 *)p; + + memset(tpm2, 0, sizeof(*tpm2)); + tpm2->start_method = ACPI_TPM2_START_TIS; + acpi_fill_header(&tpm2->header, "TPM2", sizeof(*tpm2), 4); + acpi_set_checksum(&tpm2->header); + return p + sizeof(*tpm2); +} + +static uint8_t *acpi_build_hpet(uint8_t *p) +{ + struct acpi_hpet *hpet = (struct acpi_hpet *)p; + + memset(hpet, 0, sizeof(*hpet)); + /* GCAP_ID low dword: vendor, timer count and revision */ + hpet->block_id = mmio_read32(ACPI_HPET_BASE); + hpet->base.space_id = ACPI_GAS_SYSTEM_MEMORY; + hpet->base.bit_width = 64; + hpet->base.address = ACPI_HPET_BASE; + hpet->min_tick = 0x80; + acpi_fill_header(&hpet->header, "HPET", sizeof(*hpet), 1); + acpi_set_checksum(&hpet->header); + return p + sizeof(*hpet); +} + +static uint8_t *dmar_add_scope(uint8_t *e, uint8_t type, uint8_t id, + uint8_t dev, uint8_t fn) +{ + struct dmar_scope *s = (struct dmar_scope *)e; + + memset(s, 0, sizeof(*s)); + s->type = type; + s->length = sizeof(*s); + s->enumeration_id = id; + s->dev = dev; + s->fn = fn; + return e + sizeof(*s); +} + +static uint8_t *acpi_build_dmar(uint8_t *p) +{ + struct acpi_dmar *dmar = (struct acpi_dmar *)p; + uint8_t *e = p + sizeof(*dmar); + struct dmar_drhd *drhd; + struct dmar_rmrr *rmrr; + uint32_t bdsm, tolud; + int igd; + + memset(dmar, 0, sizeof(*dmar)); + dmar->host_address_width = DMAR_HAW; + dmar->flags = DMAR_FLAGS; + + igd = (pci_config_read32(0, 2, 0, 0) & 0xffff) != 0xffff; + if (igd) { + drhd = (struct dmar_drhd *)e; + memset(drhd, 0, sizeof(*drhd)); + drhd->type = DMAR_DRHD; + drhd->base = DMAR_GFX_UNIT; + e = dmar_add_scope(e + sizeof(*drhd), DMAR_SCOPE_ENDPOINT, 0, 2, 0); + drhd->length = (uint16_t)(e - (uint8_t *)drhd); + } + + drhd = (struct dmar_drhd *)e; + memset(drhd, 0, sizeof(*drhd)); + drhd->type = DMAR_DRHD; + drhd->flags = DMAR_INCLUDE_PCI_ALL; + drhd->base = DMAR_DEFAULT_UNIT; + e = dmar_add_scope(e + sizeof(*drhd), DMAR_SCOPE_IOAPIC, ACPI_IOAPIC_ID, + 0x1e, 7); + e = dmar_add_scope(e, DMAR_SCOPE_HPET, 0, 0x1e, 6); + drhd->length = (uint16_t)(e - (uint8_t *)drhd); + + /* Graphics stolen memory (BDSM, 00:02.0) up to TOLUD (00:00.0), which the + * IGD keeps using through DMA remapping */ + bdsm = pci_config_read32(0, 2, 0, 0x5c) & 0xfff00000U; + tolud = pci_config_read32(0, 0, 0, 0xbc) & 0xfff00000U; + if (igd && bdsm != 0 && tolud > bdsm) { + rmrr = (struct dmar_rmrr *)e; + memset(rmrr, 0, sizeof(*rmrr)); + rmrr->type = DMAR_RMRR; + rmrr->base = bdsm; + rmrr->limit = (uint64_t)tolud - 1; + e = dmar_add_scope(e + sizeof(*rmrr), DMAR_SCOPE_ENDPOINT, 0, 2, 0); + rmrr->length = (uint16_t)(e - (uint8_t *)rmrr); + } + + acpi_fill_header(&dmar->header, "DMAR", (uint32_t)(e - p), 2); + acpi_set_checksum(&dmar->header); + return e; +} + static void acpi_gas_io(struct acpi_gas *g, uint8_t width, uint64_t addr) { g->space_id = ACPI_GAS_SYSTEM_IO; @@ -381,6 +544,9 @@ uint64_t acpi_setup(void) uint8_t *fadt; uint8_t *madt; uint8_t *mcfg; + uint8_t *tpm2; + uint8_t *hpet; + uint8_t *dmar; struct acpi_header *xsdt; struct acpi_rsdp *rsdp; uint64_t *xsdt_entries; @@ -402,14 +568,26 @@ uint64_t acpi_setup(void) mcfg = p; p = acpi_align(acpi_build_mcfg(p)); + tpm2 = p; + p = acpi_align(acpi_build_tpm2(p)); + + hpet = p; + p = acpi_align(acpi_build_hpet(p)); + + dmar = p; + p = acpi_align(acpi_build_dmar(p)); + /* XSDT lists the top-level tables; the DSDT is reached via the FADT. */ xsdt = (struct acpi_header *)p; xsdt_entries = (uint64_t *)((uint8_t *)xsdt + sizeof(*xsdt)); xsdt_entries[0] = (uint64_t)(uintptr_t)fadt; xsdt_entries[1] = (uint64_t)(uintptr_t)madt; xsdt_entries[2] = (uint64_t)(uintptr_t)mcfg; + xsdt_entries[3] = (uint64_t)(uintptr_t)tpm2; + xsdt_entries[4] = (uint64_t)(uintptr_t)hpet; + xsdt_entries[5] = (uint64_t)(uintptr_t)dmar; acpi_fill_header(xsdt, "XSDT", - (uint32_t)(sizeof(*xsdt) + 3 * sizeof(uint64_t)), 1); + (uint32_t)(sizeof(*xsdt) + 6 * sizeof(uint64_t)), 1); acpi_set_checksum(xsdt); p = acpi_align((uint8_t *)xsdt + xsdt->length); diff --git a/src/x86/acpi_dsdt.asl b/src/x86/acpi_dsdt.asl index 0a9b66f121..5d85c29b48 100644 --- a/src/x86/acpi_dsdt.asl +++ b/src/x86/acpi_dsdt.asl @@ -1,7 +1,8 @@ /* acpi_dsdt.asl * * Minimal DSDT for the NAI 68INT6: the PCI root bus with its _CRS and the - * _PRT INTx routing. Compiled offline with iasl and committed as a C array. + * _PRT INTx routing, plus the board devices on LPSS I2C0 and GSPI0. + * Compiled offline with iasl and committed as a C array. * * Regenerate after editing: * iasl src/x86/acpi_dsdt.asl @@ -29,6 +30,7 @@ DefinitionBlock ("", "DSDT", 2, "WOLFBT", "NAITGL", 0x00000001) { Package () { 0x0002FFFF, 0, Zero, 16 }, /* iGPU INTA */ Package () { 0x0004FFFF, 0, Zero, 16 }, /* DTT INTA */ + Package () { 0x0006FFFF, 0, Zero, 16 }, /* PEG60 INTA */ Package () { 0x000DFFFF, 0, Zero, 16 }, /* TBT INTA */ Package () { 0x0014FFFF, 0, Zero, 16 }, /* xHCI INTA */ Package () { 0x0014FFFF, 1, Zero, 17 }, /* xHCI INTB */ @@ -53,6 +55,27 @@ DefinitionBlock ("", "DSDT", 2, "WOLFBT", "NAITGL", 0x00000001) }) } + /* The OS gets every PCIe feature it asks for (native hot-plug, + * PME, AER, capability structure, LTR): no firmware handler owns + * them after handoff */ + Method (_OSC, 4, Serialized) + { + Local0 = Arg3 + CreateDWordField (Local0, Zero, CDW1) + If ((Arg0 == ToUUID ("33db4d5b-1ff7-401c-9657-7441c03dd766"))) + { + If ((Arg1 != One)) + { + CDW1 |= 0x08 + } + } + Else + { + CDW1 |= 0x04 + } + Return (Local0) + } + Name (_CRS, ResourceTemplate () { /* Bus range covered by the MCFG (bus 0..0x17). */ @@ -84,6 +107,170 @@ DefinitionBlock ("", "DSDT", 2, "WOLFBT", "NAITGL", 0x00000001) 0x0000000000000000, 0x0000004000000000, 0x0000007FFFFFFFFF, 0x0000000000000000, 0x0000004000000000) }) + + /* Root ports with the INTx routing of the devices behind them, as + * the stock BIOS declares it; RP10's differs from a plain swizzle. */ + Device (PEG0) /* 00:06.0 */ + { + Name (_ADR, 0x00060000) + Name (_PRT, Package () + { + Package () { 0xFFFF, 0, Zero, 16 }, + Package () { 0xFFFF, 1, Zero, 17 }, + Package () { 0xFFFF, 2, Zero, 18 }, + Package () { 0xFFFF, 3, Zero, 19 }, + }) + } + + Device (RP05) /* 00:1c.0, PCH port 5 */ + { + Name (_ADR, 0x001C0000) + Name (_PRT, Package () + { + Package () { 0xFFFF, 0, Zero, 16 }, + Package () { 0xFFFF, 1, Zero, 17 }, + Package () { 0xFFFF, 2, Zero, 18 }, + Package () { 0xFFFF, 3, Zero, 19 }, + }) + } + + Device (RP09) /* 00:1d.0, PCH port 9 */ + { + Name (_ADR, 0x001D0000) + Name (_PRT, Package () + { + Package () { 0xFFFF, 0, Zero, 16 }, + Package () { 0xFFFF, 1, Zero, 17 }, + Package () { 0xFFFF, 2, Zero, 18 }, + Package () { 0xFFFF, 3, Zero, 19 }, + }) + } + + Device (RP10) /* 00:1d.1, PCH port 10 */ + { + Name (_ADR, 0x001D0001) + Name (_PRT, Package () + { + Package () { 0xFFFF, 0, Zero, 17 }, + Package () { 0xFFFF, 1, Zero, 18 }, + Package () { 0xFFFF, 2, Zero, 19 }, + Package () { 0xFFFF, 3, Zero, 16 }, + }) + } + + /* LPSS I2C0 and GSPI0 with the board devices the stock BIOS + * declares, so NAI's Linux drivers (naigspi, at24) bind. */ + Device (I2C0) + { + Name (_ADR, 0x00150000) + + Device (EEP2) + { + Name (_HID, "15AC0002") + Name (_CID, "PRP0001") + Name (_DDN, "NAI Motherboard EEPROM") + Name (_CRS, ResourceTemplate () + { + I2cSerialBusV2 (0x0056, ControllerInitiated, 100000, + AddressingMode7Bit, "\\_SB.PCI0.I2C0") + }) + Name (_DSD, Package () + { + ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () + { + Package () { "compatible", "atmel,24c02" }, + Package () { "size", 256 }, + Package () { "pagesize", 8 } + } + }) + } + + Device (EEP3) + { + Name (_HID, "15AC0003") + Name (_CID, "PRP0001") + Name (_DDN, "NAI Motherboard EEPROM SN") + Name (_CRS, ResourceTemplate () + { + I2cSerialBusV2 (0x005E, ControllerInitiated, 100000, + AddressingMode7Bit, "\\_SB.PCI0.I2C0") + }) + Name (_DSD, Package () + { + ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () + { + Package () { "compatible", "atmel,24cs02" }, + Package () { "size", 16 }, + Package () { "pagesize", 8 }, + Package () { "read-only", One } + } + }) + } + + Device (RTC1) + { + Name (_HID, "15AC0004") + Name (_CID, "PRP0001") + Name (_DDN, "NAI Motherboard WDT RTC") + Name (_CRS, ResourceTemplate () + { + I2cSerialBusV2 (0x0068, ControllerInitiated, 100000, + AddressingMode7Bit, "\\_SB.PCI0.I2C0") + }) + Name (_DSD, Package () + { + ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () + { + Package () { "compatible", "dallas,ds1374" } + } + }) + } + } + + Device (SPI0) + { + Name (_ADR, 0x001E0002) + + Device (PRC0) + { + Name (_HID, "15AC0001") + Name (_STR, Unicode ("NAI GSPI Processor Register Controller")) + Name (_CRS, ResourceTemplate () + { + SpiSerialBusV2 (0x0000, PolarityLow, FourWireMode, 8, + ControllerInitiated, 500000, ClockPolarityLow, + ClockPhaseFirst, "\\_SB.PCI0.SPI0") + }) + } + } + } + + /* TPM 2.0 on its TIS (FIFO) interface; wolfBoot leaves its interrupt + * unconfigured, so the OS driver polls */ + Device (TPM) + { + Name (_HID, "MSFT0101") + Name (_STR, Unicode ("TPM 2.0 Device")) + OperationRegion (TPMR, SystemMemory, 0xFED40000, 0x5000) + Field (TPMR, AnyAcc, NoLock, Preserve) + { + ACC0, 8 + } + Name (_CRS, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0xFED40000, 0x00005000) + }) + Method (_STA, 0, NotSerialized) + { + If ((ACC0 == 0xFF)) + { + Return (Zero) + } + Return (0x0F) + } } } } diff --git a/src/x86/ata.c b/src/x86/ata.c index 535cca9254..c299d92a26 100644 --- a/src/x86/ata.c +++ b/src/x86/ata.c @@ -774,7 +774,8 @@ static int ata_drive_read_sector(int drv, uint64_t start, uint32_t count, cmdfis->lba5 = (uint8_t)((start >> 40) & 0xFF); cmdfis->device = (1 << 6); /* LBA mode */ cmdfis->count = (uint16_t)(count & 0xFFFF); - exec_cmd_slot(drv, slot); + if (exec_cmd_slot(drv, slot) != 0) + return -1; return count << ata->sector_size_shift; } @@ -794,7 +795,7 @@ static int ata_drive_write_sector(int drv, uint64_t start, uint32_t count, cmdfis = (struct fis_reg_h2d *)(&tbl->cfis); cmdfis->fis_type = FIS_TYPE_REG_H2D; cmdfis->c = 1; - cmdfis->command = ATA_CMD_WRITE_DMA; + cmdfis->command = ATA_CMD_WRITE_DMA_EX; cmdfis->lba0 = (uint8_t)(start & 0xFF); cmdfis->lba1 = (uint8_t)((start >> 8) & 0xFF); cmdfis->lba2 = (uint8_t)((start >> 16) & 0xFF); @@ -804,7 +805,8 @@ static int ata_drive_write_sector(int drv, uint64_t start, uint32_t count, cmdfis->device = (1 << 6); /* LBA mode */ cmdfis->count = (uint16_t)(count & 0xFFFF); - exec_cmd_slot(drv, slot); + if (exec_cmd_slot(drv, slot) != 0) + return -1; return count << ata->sector_size_shift; } @@ -826,12 +828,16 @@ static void ata_cache_pull(int drv, uint64_t sector) ata->cached = sector; } -static void ata_cache_commit(int drv) +static int ata_cache_commit(int drv) { struct ata_drive *ata = &ATA_Drv[drv]; if (ata->cached == CACHE_INVALID) - return; - ata_drive_write_sector(drv, ata->cached, 1, ata->sector_cache); + return 0; + if (ata_drive_write_sector(drv, ata->cached, 1, ata->sector_cache) < 0) { + ata->cached = CACHE_INVALID; + return -1; + } + return 0; } /** @@ -881,6 +887,8 @@ int ata_drive_read(int drv, uint64_t start, uint32_t size, uint8_t *buf) } if (size > 0) { ata_cache_pull(drv, sect_start); + if (ata->cached != sect_start) + return -1; memcpy(buf + buffer_off, ata->sector_cache, size); buffer_off += size; } @@ -920,7 +928,8 @@ int ata_drive_write(int drv, uint64_t start, uint32_t size, if (ata->cached != sect_start) return -1; memcpy(ata->sector_cache + sect_off, buf, len); - ata_cache_commit(drv); + if (ata_cache_commit(drv) < 0) + return -1; size -= len; buffer_off += len; sect_start++; @@ -937,8 +946,11 @@ int ata_drive_write(int drv, uint64_t start, uint32_t size, } if (size > 0) { ata_cache_pull(drv, sect_start); + if (ata->cached != sect_start) + return -1; memcpy(ata->sector_cache, buf + buffer_off, size); - ata_cache_commit(drv); + if (ata_cache_commit(drv) < 0) + return -1; buffer_off += size; } return buffer_off; diff --git a/src/x86/common.c b/src/x86/common.c index 1da8926c5f..f1014799c5 100644 --- a/src/x86/common.c +++ b/src/x86/common.c @@ -236,6 +236,30 @@ uint8_t io_read8(uint16_t port) return v; } +/* CMOS/RTC scratch RAM (0x70/0x71). Bytes 0x40-0x7F are outside the RTC + * checksum and survive a 0xCF9 reset but not loss of the RTC well. Bit 7 of + * the index port is NMI-disable and must be preserved. */ +#define CMOS_INDEX_PORT 0x70 +#define CMOS_DATA_PORT 0x71 + +uint8_t cmos_read(uint8_t index) +{ + uint8_t nmi; + + nmi = io_read8(CMOS_INDEX_PORT) & 0x80; + io_write8(CMOS_INDEX_PORT, nmi | (index & 0x7F)); + return io_read8(CMOS_DATA_PORT); +} + +void cmos_write(uint8_t index, uint8_t value) +{ + uint8_t nmi; + + nmi = io_read8(CMOS_INDEX_PORT) & 0x80; + io_write8(CMOS_INDEX_PORT, nmi | (index & 0x7F)); + io_write8(CMOS_DATA_PORT, value); +} + /** * @brief Reset the system. * @@ -368,6 +392,75 @@ void cpuid(uint32_t eax_param, *edx = _edx; } +static void cpuid_subleaf(uint32_t leaf, uint32_t subleaf, uint32_t *eax, + uint32_t *ebx) +{ + uint32_t a, b, c, d; + + __asm__ __volatile__ ("cpuid" + : "=a"(a), "=b"(b), "=c"(c), "=d"(d) + : "a"(leaf), "c"(subleaf)); + (void)c; + (void)d; + *eax = a; + *ebx = b; +} + +#define MSR_CORE_THREAD_COUNT 0x35 + +/** + * @brief List the APIC IDs of the enabled logical processors. + * + * MSR_CORE_THREAD_COUNT gives the enabled cores and threads (CPUID leaf 0xB + * still counts the second thread when Hyper-Threading is off), and leaf 0xB + * the APIC ID layout: thread t of core c has APIC ID (c << SMT shift) | t, + * with enabled cores taken to be numbered from 0. The primary threads come + * first, then their siblings, as a BIOS orders the MADT. + * + * @param ids Output array of APIC IDs. + * @param max Capacity of ids. + * @return The number of IDs written, or 0 if the counts are unusable. + */ +int x86_cpu_apic_ids(uint8_t *ids, int max) +{ + uint32_t eax, ebx, lo, hi; + + __asm__ __volatile__ ("rdmsr" : "=a"(lo), "=d"(hi) + : "c"(MSR_CORE_THREAD_COUNT)); + (void)hi; + cpuid_subleaf(0xb, 0, &eax, &ebx); + (void)ebx; + return x86_apic_ids_from_topology(lo, eax & 0x1f, ids, max); +} + +/** + * @brief APIC IDs from MSR_CORE_THREAD_COUNT and the SMT shift. + * + * @param core_thread_count MSR_CORE_THREAD_COUNT: threads in bits 15:0, + * cores in bits 31:16. + * @param shift APIC ID bits taken by the thread number (CPUID 0xB level 0). + * @param ids Output array of APIC IDs. + * @param max Capacity of ids. + * @return The number of IDs written, or 0 if the counts are unusable. + */ +int x86_apic_ids_from_topology(uint32_t core_thread_count, uint32_t shift, + uint8_t *ids, int max) +{ + uint32_t threads, logical, cores, t, c; + int n = 0; + + logical = core_thread_count & 0xffff; + cores = (core_thread_count >> 16) & 0xffff; + if (cores == 0 || logical < cores) + return 0; + threads = logical / cores; + for (t = 0; t < threads; t++) { + for (c = 0; c < cores && n < max; c++) + ids[n++] = (uint8_t)((c << shift) | t); + } + return n; +} + /** * @brief Check if 1GB page is supported by the CPU. * diff --git a/src/x86/fsp.c b/src/x86/fsp.c index 0ed59ec439..35540b8ca7 100644 --- a/src/x86/fsp.c +++ b/src/x86/fsp.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef WOLFBOOT_FSP_NVS_CACHE +#include +#endif #define FSP_INFO_HEADER_OFFSET 0x94 #define EFI_SUCCESS 0x0 @@ -41,6 +44,7 @@ extern uint8_t _fsp_s_base_start[]; int fsp_machine_update_s_parameters(uint8_t *default_s_params); int fsp_pre_silicon_init_cb(void); +int fsp_pre_pci_enum_cb(void); /*! * \brief Check if the FSP info header is valid. * @@ -162,6 +166,9 @@ void fsp_init_silicon(void) panic(); } wolfBoot_printf("success"); +#if defined(WOLFBOOT_FSP_NVS_CACHE) && !defined(BUILD_LOADER_STAGE1) + fsp_nvs_save(); +#endif memcpy(_fsp_s_base_start, _start_fsp_s, _end_fsp_s - _start_fsp_s); fsp_s_hdr = (struct fsp_info_header*)(_fsp_s_base_start + FSP_INFO_HEADER_OFFSET); silicon_init_off = fsp_s_hdr->FspSiliconInitEntryOffset; @@ -189,6 +196,9 @@ void fsp_init_silicon(void) panic(); } wolfBoot_printf("success"); + status = fsp_pre_pci_enum_cb(); + if (status != 0) + panic(); pci_enum_do(); pci_dump_config_space(); notify_phase_off = fsp_s_hdr->NotifyPhaseEntryOffset; diff --git a/src/x86/fsp_nvs.c b/src/x86/fsp_nvs.c new file mode 100644 index 0000000000..3585904a4d --- /dev/null +++ b/src/x86/fsp_nvs.c @@ -0,0 +1,202 @@ +/* fsp_nvs.c + * + * Cache of the FSP-M memory training data in SPI flash. Stage 1 hands a + * valid saved copy back to FspMemInit through NvsBufferPtr so the memory + * reference code takes its fast path; stage 2 saves the data FSP-M returned + * in its non-volatile storage HOB whenever it differs from the saved copy. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(FSP_NVS_BASE) || !defined(FSP_NVS_SIZE) +#error "FSP_NVS_CACHE needs FSP_NVS_BASE and FSP_NVS_SIZE" +#endif + +#define FSP_NVS_SECTOR_SIZE 0x1000U +#if (FSP_NVS_SIZE % FSP_NVS_SECTOR_SIZE) != 0 +#error "FSP_NVS_SIZE must be a multiple of the 4 KB erase size" +#endif +#ifdef BOOTLOADER_PARTITION_SIZE +#if (FSP_NVS_BASE) + (FSP_NVS_SIZE) > 0x100000000ULL - (BOOTLOADER_PARTITION_SIZE) +#error "the FSP_NVS area must lie below the wolfBoot flash window" +#endif +#endif + +#ifndef FSP_NVS_CACHE_FLUSH +/* The flash window is cached; drop stale lines before the read-back */ +#define FSP_NVS_CACHE_FLUSH() __asm__ volatile ("wbinvd" ::: "memory") +#endif + +uint32_t fsp_nvs_crc32(uint32_t crc, const uint8_t *buf, uint32_t len) +{ + int i; + + crc = ~crc; + while (len-- > 0) { + crc ^= *buf++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ (0xEDB88320U & (0U - (crc & 1U))); + } + return ~crc; +} + +int fsp_nvs_record_valid(const struct fsp_nvs_record *rec, uint32_t key, + uint32_t area_size) +{ + if (area_size <= sizeof(*rec)) + return 0; + if (rec->magic != FSP_NVS_MAGIC || rec->key != key) + return 0; + if (rec->len == 0 || rec->len > area_size - sizeof(*rec)) + return 0; + return fsp_nvs_crc32(0, rec->data, rec->len) == rec->crc; +} + +#ifdef BUILD_LOADER_STAGE1 +/* CMOS byte set when FspMemInit failed with the saved record. Boots train + * from scratch until FspMemInit succeeds without it, across the resets FSP-M + * requests, and stage 2 then replaces the record. Stage 1 runs from SPI + * flash, so it cannot erase the record itself. */ +#define FSP_NVS_SKIP_CMOS_IDX 0x52 +#define FSP_NVS_SKIP_CMOS_MAGIC 0xA5 + +void fsp_nvs_skip(int skip) +{ + cmos_write(FSP_NVS_SKIP_CMOS_IDX, skip ? FSP_NVS_SKIP_CMOS_MAGIC : 0); +} + +/* Returns 1 when FspMemInit is given the saved record. */ +int fsp_nvs_apply(uint8_t *udp_m, uint32_t udp_size, const uint8_t *fsp_m, + uint32_t fsp_m_size) +{ + const struct fsp_nvs_record *rec = + (const struct fsp_nvs_record *)(uintptr_t)FSP_NVS_BASE; + FSPM_ARCH_UPD *arch = (FSPM_ARCH_UPD *)(udp_m + sizeof(FSP_UPD_HEADER)); + uint32_t key; + + /* The key is taken before NvsBufferPtr and BootMode are changed, so + * stage 2 can match it against the UPDs this boot trained with. It + * covers the whole FSP-M image: different FSP-M builds can report the + * same revision. */ + key = fsp_nvs_crc32(0, fsp_m, fsp_m_size); + key = fsp_nvs_crc32(key, udp_m, udp_size); + stage2_get_parameters()->fsp_nvs_key = key; + + if (cmos_read(FSP_NVS_SKIP_CMOS_IDX) == FSP_NVS_SKIP_CMOS_MAGIC) { + wolfBoot_printf("FSP NVS: saved training data failed, retraining\r\n"); + return 0; + } + if (!fsp_nvs_record_valid(rec, key, FSP_NVS_SIZE)) { + wolfBoot_printf("FSP NVS: no saved training data\r\n"); + return 0; + } + arch->NvsBufferPtr = (uint32_t)(uintptr_t)rec->data; + arch->BootMode = BOOT_ASSUMING_NO_CONFIGURATION_CHANGES; + wolfBoot_printf("FSP NVS: using saved training data (%d bytes)\r\n", + (int)rec->len); + return 1; +} +#else +/* FSP_NON_VOLATILE_STORAGE_HOB_GUID */ +static const struct efi_guid fsp_nvs_hob_guid = { + 0x721acf02, 0x4d77, 0x4c2a, + { 0xb3, 0xdc, 0x27, 0x0b, 0x7b, 0xa9, 0xe4, 0xb0 } +}; + +static struct efi_hob_guid_type *fsp_nvs_find_hob(struct efi_hob *hob) +{ + while (hob != NULL && + hob_get_type(hob) != EFI_HOB_TYPE_END_OF_HOB_LIST) { + if (hob_get_length(hob) < sizeof(struct efi_hob_generic_header)) + return NULL; + if (hob_get_type(hob) == EFI_HOB_TYPE_GUID_EXTENSION && + hob_get_length(hob) > sizeof(struct efi_hob_guid_type) && + hob_guid_equals(&hob->u.guid.name, + (struct efi_guid *)&fsp_nvs_hob_guid)) + return &hob->u.guid; + hob = hob_get_next(hob); + } + return NULL; +} + +static int fsp_nvs_is_current(const struct fsp_nvs_record *rec, uint32_t key, + const uint8_t *data, uint32_t len) +{ + return fsp_nvs_record_valid(rec, key, FSP_NVS_SIZE) && rec->len == len && + memcmp(rec->data, data, len) == 0; +} + +void fsp_nvs_save(void) +{ + const struct fsp_nvs_record *rec = + (const struct fsp_nvs_record *)(uintptr_t)FSP_NVS_BASE; + struct stage2_parameter *params = stage2_get_parameters(); + struct efi_hob_guid_type *hob; + struct fsp_nvs_record hdr; + const uint8_t *data; + uint32_t len, erase_len; + + hob = fsp_nvs_find_hob((struct efi_hob *)(uintptr_t)params->hobList); + if (hob == NULL) { + wolfBoot_printf("FSP NVS: no training data HOB\r\n"); + return; + } + data = (const uint8_t *)(hob + 1); + len = hob->header.hob_length - sizeof(*hob); + if (len > FSP_NVS_SIZE - sizeof(hdr)) { + wolfBoot_printf("FSP NVS: training data (%d bytes) exceeds the cache\r\n", + (int)len); + return; + } + if (fsp_nvs_is_current(rec, params->fsp_nvs_key, data, len)) + return; + + memset(&hdr, 0, sizeof(hdr)); + hdr.magic = FSP_NVS_MAGIC; + hdr.key = params->fsp_nvs_key; + hdr.len = len; + hdr.crc = fsp_nvs_crc32(0, data, len); + erase_len = (sizeof(hdr) + len + FSP_NVS_SECTOR_SIZE - 1) & + ~(FSP_NVS_SECTOR_SIZE - 1); + /* The header goes last, so an interrupted save leaves no valid record */ + if (tgl_spi_flash_erase(FSP_NVS_BASE, erase_len) != 0 || + tgl_spi_flash_write(FSP_NVS_BASE + sizeof(hdr), data, len) != 0 || + tgl_spi_flash_write(FSP_NVS_BASE, (const uint8_t *)&hdr, + sizeof(hdr)) != 0) { + wolfBoot_printf("FSP NVS: saving training data failed\r\n"); + return; + } + FSP_NVS_CACHE_FLUSH(); + if (!fsp_nvs_is_current(rec, hdr.key, data, len)) { + wolfBoot_printf("FSP NVS: training data read-back mismatch\r\n"); + return; + } + wolfBoot_printf("FSP NVS: saved %d bytes of training data\r\n", (int)len); +} +#endif /* BUILD_LOADER_STAGE1 */ diff --git a/src/x86/mptable.c b/src/x86/mptable.c index 2d1f294724..ef1912082e 100644 --- a/src/x86/mptable.c +++ b/src/x86/mptable.c @@ -518,7 +518,25 @@ void mptable_setup(void) _mp->mpce_processor[0]._res[0] = _mp->mpce_processor[0]._res[1] = 0; -#if defined(TARGET_kontron_vx3060_s2) || defined(TARGET_nai_68int6) +#if defined(TARGET_nai_68int6) + { + uint8_t apic_ids[MP_CPU_NUM_ENTRY]; + int i, j = 1, cpus; + + cpus = x86_cpu_apic_ids(apic_ids, MP_CPU_NUM_ENTRY); + for (i = 0; i < cpus && j < MP_CPU_NUM_ENTRY; i++) { + if (apic_ids[i] == apic_id) + continue; + _mp->mpce_processor[j].apic_id = apic_ids[i]; + _mp->mpce_processor[j].apic_ver = (uint8_t)(apic_ver & 0xff); + _mp->mpce_processor[j].cpu_flags = 0x1; /* enabled */ + _mp->mpce_processor[j].cpu_signature = cpupid_sign; + _mp->mpce_processor[j].feature_flags = 0; + _mp->mpce_processor[j]._res[0] = _mp->mpce_processor[j]._res[1] = 0; + j++; + } + } +#elif defined(TARGET_kontron_vx3060_s2) { int i; for (i = 1; i < 4; ++i) { diff --git a/src/x86/qemu_fsp.c b/src/x86/qemu_fsp.c index 37fc5650f7..ece0977707 100644 --- a/src/x86/qemu_fsp.c +++ b/src/x86/qemu_fsp.c @@ -140,3 +140,8 @@ int fsp_pre_silicon_init_cb(void) { return 0; } + +int fsp_pre_pci_enum_cb(void) +{ + return 0; +} diff --git a/src/x86/tgl_fsp.c b/src/x86/tgl_fsp.c index 03066e6f82..397a7ec4e9 100644 --- a/src/x86/tgl_fsp.c +++ b/src/x86/tgl_fsp.c @@ -110,6 +110,8 @@ #define GPIO_TERM_MASK (0xf) << GPIO_TERM_SHIFT #define GPIO_RXEVCONF_SHIFT (25) #define GPIO_RXEVCONF_MASK (0x3) << GPIO_RXEVCONF_SHIFT +#define GPIO_TX_STATE (1U << 0) +#define GPIO_RX_STATE (1U << 1) #define PCR_INTERRUPT_PORT_ID (0xc4) #define GIC_OFFSET (0x31FC) #define GIC_SHUTDOWN_STATUS_BIT (1 << 0) @@ -143,9 +145,9 @@ #define GPIO_GPPC_E12_CFG_OFF (0xb30) #define GPIO_GPPC_E15_CFG_OFF (0xb60) #define GPIO_GPPC_E16_CFG_OFF (0xb70) -#define GPIO_GPPC_F2_CFG_OFF (0xba0) -#define GPIO_GPPC_F4_CFG_OFF (0xbc0) -#define GPIO_GPPC_F5_CFG_OFF (0xbd0) +#define GPIO_GPPC_E19_CFG_OFF (0xba0) +#define GPIO_GPPC_E21_CFG_OFF (0xbc0) +#define GPIO_GPPC_E22_CFG_OFF (0xbd0) #define GPIO_GPPC_F9_CFG_OFF (0x910) #define GPIO_GPPC_A9_CFG_OFF (0xa30) #define GPIO_GPPC_T2_CFG_OFF (0x8c0) @@ -677,7 +679,13 @@ static void fsp_set_silicon_cfg(FSPS_UPD *fsps) upd->Usb2PhyPehalfbit[15] = 0; upd->PchTsnEnable[0] = 0; upd->PchTsnEnable[1] = 0; +#ifdef TARGET_nai_68int6 + /* No CNVi module is fitted; forcing Disable makes FSP-S request a global + * reset on every cold boot, while Auto finds nothing and does not */ + upd->CnviMode = 1; +#else upd->CnviMode = 0; +#endif upd->CnviBtCore = 0; upd->SataLedEnable = 1; upd->IomTypeCPortPadCfg[0] = 0; @@ -1030,11 +1038,18 @@ int fsp_machine_update_s_parameters(uint8_t *default_s_params) memset(upd->PcieRpHotPlug, 0, sizeof(upd->PcieRpHotPlug)); memset(upd->CpuPcieRpHotPlug, 0, sizeof(upd->CpuPcieRpHotPlug)); #ifdef TARGET_nai_68int6 + /* As the stock BIOS: hot-plug on PCH root ports 5, 9 and 10, so FSP-S + * keeps a port without a link (10 on a bare board) enabled */ + upd->PcieRpHotPlug[4] = 1; + upd->PcieRpHotPlug[8] = 1; + upd->PcieRpHotPlug[9] = 1; upd->Eist = FSP_INTEL_SPEED_STEP; upd->Hwp = FSP_INTEL_SPEED_SHIFT; upd->Cx = FSP_C_STATES; upd->TxtEnable = FSP_TXT; upd->ThermalMonitor = FSP_THERMAL_THROTTLING; + /* I2C0 (PCI mode) carries the board EEPROMs and the watchdog RTC */ + upd->SerialIoI2cMode[0] = 1; #endif return 0; } @@ -1089,7 +1104,8 @@ enum gpio_config_flags { GPIO_SET_RXINV = (1 << 4), GPIO_SET_RESET = (1 << 5), GPIO_SET_TERM = (1 << 6), - GPIO_SET_RXEVCONF = (1 << 7) + GPIO_SET_RXEVCONF = (1 << 7), + GPIO_SET_TX = (1 << 8) }; struct tgl_gpio_conf { @@ -1103,6 +1119,7 @@ struct tgl_gpio_conf { uint8_t gpio_term; uint8_t gpio_rxevconf; uint8_t gpio_rxinv; + uint8_t gpio_tx; }; #if defined (TARGET_kontron_vx3060_s2) @@ -1229,7 +1246,7 @@ static const struct tgl_gpio_conf gpio_table_premem[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F2_CFG_OFF, + .cfg_offset = GPIO_GPPC_E19_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), @@ -1313,7 +1330,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F4_CFG_OFF, + .cfg_offset = GPIO_GPPC_E21_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), @@ -1324,7 +1341,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { {.gpio = { .comm_port_id = GPIO_COMM_4_PORT_ID, - .cfg_offset = GPIO_GPPC_F5_CFG_OFF, + .cfg_offset = GPIO_GPPC_E22_CFG_OFF, }, .flags = (GPIO_SET_MODE | GPIO_SET_DIRECTION | GPIO_SET_INTERRUPT | GPIO_SET_RESET), @@ -1547,7 +1564,7 @@ static void tgl_gpio_configure(const struct tgl_gpio_conf *gpio) g = gpio->gpio; - GPIO_DEBUG_PRINTF("Gpio port: %d off: 0x%x :\r\n", i, g.comm_port_id, + GPIO_DEBUG_PRINTF("Gpio port: 0x%x off: 0x%x :\r\n", g.comm_port_id, g.cfg_offset); dw0 = _dw0 = pch_read32(g.comm_port_id, g.cfg_offset); @@ -1577,6 +1594,11 @@ static void tgl_gpio_configure(const struct tgl_gpio_conf *gpio) dw0 &= ~(GPIO_RXEVCONF_MASK); dw0 |= gpio->gpio_rxevconf << GPIO_RXEVCONF_SHIFT; } + if (gpio->flags & GPIO_SET_TX) { + dw0 &= ~GPIO_TX_STATE; + if (gpio->gpio_tx) + dw0 |= GPIO_TX_STATE; + } GPIO_DEBUG_PRINTF("dw0: 0x%x -> 0x%x, dw1: 0x%x->0x%x\r\n", _dw0, dw0, _dw1, dw1); @@ -1588,6 +1610,39 @@ static void tgl_gpio_configure(const struct tgl_gpio_conf *gpio) } } +#ifdef TARGET_nai_68int6 +static uint64_t tgl_rdtsc(void) +{ + uint32_t lo, hi; + + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +} + +/* TSC ticks per millisecond: CPUID 0x15 gives the crystal clock and the + * TSC/crystal ratio, CPUID 0x16 the base frequency in MHz. */ +static uint32_t tgl_tsc_per_ms(void) +{ + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + + cpuid(0x15, &eax, &ebx, &ecx, &edx); + if (eax != 0 && ebx != 0 && ecx != 0) + return (ecx / 1000) * ebx / eax; + cpuid(0x16, &eax, &ebx, &ecx, &edx); + if ((eax & 0xffff) != 0) + return (eax & 0xffff) * 1000; + return 2000000; +} + +static void tgl_delay_ms(uint32_t ms) +{ + uint64_t end = tgl_rdtsc() + (uint64_t)tgl_tsc_per_ms() * ms; + + while (tgl_rdtsc() < end) + ; +} +#endif /* TARGET_nai_68int6 */ + #ifdef TARGET_kontron_vx3060_s2 /** * @brief Set up ECE1200 device on Kontron VX3060-S2 board. @@ -1781,6 +1836,13 @@ int fsp_pre_mem_init_cb(void) for (i = 0; i < n; i++) { tgl_gpio_configure(&gpio_table_premem[i]); } +#ifdef TARGET_nai_68int6 + if (NAI_68INT6_PCIE_LINK_DELAY_S > 0) { + wolfBoot_printf("PCIe link delay %d s\r\n", + NAI_68INT6_PCIE_LINK_DELAY_S); + tgl_delay_ms(NAI_68INT6_PCIE_LINK_DELAY_S * 1000); + } +#endif return 0; } @@ -1796,4 +1858,55 @@ int fsp_pre_silicon_init_cb(void) return 0; } + +#ifdef TARGET_nai_68int6 +#define NAI_PCIE_RDY_POLL_MS 50 +#define NAI_PCIE_RDY_TIMEOUT_MS (5000 + NAI_68INT6_FPGA_READY_DELAY_S * 1000) + +/* As the stock BIOS does before PCI enumeration: wait for the FPGA to raise + * PCIE_RDY (GPP_C23), then release GPP_B12. */ +static void nai_pcie_ready(void) +{ + uint32_t polls = 0, waited_ms = 0, dw0, id; + int ready; + + if (NAI_68INT6_BOOT_DELAY_S > 0) { + wolfBoot_printf("Boot delay %d s\r\n", NAI_68INT6_BOOT_DELAY_S); + tgl_delay_ms(NAI_68INT6_BOOT_DELAY_S * 1000); + } + for (;;) { + polls++; + ready = (pch_read32(GPIO_COMM_4_PORT_ID, GPIO_GPPC_C23_CFG_OFF) & + GPIO_RX_STATE) != 0; + if (ready || waited_ms >= NAI_PCIE_RDY_TIMEOUT_MS) + break; + tgl_delay_ms(NAI_PCIE_RDY_POLL_MS); + waited_ms += NAI_PCIE_RDY_POLL_MS; + } + if (ready) + wolfBoot_printf("PCIE_RDY high after %d polls\r\n", (int)polls); + else + wolfBoot_printf("Warning: PCIE_RDY low after %d ms, continuing\r\n", + (int)waited_ms); + + dw0 = pch_read32(GPIO_COMM_0_PORT_ID, GPIO_GPPC_B12_CFG_OFF); + pch_write32(GPIO_COMM_0_PORT_ID, GPIO_GPPC_B12_CFG_OFF, + dw0 | GPIO_TX_STATE); + if ((pch_read32(GPIO_COMM_0_PORT_ID, GPIO_GPPC_B12_CFG_OFF) & + GPIO_TX_STATE) == 0) + wolfBoot_printf("Warning: GPP_B12 did not go high\r\n"); + + id = pci_config_read32(0, 6, 0, 0); + wolfBoot_printf("PCIe 00:06.0: %s (0x%x)\r\n", + (id & 0xffff) == 0xffff ? "absent" : "present", id); +} +#endif /* TARGET_nai_68int6 */ + +int fsp_pre_pci_enum_cb(void) +{ +#ifdef TARGET_nai_68int6 + nai_pcie_ready(); +#endif + return 0; +} #endif /* TGL_FSP_H */ diff --git a/tools/scripts/x86_fsp/compute_pcr.py b/tools/scripts/x86_fsp/compute_pcr.py index 7e3c1048e9..9f19458de3 100644 --- a/tools/scripts/x86_fsp/compute_pcr.py +++ b/tools/scripts/x86_fsp/compute_pcr.py @@ -43,10 +43,11 @@ def get_config_value(config: str, name): def get_sha256_hash_of_wolfboot_image(file_path: str): """ - Get sha256 hash of wolfboot image at file_path + Get the image digest (SHA-256 or SHA-384 TLV) of wolfboot image at file_path """ HDR_OFF = 8 WOLFBOOT_SHA_HDR = 0x03 + WOLFBOOT_SHA384_HDR = 0x14 with open(file_path, 'rb') as f: data = f.read() data = data[HDR_OFF:] @@ -57,7 +58,7 @@ def get_sha256_hash_of_wolfboot_image(file_path: str): t, l = struct.unpack(' bytearray: is_measured_boot = get_config_value(config, 'MEASURED_BOOT') == '1' - # wolfBoot_image_measure() extends PCR with wolfboot image hash + # wolfBoot_image_measure() re-hashes the wolfboot image digest with + # SHA-256 and extends PCR0 with the result if is_measured_boot: wb_hash = get_sha256_hash_of_wolfboot_image('stage1/wolfboot_raw_v1_signed.bin') - pcr0 = pcr_extend(pcr0, wb_hash) + pcr0 = pcr_extend(pcr0, get_sha256_hash(wb_hash)) print(f"PCR0 after wolfboot image measure: {pcr0.hex()}") # the pcrdigest needed by policy_sign tool is the hash of the concatenation of all PCRs involved in the policy. diff --git a/tools/scripts/x86_fsp/tgl/assemble_image.sh b/tools/scripts/x86_fsp/tgl/assemble_image.sh index 50cad1ec51..d1abde1da0 100755 --- a/tools/scripts/x86_fsp/tgl/assemble_image.sh +++ b/tools/scripts/x86_fsp/tgl/assemble_image.sh @@ -7,6 +7,24 @@ BIOS_REGION_PATH=/tmp/bios.bin set -e +# The FSP-M training data cache holds one board's memory training. A flash +# dump of a board that has run wolfBoot carries it, so blank it rather than +# copy it to every board flashed with this image. +erase_fsp_nvs_cache() +{ + local base size image_size offset + + grep -q '^FSP_NVS_CACHE=1$' .config || return 0 + base=$(sed -n 's/^FSP_NVS_BASE=//p' .config) + size=$(sed -n 's/^FSP_NVS_SIZE=//p' .config) + image_size=$(stat -c%s "$WOLFBOOT_DIR/final_image.bin") + # the image ends at 4 GB + offset=$((image_size - (0x100000000 - base))) + head -c $((size)) /dev/zero | tr '\0' '\377' | \ + dd of="$WOLFBOOT_DIR/final_image.bin" bs=64K oflag=seek_bytes \ + seek=$offset conv=notrunc status=none +} + build_and_sign_image() { # compute the size differences between $FLASH_DUMP and "$WOLFBOOT_DIR"/wolfboot_stage1.bin and store it in SIZE @@ -16,6 +34,7 @@ build_and_sign_image() cp "$FLASH_DUMP" "$WOLFBOOT_DIR/temp_image.bin" truncate -s $SIZE "$WOLFBOOT_DIR/temp_image.bin" cat "$WOLFBOOT_DIR/temp_image.bin" "$BIOS_REGION_PATH" > "$WOLFBOOT_DIR/final_image.bin" + erase_fsp_nvs_cache if grep -q '^WOLFBOOT_TPM_SEAL=1$' .config; then PCR0=$(python ./tools/scripts/x86_fsp/compute_pcr.py "$WOLFBOOT_DIR"/final_image.bin | tail -n 1) "$WOLFBOOT_DIR"/tools/tpm/policy_sign -ecc256 -key=tpm_seal_key.key -pcr=0 -pcrdigest="$PCR0" @@ -30,14 +49,13 @@ assemble() } # Parse command line options -while getopts "s:n:m:" opt; do +while getopts "n:" opt; do case "$opt" in n) FLASH_DUMP="$OPTARG" ;; *) - echo "Usage: $0 [-k] [-s FLASH_DUMP]" - echo "-k: make keys" + echo "Usage: $0 -n FLASH_DUMP" echo "-n FLASH_DUMP: assemble an image for being used without IBG" exit 1 ;; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 12a69a11e7..8782551c10 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -114,6 +114,7 @@ TESTS+=unit-otp-keystore-gen-zeroize TESTS+=unit-x86-paging-oob TESTS+=unit-ahci-unlock-panic TESTS+=unit-ata-security-passphrase-zeroize +TESTS+=unit-ata-sector-io TESTS+=unit-fwtpm-nv-oob TESTS+=unit-elf-bss-guard TESTS+=unit-elf-entry-inplace @@ -156,6 +157,13 @@ TESTS+=unit-t10xx-flash-status TESTS+=unit-p1021-erase-advance TESTS+=unit-p1021-read-badblock TESTS+=unit-kontron-tgl-spi +TESTS+=unit-fsp-nvs +TESTS+=unit-fsp-nvs-save +TESTS+=unit-tgl-spi-hwseq +TESTS+=unit-x86-apic-ids +TESTS+=unit-x86-acpi +TESTS+=unit-boot-x86-fsp-measure +TESTS+=unit-boot-x86-fsp-payload TESTS+=unit-nrf5340-flash-protect TESTS+=unit-keygen-keystore TESTS+=unit-samr21-erase-advance @@ -1475,6 +1483,10 @@ unit-ata-security-passphrase-zeroize: ../../include/target.h unit-ata-security-p gcc -o $@ unit-ata-security-passphrase-zeroize.c $(CFLAGS) \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections +unit-ata-sector-io: ../../include/target.h unit-ata-sector-io.c ../../src/x86/ata.c + gcc -o $@ unit-ata-sector-io.c $(CFLAGS) \ + -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections + # unit-flash-write-cc26x2 includes hal/cc26x2.c directly, with cc26x2_ti_stub/ # standing in for the (not vendored) TI CC26x2 SDK headers it includes. This # is also the only build coverage hal/cc26x2.c has. @@ -2125,6 +2137,68 @@ unit-kontron-tgl-spi: unit-kontron-tgl-spi.c kontron_spi_extract.h \ kontron_spi_fn_extract.h gcc -o $@ unit-kontron-tgl-spi.c $(CFLAGS) $(LDFLAGS) +# unit-tgl-spi-hwseq runs the real SPI hardware-sequencing erase/write from +# hal/x86_fsp_tgl.c (FSP memory training data cache) against a model of the +# SPI controller registers and a RAM-backed flash window. +tgl_spi_hwseq_extract.h: ../../hal/x86_fsp_tgl.c + sed -n '/^#ifdef WOLFBOOT_FSP_NVS_CACHE/,/^#endif \/\* WOLFBOOT_FSP_NVS_CACHE \*\//p' $< > $@ + +unit-tgl-spi-hwseq:CFLAGS+=-DWOLFBOOT_FSP_NVS_CACHE +unit-tgl-spi-hwseq: unit-tgl-spi-hwseq.c kontron_spi_extract.h \ + tgl_spi_hwseq_extract.h + gcc -o $@ unit-tgl-spi-hwseq.c $(CFLAGS) $(LDFLAGS) + +# unit-x86-apic-ids runs the real x86_apic_ids_from_topology() from +# src/x86/common.c (the CPU list behind the MADT and the MP table). +x86_apic_ids_extract.h: ../../src/x86/common.c + sed -n '/^int x86_apic_ids_from_topology/,/^}/p' $< > $@ + +unit-x86-apic-ids: unit-x86-apic-ids.c x86_apic_ids_extract.h + gcc -o $@ unit-x86-apic-ids.c $(CFLAGS) $(LDFLAGS) + +# unit-x86-acpi runs the x86 FSP ACPI table builders (src/x86/acpi.c) +# against mocked PCI, MMIO and CPU topology reads. +unit-x86-acpi: unit-x86-acpi.c ../../src/x86/acpi.c ../../include/x86/acpi_dsdt.h + gcc -o $@ unit-x86-acpi.c $(CFLAGS) $(LDFLAGS) + +# unit-boot-x86-fsp-payload runs the ELF segment check and the per-image +# Multiboot2 ELF or Linux choice (src/boot_x86_fsp_payload.c). +unit-boot-x86-fsp-payload: ../../include/target.h unit-boot-x86-fsp-payload.c \ + ../../src/boot_x86_fsp_payload.c + gcc -o $@ unit-boot-x86-fsp-payload.c $(CFLAGS) \ + -DWOLFBOOT_LOAD_BASE=0x58000200 -DWOLFBOOT_FSP -DTARGET_nai_68int6 \ + -DWOLFBOOT_64BIT \ + -DX86_UART_BASE=0xFE036000 -DWOLFBOOT_LINUX_PAYLOAD -DWOLFBOOT_ELF \ + -DWOLFBOOT_MULTIBOOT2 $(LDFLAGS) + +# unit-boot-x86-fsp-measure runs the stage 1 PCR 0 measurement of stage 2 +# (wolfBoot_image_measure() in src/boot_x86_fsp.c) with real SHA-256. +unit-boot-x86-fsp-measure: ../../include/target.h unit-boot-x86-fsp-measure.c \ + ../../src/boot_x86_fsp.c + gcc -o $@ unit-boot-x86-fsp-measure.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) \ + -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFTPM_USER_SETTINGS \ + -DWOLFBOOT_LOAD_BASE=0x100000 -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \ + -D__WOLFBOOT -DWOLFBOOT_TPM -DWOLFBOOT_MEASURED_BOOT \ + -DWOLFBOOT_MEASURED_PCR_A=0 \ + -DWOLFBOOT_HASH_SHA384 -DWOLFBOOT_SIGN_ECC384 \ + -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections + +# unit-fsp-nvs runs the FSP-M memory training data cache record check and +# the stage 1 hand-off (src/x86/fsp_nvs.c) against a RAM-backed area. +unit-fsp-nvs:CFLAGS+=-DWOLFBOOT_FSP -DWOLFBOOT_FSP_NVS_CACHE \ + -DBUILD_LOADER_STAGE1 -DPRINTF_ENABLED +unit-fsp-nvs: unit-fsp-nvs.c ../../src/x86/fsp_nvs.c + gcc -o $@ unit-fsp-nvs.c $(CFLAGS) $(LDFLAGS) + +# unit-fsp-nvs-save runs the stage 2 save path against a synthetic HOB list +# and a RAM-backed flash area; built non-PIE so the HOB list sits below 4 GB +# (stage2_parameter.hobList is 32-bit). +unit-fsp-nvs-save:CFLAGS+=-DWOLFBOOT_FSP -DWOLFBOOT_FSP_NVS_CACHE \ + -DPRINTF_ENABLED -fno-pie +unit-fsp-nvs-save: unit-fsp-nvs-save.c ../../src/x86/fsp_nvs.c ../../src/x86/hob.c + gcc -o $@ unit-fsp-nvs-save.c $(CFLAGS) -no-pie $(LDFLAGS) + # unit-nrf5340-flash-protect runs the real hal_flash_protect() from # hal/nrf5340.c against a mock SPU region-permission array (F-13623: the # region count truncated, so a sub-block or partial-tail len left flash diff --git a/tools/unit-tests/unit-ata-sector-io.c b/tools/unit-tests/unit-ata-sector-io.c new file mode 100644 index 0000000000..6897531dff --- /dev/null +++ b/tools/unit-tests/unit-ata-sector-io.c @@ -0,0 +1,218 @@ +/* unit-ata-sector-io.c + * + * Unit tests for the AHCI sector read and write commands in src/x86/ata.c: + * a write at or past LBA 2^24 uses the 48-bit WRITE DMA EXT command, so its + * upper LBA bits reach the drive, and a command that ends in a task-file error is + * reported to the caller, including the read or write-back of a partial + * sector. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include +#include +#include + +#define TEST_AHCI_BASE 0x10000 + +/* Port registers: every command completes at once, except the + * mock_fail_issue-th one (counting from 1), which stays pending in CI while + * IS reports a task-file error. */ +static int mock_fail_issue; +static int mock_issues; +static int mock_port_error; +static uint32_t mock_ci; + +uint32_t mmio_read32(uintptr_t address) +{ + if (address == AHCI_PxCI(TEST_AHCI_BASE, 0)) + return mock_ci; + if (address == AHCI_PxIS(TEST_AHCI_BASE, 0)) + return mock_port_error ? AHCI_PORT_IS_TFES : 0; + return 0; +} + +void mmio_write32(uintptr_t address, uint32_t value) +{ + if (address == AHCI_PxCI(TEST_AHCI_BASE, 0)) { + if (++mock_issues == mock_fail_issue) { + mock_port_error = 1; + mock_ci = value; + } + else { + mock_port_error = 0; + mock_ci = 0; + } + } +} + +void panic(void) +{ + ck_abort_msg("panic!"); +} + +#include "../../src/x86/ata.c" + +/* struct ata_drive keeps 32-bit DMA addresses, so the command list and + * table must sit below 4 GB on a 64-bit test host. */ +static uint8_t *clb_mem; +static uint8_t *ctable_mem; +static uint8_t sector[512]; + +static void setup(void) +{ + mock_fail_issue = 0; + mock_issues = 0; + mock_port_error = 0; + mock_ci = 0; + clb_mem = mmap(NULL, sizeof(struct hba_cmd_header) * 32, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); + ck_assert_ptr_ne(clb_mem, MAP_FAILED); + ctable_mem = mmap(NULL, sizeof(struct hba_cmd_table) * 32, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); + ck_assert_ptr_ne(ctable_mem, MAP_FAILED); + + memset(&ATA_Drv[0], 0, sizeof(ATA_Drv[0])); + ATA_Drv[0].ahci_base = TEST_AHCI_BASE; + ATA_Drv[0].ahci_port = 0; + ATA_Drv[0].clb_port = (uint32_t)(uintptr_t)clb_mem; + ATA_Drv[0].ctable_port = (uint32_t)(uintptr_t)ctable_mem; + ATA_Drv[0].sector_size_shift = 9; + ATA_Drv[0].cached = CACHE_INVALID; + ata_drive_count = 0; +} + +static void teardown(void) +{ + munmap(clb_mem, sizeof(struct hba_cmd_header) * 32); + munmap(ctable_mem, sizeof(struct hba_cmd_table) * 32); +} + +static const struct fis_reg_h2d *issued_fis(void) +{ + const struct hba_cmd_header *cmd = (const struct hba_cmd_header *)clb_mem; + const struct hba_cmd_table *tbl = + (const struct hba_cmd_table *)(uintptr_t)cmd->ctba; + + return (const struct fis_reg_h2d *)(&tbl->cfis); +} + +START_TEST(test_write_past_lba24_uses_48bit_command) +{ + const struct fis_reg_h2d *fis; + + /* LBA 0x10123456: a 28-bit command takes bits 27:24 from the device + * field (0x40) and the write would land at 0x123456 */ + ck_assert_int_eq(ata_drive_write_sector(0, 0x10123456ULL, 1, sector), + 512); + fis = issued_fis(); + ck_assert_uint_eq(fis->command, ATA_CMD_WRITE_DMA_EX); + ck_assert_uint_eq(fis->lba0, 0x56); + ck_assert_uint_eq(fis->lba1, 0x34); + ck_assert_uint_eq(fis->lba2, 0x12); + ck_assert_uint_eq(fis->lba3, 0x10); + ck_assert_uint_eq(fis->count, 1); +} +END_TEST + +START_TEST(test_read_uses_48bit_command) +{ + ck_assert_int_eq(ata_drive_read_sector(0, 0x10123456ULL, 1, sector), 512); + ck_assert_uint_eq(issued_fis()->command, ATA_CMD_READ_DMA_EX); + ck_assert_uint_eq(issued_fis()->lba3, 0x10); +} +END_TEST + +START_TEST(test_port_error_is_reported) +{ + mock_fail_issue = 1; + ck_assert_int_lt(ata_drive_write_sector(0, 100, 1, sector), 0); + + mock_issues = 0; + ck_assert_int_lt(ata_drive_read_sector(0, 100, 1, sector), 0); +} +END_TEST + +START_TEST(test_partial_sector_write_error_is_reported) +{ + /* a partial sector is read into the cache (issue 1) and written back + * (issue 2); a failed write-back fails the call */ + mock_fail_issue = 2; + ck_assert_int_lt(ata_drive_write(0, 512 * 100 + 16, 32, sector), 0); + ck_assert(ATA_Drv[0].cached == CACHE_INVALID); + + /* a trailing partial sector after whole sectors: issue 1 writes the + * whole sector, 2 reads the tail sector, 3 writes it back */ + mock_issues = 0; + mock_fail_issue = 3; + ck_assert_int_lt(ata_drive_write(0, 512 * 100, 512 + 32, sector), 0); + + mock_issues = 0; + mock_fail_issue = 0; + ck_assert_int_eq(ata_drive_write(0, 512 * 100 + 16, 32, sector), 32); +} +END_TEST + +START_TEST(test_partial_sector_read_error_is_reported) +{ + /* a sector is cached, then a read of one whole sector (issue 1) plus a + * trailing partial sector (issue 2) fails on the partial sector */ + ck_assert_int_eq(ata_drive_read(0, 512 * 50 + 16, 32, sector), 32); + mock_issues = 0; + mock_fail_issue = 2; + ck_assert_int_lt(ata_drive_read(0, 512 * 100, 512 + 32, sector), 0); + + /* and a leading partial sector */ + mock_issues = 0; + mock_fail_issue = 1; + ck_assert_int_lt(ata_drive_read(0, 512 * 200 + 16, 32, sector), 0); +} +END_TEST + +static Suite *ata_sector_io_suite(void) +{ + Suite *s = suite_create("ata-sector-io"); + TCase *tc = tcase_create("ata-sector-io"); + + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_write_past_lba24_uses_48bit_command); + tcase_add_test(tc, test_read_uses_48bit_command); + tcase_add_test(tc, test_port_error_is_reported); + tcase_add_test(tc, test_partial_sector_write_error_is_reported); + tcase_add_test(tc, test_partial_sector_read_error_is_reported); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = ata_sector_io_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-boot-x86-fsp-measure.c b/tools/unit-tests/unit-boot-x86-fsp-measure.c new file mode 100644 index 0000000000..8e3094e8eb --- /dev/null +++ b/tools/unit-tests/unit-boot-x86-fsp-measure.c @@ -0,0 +1,142 @@ +/* unit-boot-x86-fsp-measure.c + * + * Unit tests for the stage 1 measurement of stage 2 (wolfBoot_image_measure() + * in src/boot_x86_fsp.c): the SHA-384 image digest is re-hashed with + * SHA-256 and extended into PCR 0, the value compute_pcr.py predicts. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#include "../../src/boot_x86_fsp.c" + +static uint8_t g_image_digest[WOLFBOOT_SHA_DIGEST_SIZE]; +static int g_verify_ret; +static int g_extend_calls; +static uint8_t g_extend_pcr; +static uint8_t g_extended[SHA256_DIGEST_SIZE]; + +uint8_t pci_config_read8(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off) +{ + (void)bus; (void)dev; (void)fun; (void)off; + return 0xff; +} + +uint16_t pci_config_read16(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off) +{ + (void)bus; (void)dev; (void)fun; (void)off; + return 0xffff; +} + +int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image) +{ + (void)image; + img->sha_hash = NULL; + return 0; +} + +int wolfBoot_verify_integrity(struct wolfBoot_image *img) +{ + if (g_verify_ret == 0) + img->sha_hash = g_image_digest; + return g_verify_ret; +} + +int wolfBoot_tpm2_extend(uint8_t pcrIndex, uint8_t *hash, int line) +{ + (void)line; + g_extend_calls++; + g_extend_pcr = pcrIndex; + memcpy(g_extended, hash, sizeof(g_extended)); + return 0; +} + +void wolfBoot_print_hexstr(const unsigned char *bin, unsigned long sz, + unsigned long maxLine) +{ + (void)bin; (void)sz; (void)maxLine; +} + +static void reset_mocks(void) +{ + unsigned int i; + + for (i = 0; i < sizeof(g_image_digest); i++) + g_image_digest[i] = (uint8_t)i; + g_verify_ret = 0; + g_extend_calls = 0; + g_extend_pcr = 0xff; + memset(g_extended, 0, sizeof(g_extended)); +} + +START_TEST(test_measure_extends_sha256_of_the_digest) +{ + /* sha256(bytes(range(48))) */ + static const uint8_t want[SHA256_DIGEST_SIZE] = { + 0x4d, 0xbd, 0xc2, 0xb2, 0xb6, 0x2c, 0xb0, 0x07, + 0x49, 0x78, 0x5b, 0xc8, 0x42, 0x02, 0x23, 0x6d, + 0xbc, 0x37, 0x77, 0xd7, 0x46, 0x60, 0x61, 0x1b, + 0x8e, 0x58, 0x81, 0x2f, 0x0c, 0xfd, 0xe6, 0xc3 + }; + uint8_t image[16]; + + reset_mocks(); + ck_assert_int_eq(wolfBoot_image_measure(image), 0); + ck_assert_int_eq(g_extend_calls, 1); + ck_assert_uint_eq(g_extend_pcr, WOLFBOOT_MEASURED_PCR_A); + ck_assert_int_eq(memcmp(g_extended, want, sizeof(want)), 0); +} +END_TEST + +START_TEST(test_measure_skips_an_unverified_image) +{ + uint8_t image[16]; + + reset_mocks(); + g_verify_ret = -1; + ck_assert_int_ne(wolfBoot_image_measure(image), 0); + ck_assert_int_eq(g_extend_calls, 0); +} +END_TEST + +static Suite *measure_suite(void) +{ + Suite *s = suite_create("boot-x86-fsp-measure"); + TCase *tc = tcase_create("boot-x86-fsp-measure"); + + tcase_add_test(tc, test_measure_extends_sha256_of_the_digest); + tcase_add_test(tc, test_measure_skips_an_unverified_image); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = measure_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-boot-x86-fsp-payload.c b/tools/unit-tests/unit-boot-x86-fsp-payload.c new file mode 100644 index 0000000000..296edcdb67 --- /dev/null +++ b/tools/unit-tests/unit-boot-x86-fsp-payload.c @@ -0,0 +1,207 @@ +/* unit-boot-x86-fsp-payload.c + * + * Unit tests for the x86 FSP payload hand-off (src/boot_x86_fsp_payload.c): + * the ELF segment check against wolfBoot's own memory, and the per-image + * choice between the Multiboot2 ELF and Linux paths, with the signed or + * built-in kernel command line. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include + +#include "../../src/boot_x86_fsp_payload.c" + +static struct stage2_parameter g_params; +static jmp_buf g_panic_jmp; +static int g_panics; +static int g_elf_loads; +static int g_mb2_jumps; +static int g_linux_loads; +static const char *g_linux_cmdline; +static uint8_t g_mb2_header[16]; + +void panic(void) +{ + g_panics++; + longjmp(g_panic_jmp, 1); +} + +struct stage2_parameter *stage2_get_parameters(void) +{ + return &g_params; +} + +void mptable_setup(void) +{ +} + +int x86_paging_dump_info(void) +{ + return 0; +} + +int x86_paging_map_memory(uint64_t va, uint64_t pa, uint32_t size) +{ + (void)va; + (void)pa; + (void)size; + return 0; +} + +int elf_load_image_mmu(uint8_t *image, uint32_t image_sz, uintptr_t *pentry, + elf_mmu_map_cb mmu_cb) +{ + (void)image; + (void)image_sz; + (void)mmu_cb; + g_elf_loads++; + *pentry = 0x1000; + return 0; +} + +uint8_t *mb2_find_header(uint8_t *image, int size) +{ + (void)image; + (void)size; + return g_mb2_header; +} + +int mb2_build_boot_info_header(uint8_t *boot_info, uint8_t *mb2_header, + void *params, unsigned max_size) +{ + (void)boot_info; + (void)mb2_header; + (void)params; + (void)max_size; + return 0; +} + +void mb2_jump(uintptr_t entry, uint32_t boot_info) +{ + (void)entry; + (void)boot_info; + g_mb2_jumps++; +} + +void load_linux(uint8_t *linux_image, uint32_t image_size, void *params, + const char *cmd_line) +{ + (void)linux_image; + (void)image_size; + (void)params; + g_linux_loads++; + g_linux_cmdline = cmd_line; +} + +static void reset_mocks(void) +{ + memset(&g_params, 0, sizeof(g_params)); + g_params.payload_size = 4096; + g_panics = 0; + g_elf_loads = 0; + g_mb2_jumps = 0; + g_linux_loads = 0; + g_linux_cmdline = NULL; +} + +START_TEST(test_elf_segments_outside_wolfboot_memory) +{ + /* below the reserved window, ending exactly at its start, and above + * 4 GB are all accepted; an empty segment is never checked */ + ck_assert_int_eq(boot_elf_map_cb(0, 0x200000, 0x1000), 0); + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_START - 0x1000, 0x1000), + 0); + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_END, 0x1000), 0); + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_START, 0), 0); +} +END_TEST + +START_TEST(test_elf_segments_over_wolfboot_memory) +{ + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_START - 0x1000, 0x1001), + -1); + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_START, 1), -1); + ck_assert_int_eq(boot_elf_map_cb(0, ELF_RESERVED_END - 1, 0x1000), -1); + ck_assert_int_eq(boot_elf_map_cb(0, 0x100000, 0xfff00000U), -1); +} +END_TEST + +START_TEST(test_elf_image_takes_the_multiboot2_path) +{ + uint32_t image[4] = { 0 }; + + reset_mocks(); + memcpy(image, ELF_IDENT_STR, 4); + if (setjmp(g_panic_jmp) == 0) + do_boot(image); + ck_assert_int_eq(g_panics, 0); + ck_assert_int_eq(g_elf_loads, 1); + ck_assert_int_eq(g_mb2_jumps, 1); + ck_assert_int_eq(g_linux_loads, 0); +} +END_TEST + +START_TEST(test_other_image_boots_linux) +{ + uint32_t image[4] = { 0 }; + + reset_mocks(); + memcpy(image, "MZ\0\0", 4); + do_boot(image); + ck_assert_int_eq(g_elf_loads, 0); + ck_assert_int_eq(g_linux_loads, 1); + ck_assert_ptr_eq(g_linux_cmdline, cmdline); + + reset_mocks(); + /* stage2_params carries a 32-bit address; only compared, not read */ + g_params.cmdline = 0x5a000000U; + do_boot(image); + ck_assert_int_eq(g_linux_loads, 1); + ck_assert_ptr_eq(g_linux_cmdline, (const char *)(uintptr_t)0x5a000000U); +} +END_TEST + +static Suite *payload_suite(void) +{ + Suite *s = suite_create("boot-x86-fsp-payload"); + TCase *tc = tcase_create("boot-x86-fsp-payload"); + + tcase_add_test(tc, test_elf_segments_outside_wolfboot_memory); + tcase_add_test(tc, test_elf_segments_over_wolfboot_memory); + tcase_add_test(tc, test_elf_image_takes_the_multiboot2_path); + tcase_add_test(tc, test_other_image_boots_linux); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = payload_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-fsp-nvs-save.c b/tools/unit-tests/unit-fsp-nvs-save.c new file mode 100644 index 0000000000..4c02fa5385 --- /dev/null +++ b/tools/unit-tests/unit-fsp-nvs-save.c @@ -0,0 +1,254 @@ +/* unit-fsp-nvs-save.c + * + * Unit tests for the stage 2 half of the FSP-M memory training data cache + * (src/x86/fsp_nvs.c): finding the non-volatile storage HOB, deciding + * whether the saved copy is current, and the erase/write sequence, against + * a RAM-backed flash area and a synthetic HOB list. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#define AREA_SIZE 0x2000U +static uint8_t g_area[AREA_SIZE] __attribute__((aligned(4096))); + +#define FSP_NVS_BASE ((uintptr_t)g_area) +#define FSP_NVS_SIZE AREA_SIZE +#define FSP_NVS_CACHE_FLUSH() do { } while (0) + +#include "stage2_params.h" + +static struct stage2_parameter g_params; +static int g_erase_calls, g_write_calls, g_fail_write_at; + +struct stage2_parameter *stage2_get_parameters(void) +{ + return &g_params; +} + +int tgl_spi_flash_erase(uint32_t address, uint32_t len) +{ + uintptr_t off = (uintptr_t)address - (uintptr_t)(uint32_t)FSP_NVS_BASE; + + g_erase_calls++; + ck_assert_uint_eq(address & 0xfff, 0); + ck_assert_uint_eq(len & 0xfff, 0); + ck_assert_uint_le(off + len, AREA_SIZE); + memset(g_area + off, 0xff, len); + return 0; +} + +int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len) +{ + uintptr_t off = (uintptr_t)address - (uintptr_t)(uint32_t)FSP_NVS_BASE; + + g_write_calls++; + if (g_fail_write_at == g_write_calls) + return -1; + ck_assert_uint_le(off + len, AREA_SIZE); + memcpy(g_area + off, buf, len); + return 0; +} + +#include "../../src/x86/hob.c" +/* FSP_NVS_BASE is a RAM buffer here, not a flash address */ +#undef BOOTLOADER_PARTITION_SIZE +#include "../../src/x86/fsp_nvs.c" + +/* HOB list in static storage: the non-PIE build keeps it below 4 GB, as + * stage2_parameter.hobList is 32-bit. */ +static uint8_t g_hobs[0x4000] __attribute__((aligned(8))); + +static uint8_t *add_guid_hob(uint8_t *p, const struct efi_guid *guid, + uint32_t data_len, uint8_t seed) +{ + struct efi_hob_guid_type *h = (struct efi_hob_guid_type *)p; + uint32_t i, total = (uint32_t)sizeof(*h) + data_len; + + total = (total + 7U) & ~7U; + h->header.hob_type = EFI_HOB_TYPE_GUID_EXTENSION; + h->header.hob_length = (uint16_t)total; + h->header.reserved = 0; + memcpy(&h->name, guid, sizeof(*guid)); + for (i = 0; i < data_len; i++) + p[sizeof(*h) + i] = (uint8_t)(seed + i); + return p + total; +} + +static void end_hobs(uint8_t *p) +{ + struct efi_hob_generic_header *h = (struct efi_hob_generic_header *)p; + + h->hob_type = EFI_HOB_TYPE_END_OF_HOB_LIST; + h->hob_length = sizeof(*h); + h->reserved = 0; +} + +static const struct efi_guid other_guid = { + 0x11111111, 0x2222, 0x3333, { 1, 2, 3, 4, 5, 6, 7, 8 } +}; + +/* An unrelated GUID HOB, then the NVS HOB with data_len bytes. */ +static void build_hobs(uint32_t data_len, uint8_t seed) +{ + uint8_t *p = g_hobs; + + memset(g_hobs, 0, sizeof(g_hobs)); + p = add_guid_hob(p, &other_guid, 24, 0x55); + p = add_guid_hob(p, &fsp_nvs_hob_guid, data_len, seed); + end_hobs(p); +} + +static void setup(uint32_t key) +{ + memset(&g_params, 0, sizeof(g_params)); + g_params.hobList = (uint32_t)(uintptr_t)g_hobs; + g_params.fsp_nvs_key = key; + g_erase_calls = g_write_calls = g_fail_write_at = 0; +} + +static const struct fsp_nvs_record *area(void) +{ + return (const struct fsp_nvs_record *)g_area; +} + +START_TEST(test_save_into_empty_area) +{ + ck_assert_uint_lt((uintptr_t)g_hobs, 0x100000000ULL); + memset(g_area, 0xff, sizeof(g_area)); + setup(0xabcd); + build_hobs(1000, 3); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 1); + ck_assert_int_eq(fsp_nvs_record_valid(area(), 0xabcd, AREA_SIZE), 1); + ck_assert_uint_eq(area()->len, 1000); + /* the unrelated HOB is 48 bytes, then the NVS HOB header */ + ck_assert_int_eq(memcmp(area()->data, + g_hobs + 48 + sizeof(struct efi_hob_guid_type), 1000), 0); +} +END_TEST + +START_TEST(test_current_copy_is_not_rewritten) +{ + memset(g_area, 0xff, sizeof(g_area)); + setup(0xabcd); + build_hobs(1000, 3); + fsp_nvs_save(); + setup(0xabcd); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 0); + ck_assert_int_eq(g_write_calls, 0); +} +END_TEST + +START_TEST(test_changed_data_or_key_is_rewritten) +{ + memset(g_area, 0xff, sizeof(g_area)); + setup(0xabcd); + build_hobs(1000, 3); + fsp_nvs_save(); + + setup(0xabcd); + build_hobs(1000, 4); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 1); + ck_assert_uint_eq(area()->data[0], 4); + + setup(0x1234); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 1); + ck_assert_int_eq(fsp_nvs_record_valid(area(), 0x1234, AREA_SIZE), 1); +} +END_TEST + +START_TEST(test_missing_or_oversized_hob) +{ + uint8_t *p; + + memset(g_area, 0xff, sizeof(g_area)); + setup(1); + memset(g_hobs, 0, sizeof(g_hobs)); + p = add_guid_hob(g_hobs, &other_guid, 24, 0); + end_hobs(p); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 0); + + setup(1); + build_hobs(AREA_SIZE - sizeof(struct fsp_nvs_record) + 1, 0); + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 0); +} +END_TEST + +START_TEST(test_zero_length_hob_stops_the_walk) +{ + struct efi_hob_generic_header *h = + (struct efi_hob_generic_header *)g_hobs; + + memset(g_area, 0xff, sizeof(g_area)); + setup(1); + memset(g_hobs, 0, sizeof(g_hobs)); + h->hob_type = EFI_HOB_TYPE_GUID_EXTENSION; + h->hob_length = 0; + fsp_nvs_save(); + ck_assert_int_eq(g_erase_calls, 0); +} +END_TEST + +START_TEST(test_failed_header_write_leaves_no_record) +{ + memset(g_area, 0xff, sizeof(g_area)); + setup(7); + build_hobs(1000, 3); + /* the data is written first, then the header; fail the header */ + g_fail_write_at = 2; + fsp_nvs_save(); + ck_assert_int_eq(fsp_nvs_record_valid(area(), 7, AREA_SIZE), 0); +} +END_TEST + +static Suite *fsp_nvs_save_suite(void) +{ + Suite *s = suite_create("fsp-nvs-save"); + TCase *tc = tcase_create("fsp-nvs-save"); + + tcase_add_test(tc, test_save_into_empty_area); + tcase_add_test(tc, test_current_copy_is_not_rewritten); + tcase_add_test(tc, test_changed_data_or_key_is_rewritten); + tcase_add_test(tc, test_missing_or_oversized_hob); + tcase_add_test(tc, test_zero_length_hob_stops_the_walk); + tcase_add_test(tc, test_failed_header_write_leaves_no_record); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = fsp_nvs_save_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-fsp-nvs.c b/tools/unit-tests/unit-fsp-nvs.c new file mode 100644 index 0000000000..84b4c0612b --- /dev/null +++ b/tools/unit-tests/unit-fsp-nvs.c @@ -0,0 +1,247 @@ +/* unit-fsp-nvs.c + * + * Unit tests for the FSP-M memory training data cache (src/x86/fsp_nvs.c): + * the record check that decides whether saved data is handed back to + * FspMemInit, and the stage 1 hand-off itself. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#define AREA_SIZE 0x1000U +static uint8_t g_area[AREA_SIZE] __attribute__((aligned(16))); + +#define FSP_NVS_BASE ((uintptr_t)g_area) +#define FSP_NVS_SIZE AREA_SIZE + +#include "stage2_params.h" + +static struct stage2_parameter g_params; + +struct stage2_parameter *stage2_get_parameters(void) +{ + return &g_params; +} + +static uint8_t g_cmos[128]; + +uint8_t cmos_read(uint8_t index) +{ + return g_cmos[index & 0x7F]; +} + +void cmos_write(uint8_t index, uint8_t value) +{ + g_cmos[index & 0x7F] = value; +} + +/* FSP_NVS_BASE is a RAM buffer here, not a flash address */ +#undef BOOTLOADER_PARTITION_SIZE +#include "../../src/x86/fsp_nvs.c" + +#define UDP_SIZE 0x100U +#define FSPM_SIZE 0x200U + +static uint8_t g_udp[UDP_SIZE]; +static uint8_t g_fspm[FSPM_SIZE]; + +static FSPM_ARCH_UPD *arch(void) +{ + return (FSPM_ARCH_UPD *)(g_udp + sizeof(FSP_UPD_HEADER)); +} + +static uint32_t expected_key(void) +{ + uint32_t key = fsp_nvs_crc32(0, g_fspm, FSPM_SIZE); + + return fsp_nvs_crc32(key, g_udp, UDP_SIZE); +} + +static struct fsp_nvs_record *write_record(uint32_t key, uint32_t len) +{ + struct fsp_nvs_record *rec = (struct fsp_nvs_record *)g_area; + uint32_t i; + + memset(g_area, 0xff, sizeof(g_area)); + rec->magic = FSP_NVS_MAGIC; + rec->key = key; + rec->len = len; + for (i = 0; i < len; i++) + rec->data[i] = (uint8_t)(i * 7 + 1); + rec->crc = fsp_nvs_crc32(0, rec->data, len); + return rec; +} + +static void reset_udp(void) +{ + uint32_t i; + + for (i = 0; i < UDP_SIZE; i++) + g_udp[i] = (uint8_t)i; + for (i = 0; i < FSPM_SIZE; i++) + g_fspm[i] = (uint8_t)(i * 3); + arch()->NvsBufferPtr = 0; + arch()->BootMode = BOOT_WITH_FULL_CONFIGURATION; + memset(&g_params, 0, sizeof(g_params)); + memset(g_cmos, 0, sizeof(g_cmos)); +} + +START_TEST(test_crc32_check_value) +{ + ck_assert_uint_eq(fsp_nvs_crc32(0, (const uint8_t *)"123456789", 9), + 0xCBF43926U); + /* chaining two halves equals one pass */ + ck_assert_uint_eq(fsp_nvs_crc32(fsp_nvs_crc32(0, + (const uint8_t *)"1234", 4), + (const uint8_t *)"56789", 5), 0xCBF43926U); +} +END_TEST + +START_TEST(test_record_valid) +{ + struct fsp_nvs_record *rec = write_record(0x1234, 100); + + ck_assert_int_eq(fsp_nvs_record_valid(rec, 0x1234, AREA_SIZE), 1); + ck_assert_int_eq(fsp_nvs_record_valid(rec, 0x1235, AREA_SIZE), 0); +} +END_TEST + +START_TEST(test_record_rejects_bad_fields) +{ + struct fsp_nvs_record *rec; + + rec = write_record(1, 100); + rec->magic ^= 1; + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 0); + + rec = write_record(1, 100); + rec->data[50] ^= 0x80; + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 0); + + rec = write_record(1, 100); + rec->len = 0; + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 0); + + /* data exactly filling the area is accepted, one byte more is not */ + rec = write_record(1, AREA_SIZE - sizeof(*rec)); + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 1); + rec->len = AREA_SIZE - sizeof(*rec) + 1; + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 0); + rec->len = 0xffffffffU; + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, AREA_SIZE), 0); + + /* an area too small for the header is never valid */ + rec = write_record(1, 4); + ck_assert_int_eq(fsp_nvs_record_valid(rec, 1, sizeof(*rec)), 0); +} +END_TEST + +START_TEST(test_apply_without_record) +{ + reset_udp(); + memset(g_area, 0xff, sizeof(g_area)); + ck_assert_int_eq(fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE), 0); + ck_assert_uint_eq(arch()->NvsBufferPtr, 0); + ck_assert_uint_eq(arch()->BootMode, BOOT_WITH_FULL_CONFIGURATION); + ck_assert_uint_eq(g_params.fsp_nvs_key, expected_key()); +} +END_TEST + +START_TEST(test_apply_with_matching_record) +{ + struct fsp_nvs_record *rec; + + reset_udp(); + rec = write_record(expected_key(), 200); + ck_assert_int_eq(fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE), 1); + ck_assert_uint_eq(arch()->NvsBufferPtr, + (uint32_t)(uintptr_t)rec->data); + ck_assert_uint_eq(arch()->BootMode, + BOOT_ASSUMING_NO_CONFIGURATION_CHANGES); +} +END_TEST + +START_TEST(test_apply_skips_a_rejected_record_until_cleared) +{ + /* after FspMemInit failed with the record, boots train from scratch, + * across FSP-M's reset requests, until a FspMemInit succeeds */ + reset_udp(); + write_record(expected_key(), 200); + fsp_nvs_skip(1); + ck_assert_int_eq(fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE), 0); + ck_assert_uint_eq(arch()->NvsBufferPtr, 0); + ck_assert_uint_eq(arch()->BootMode, BOOT_WITH_FULL_CONFIGURATION); + ck_assert_uint_eq(g_params.fsp_nvs_key, expected_key()); + ck_assert_int_eq(fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE), 0); + + fsp_nvs_skip(0); + ck_assert_int_eq(fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE), 1); +} +END_TEST + +START_TEST(test_apply_rejects_changed_config) +{ + reset_udp(); + write_record(expected_key(), 200); + + /* a different FSP-M build invalidates the record, even with the same + * revision in its header */ + g_fspm[FSPM_SIZE / 2] ^= 1; + fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE); + ck_assert_uint_eq(arch()->NvsBufferPtr, 0); + + /* so does any change to the UPDs */ + reset_udp(); + g_udp[UDP_SIZE - 1] ^= 1; + fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE); + ck_assert_uint_eq(arch()->NvsBufferPtr, 0); + ck_assert_uint_eq(arch()->BootMode, BOOT_WITH_FULL_CONFIGURATION); +} +END_TEST + +static Suite *fsp_nvs_suite(void) +{ + Suite *s = suite_create("fsp-nvs"); + TCase *tc = tcase_create("fsp-nvs"); + + tcase_add_test(tc, test_crc32_check_value); + tcase_add_test(tc, test_record_valid); + tcase_add_test(tc, test_record_rejects_bad_fields); + tcase_add_test(tc, test_apply_without_record); + tcase_add_test(tc, test_apply_with_matching_record); + tcase_add_test(tc, test_apply_rejects_changed_config); + tcase_add_test(tc, test_apply_skips_a_rejected_record_until_cleared); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = fsp_nvs_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-tgl-spi-hwseq.c b/tools/unit-tests/unit-tgl-spi-hwseq.c new file mode 100644 index 0000000000..f24c2e0896 --- /dev/null +++ b/tools/unit-tests/unit-tgl-spi-hwseq.c @@ -0,0 +1,289 @@ +/* unit-tgl-spi-hwseq.c + * + * Unit tests for the Tiger Lake SPI hardware-sequencing erase/write used to + * save the FSP memory training data (hal/x86_fsp_tgl.c, extracted by the + * Makefile): BIOS-region address translation, 64-byte cycle splitting, the + * BIOS_CNTL write-enable handling and the error paths, against a model of + * the SPI controller registers and a RAM-backed flash window. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include + +#define MMIO_BASE 0xFED40000UL +#define SPI_BIOS_CNTL_EXPECTED 0xdc + +#include "kontron_spi_extract.h" + +/* BIOS region: linear 0x01400000-0x01FFFFFF, the top 12 MB of 32 MB */ +#define FREG1_INIT 0x1FFF1400U +#define REG_HSFSTS 0x04 +#define REG_FADDR 0x08 +#define REG_FDATA0 0x10 +#define REG_FREG1 0x58 + +/* Modeled flash window: linear 0x019E0000 + 128 KB */ +#define WIN_LINEAR 0x019E0000U +#define WIN_SIZE 0x20000U +#define WIN_MMIO 0xFF9E0000U + +static uint32_t g_mmio[64]; +static uint32_t g_pci_cmd; +static uint8_t g_bios_cntl; +static int g_wpd_stuck_low; +static int g_fail_cycle; +static int g_never_done; +static uint8_t g_flash[WIN_SIZE]; +static struct { + uint32_t cycle, faddr, len; +} g_log[64]; +static int g_ncycles; + +static void sim_reset(void) +{ + memset(g_mmio, 0, sizeof(g_mmio)); + g_mmio[REG_FREG1 / 4] = FREG1_INIT; + g_pci_cmd = 0x0001; + g_bios_cntl = 0; + g_wpd_stuck_low = g_fail_cycle = g_never_done = 0; + g_ncycles = 0; + memset(g_flash, 0x5a, sizeof(g_flash)); +} + +uint32_t pci_config_read32(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off) +{ + (void)bus; (void)dev; (void)fun; + if (off == PCI_BAR0_OFFSET) + return (uint32_t)MMIO_BASE; + if (off == PCI_COMMAND_OFFSET) + return g_pci_cmd; + return 0; +} + +void pci_config_write32(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off, + uint32_t val) +{ + (void)bus; (void)dev; (void)fun; + if (off == PCI_COMMAND_OFFSET) + g_pci_cmd = val; +} + +uint8_t pci_config_read8(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off) +{ + (void)bus; (void)dev; (void)fun; + ck_assert_uint_eq(off, SPI_BIOS_CNTL_EXPECTED); + return g_bios_cntl; +} + +void pci_config_write8(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off, + uint8_t val) +{ + (void)bus; (void)dev; (void)fun; + ck_assert_uint_eq(off, SPI_BIOS_CNTL_EXPECTED); + g_bios_cntl = g_wpd_stuck_low ? (uint8_t)(val & ~1U) : val; +} + +void delay(int msec) +{ + (void)msec; +} + +static void run_cycle(uint32_t ctl) +{ + uint32_t cycle = (ctl >> 17) & 0xf; + uint32_t len = ((ctl >> 24) & 0x3f) + 1; + uint32_t faddr = g_mmio[REG_FADDR / 4]; + uint32_t i; + + /* a cycle only runs with flash writes enabled */ + ck_assert_uint_eq(g_bios_cntl & 1U, 1U); + if (g_ncycles < (int)(sizeof(g_log) / sizeof(g_log[0]))) { + g_log[g_ncycles].cycle = cycle; + g_log[g_ncycles].faddr = faddr; + g_log[g_ncycles].len = len; + } + g_ncycles++; + if (faddr >= WIN_LINEAR && faddr < WIN_LINEAR + WIN_SIZE) { + if (cycle == 3) + memset(g_flash + (faddr - WIN_LINEAR), 0xff, 0x1000); + else if (cycle == 2) + for (i = 0; i < len; i++) + g_flash[faddr - WIN_LINEAR + i] = + ((uint8_t *)&g_mmio[REG_FDATA0 / 4])[i]; + } + if (g_never_done) + return; + g_mmio[REG_HSFSTS / 4] |= (g_fail_cycle ? 0x2U : 0x1U); +} + +static void mmio_write32(uintptr_t address, uint32_t value) +{ + uint32_t off = (uint32_t)(address - MMIO_BASE); + + if (off == REG_HSFSTS) { + /* status bits are write-1-to-clear */ + g_mmio[REG_HSFSTS / 4] &= ~(value & 0x7U); + if (value & (1U << 16)) + run_cycle(value); + return; + } + g_mmio[off / 4] = value; +} + +static uint32_t mmio_read32(uintptr_t address) +{ + return g_mmio[(address - MMIO_BASE) / 4]; +} + +#include "tgl_spi_hwseq_extract.h" + +START_TEST(test_linear_mapping) +{ + sim_reset(); + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, WIN_MMIO, 0x1000), + WIN_LINEAR); + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, 0xFFFFFFFFU, 1), + 0x01FFFFFF); + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, 0xFF400000U, 0x1000), + 0x01400000); + /* below the BIOS region, past its top, or empty */ + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, 0xFF3FF000U, 0x1000), -1); + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, 0xFFFFF000U, 0x2000), -1); + ck_assert_int_eq(tgl_spi_linear(MMIO_BASE, WIN_MMIO, 0), -1); +} +END_TEST + +START_TEST(test_erase_sectors) +{ + sim_reset(); + ck_assert_int_eq(tgl_spi_flash_erase(WIN_MMIO, 0x2000), 0); + ck_assert_int_eq(g_ncycles, 2); + ck_assert_uint_eq(g_log[0].cycle, 3); + ck_assert_uint_eq(g_log[0].faddr, WIN_LINEAR); + ck_assert_uint_eq(g_log[1].faddr, WIN_LINEAR + 0x1000); + ck_assert_uint_eq(g_flash[0], 0xff); + ck_assert_uint_eq(g_flash[0x1fff], 0xff); + ck_assert_uint_eq(g_flash[0x2000], 0x5a); + /* write enable dropped and PCI command restored afterwards */ + ck_assert_uint_eq(g_bios_cntl & 1U, 0); + ck_assert_uint_eq(g_pci_cmd, 0x0001); +} +END_TEST + +START_TEST(test_erase_rejects_unaligned) +{ + sim_reset(); + ck_assert_int_eq(tgl_spi_flash_erase(WIN_MMIO + 0x10, 0x1000), -1); + ck_assert_int_eq(tgl_spi_flash_erase(WIN_MMIO, 0x800), -1); + ck_assert_int_eq(g_ncycles, 0); +} +END_TEST + +START_TEST(test_write_splits_on_64_byte_boundaries) +{ + uint8_t buf[150]; + uint32_t i; + + sim_reset(); + for (i = 0; i < sizeof(buf); i++) + buf[i] = (uint8_t)(i + 1); + ck_assert_int_eq(tgl_spi_flash_write(WIN_MMIO + 0x10, buf, sizeof(buf)), + 0); + ck_assert_int_eq(g_ncycles, 3); + ck_assert_uint_eq(g_log[0].faddr, WIN_LINEAR + 0x10); + ck_assert_uint_eq(g_log[0].len, 48); + ck_assert_uint_eq(g_log[1].faddr, WIN_LINEAR + 0x40); + ck_assert_uint_eq(g_log[1].len, 64); + ck_assert_uint_eq(g_log[2].faddr, WIN_LINEAR + 0x80); + ck_assert_uint_eq(g_log[2].len, 38); + ck_assert_uint_eq(g_log[0].cycle, 2); + ck_assert_int_eq(memcmp(g_flash + 0x10, buf, sizeof(buf)), 0); + ck_assert_uint_eq(g_flash[0x10 + sizeof(buf)], 0x5a); + ck_assert_uint_eq(g_bios_cntl & 1U, 0); +} +END_TEST + +START_TEST(test_write_outside_bios_region) +{ + uint8_t buf[4] = { 0 }; + + sim_reset(); + ck_assert_int_eq(tgl_spi_flash_write(0xFF000000U, buf, sizeof(buf)), -1); + ck_assert_int_eq(g_ncycles, 0); + ck_assert_uint_eq(g_bios_cntl & 1U, 0); +} +END_TEST + +START_TEST(test_cycle_error_and_timeout) +{ + uint8_t buf[8] = { 0 }; + + sim_reset(); + g_fail_cycle = 1; + ck_assert_int_eq(tgl_spi_flash_write(WIN_MMIO, buf, sizeof(buf)), -1); + + sim_reset(); + g_never_done = 1; + ck_assert_int_eq(tgl_spi_flash_erase(WIN_MMIO, 0x1000), -1); +} +END_TEST + +START_TEST(test_write_enable_refused) +{ + uint8_t buf[8] = { 0 }; + + sim_reset(); + g_wpd_stuck_low = 1; + ck_assert_int_eq(tgl_spi_flash_write(WIN_MMIO, buf, sizeof(buf)), -1); + ck_assert_int_eq(tgl_spi_flash_erase(WIN_MMIO, 0x1000), -1); + ck_assert_int_eq(g_ncycles, 0); +} +END_TEST + +static Suite *hwseq_suite(void) +{ + Suite *s = suite_create("tgl-spi-hwseq"); + TCase *tc = tcase_create("tgl-spi-hwseq"); + + tcase_add_test(tc, test_linear_mapping); + tcase_add_test(tc, test_erase_sectors); + tcase_add_test(tc, test_erase_rejects_unaligned); + tcase_add_test(tc, test_write_splits_on_64_byte_boundaries); + tcase_add_test(tc, test_write_outside_bios_region); + tcase_add_test(tc, test_cycle_error_and_timeout); + tcase_add_test(tc, test_write_enable_refused); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = hwseq_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-update-disk-fsp.c b/tools/unit-tests/unit-update-disk-fsp.c index e38e4a1a8f..e63d119050 100644 --- a/tools/unit-tests/unit-update-disk-fsp.c +++ b/tools/unit-tests/unit-update-disk-fsp.c @@ -37,6 +37,7 @@ static uint8_t part_b_image[IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE]; static int mock_do_boot_called; static const uint32_t *mock_boot_address; static struct stage2_parameter mock_stage2_params; +static const char *mock_cmdline; static void set_u16_le(uint8_t *dst, uint16_t value) { @@ -74,6 +75,8 @@ static void reset_mocks(void) build_image(part_b_image, 7, TEST_PAYLOAD_SIZE, 0xB2); mock_do_boot_called = 0; mock_boot_address = NULL; + mock_cmdline = NULL; + mock_stage2_params.cmdline = 0xffffffffU; /* The image must fit between load_address and tolum: exactly one * TEST_PAYLOAD_SIZE payload. */ mock_stage2_params.tolum = @@ -170,6 +173,17 @@ int wolfBoot_image_part_matches(struct wolfBoot_image* img) return 0; } +uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) +{ + (void)haystack; + if (type != HDR_CMDLINE || mock_cmdline == NULL) { + *ptr = NULL; + return 0; + } + *ptr = (uint8_t *)mock_cmdline; + return (uint16_t)strlen(mock_cmdline); +} + int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity) { (void)capacity; @@ -273,6 +287,37 @@ START_TEST(test_fsp_fitting_slot_boots) } END_TEST +START_TEST(test_fsp_signed_cmdline_is_passed_on) +{ + /* A signed HDR_CMDLINE reaches the payload loader; without one the + * loader is told there is none. */ + reset_mocks(); + mock_cmdline = "console=ttyS4,115200 root=/dev/sda2"; + wolfBoot_start(); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_uint_ne(mock_stage2_params.cmdline, 0); + + reset_mocks(); + wolfBoot_start(); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_uint_eq(mock_stage2_params.cmdline, 0); +} +END_TEST + +START_TEST(test_fsp_signed_cmdline_too_long_is_ignored) +{ + static char long_cmdline[257]; + + reset_mocks(); + memset(long_cmdline, 'a', sizeof(long_cmdline) - 1); + long_cmdline[sizeof(long_cmdline) - 1] = '\0'; + mock_cmdline = long_cmdline; + wolfBoot_start(); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_uint_eq(mock_stage2_params.cmdline, 0); +} +END_TEST + Suite *wolfboot_suite(void) { Suite *s = suite_create("wolfBoot"); @@ -282,6 +327,8 @@ Suite *wolfboot_suite(void) tcase_add_test(tc, test_fsp_both_slots_oversized_panics); tcase_add_test(tc, test_fsp_inverted_tolum_rejects_both_slots); tcase_add_test(tc, test_fsp_fitting_slot_boots); + tcase_add_test(tc, test_fsp_signed_cmdline_is_passed_on); + tcase_add_test(tc, test_fsp_signed_cmdline_too_long_is_ignored); suite_add_tcase(s, tc); return s; diff --git a/tools/unit-tests/unit-x86-acpi.c b/tools/unit-tests/unit-x86-acpi.c new file mode 100644 index 0000000000..0853f627d7 --- /dev/null +++ b/tools/unit-tests/unit-x86-acpi.c @@ -0,0 +1,221 @@ +/* unit-x86-acpi.c + * + * Unit tests for the x86 FSP ACPI table builders (src/x86/acpi.c): the MADT + * CPU list, the TPM2, HPET and DMAR tables, their lengths and checksums, + * with the PCI, MMIO and CPU topology reads mocked. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#include "../../src/x86/acpi.c" + +static int g_cpus; +static int g_igd; +static uint32_t g_bdsm, g_tolud; +static uint8_t g_buf[4096] __attribute__((aligned(16))); + +int x86_cpu_apic_ids(uint8_t *ids, int max) +{ + const uint8_t order[] = { 0, 2, 4, 6, 1, 3, 5, 7 }; + int i; + + for (i = 0; i < g_cpus && i < max; i++) + ids[i] = order[i]; + return i; +} + +uint32_t mmio_read32(uintptr_t address) +{ + if (address == ACPI_HPET_BASE) + return 0x8086a701U; + if (address == ACPI_LAPIC_ADDR + 0x20) + return 0x05000000U; + return 0; +} + +uint32_t pci_config_read32(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off) +{ + (void)bus; + if (dev == 2 && fun == 0 && off == 0) + return g_igd ? 0x9a498086U : 0xffffffffU; + if (dev == 2 && fun == 0 && off == 0x5c) + return g_bdsm | 0x1U; + if (dev == 0 && fun == 0 && off == 0xbc) + return g_tolud | 0x1U; + return 0xffffffffU; +} + +static void reset_mocks(void) +{ + memset(g_buf, 0xa5, sizeof(g_buf)); + g_cpus = 4; + g_igd = 1; + g_bdsm = 0x4c000000U; + g_tolud = 0x50400000U; +} + +static int table_ok(const uint8_t *t, const char *sig, uint32_t len) +{ + const struct acpi_header *h = (const struct acpi_header *)t; + uint8_t sum = 0; + uint32_t i; + + if (memcmp(h->signature, sig, 4) != 0 || h->length != len) + return 0; + for (i = 0; i < len; i++) + sum = (uint8_t)(sum + t[i]); + return sum == 0; +} + +START_TEST(test_madt_lists_present_cpus) +{ + uint8_t *end; + const struct madt_lapic *l; + int i, n; + + for (n = 4; n <= 8; n += 4) { + reset_mocks(); + g_cpus = n; + end = acpi_build_madt(g_buf); + ck_assert(table_ok(g_buf, "APIC", (uint32_t)(end - g_buf))); + l = (const struct madt_lapic *)(g_buf + sizeof(struct acpi_madt)); + for (i = 0; i < n; i++) { + ck_assert_uint_eq(l[i].type, MADT_TYPE_LAPIC); + ck_assert_uint_eq(l[i].acpi_processor_id, i); + ck_assert_uint_eq(l[i].flags, MADT_LAPIC_ENABLED); + } + ck_assert_uint_eq(l[3].apic_id, 6); + /* the entry after the last CPU is the IOAPIC */ + ck_assert_uint_eq(l[n].type, MADT_TYPE_IOAPIC); + } + ck_assert_uint_eq(l[4].apic_id, 1); +} +END_TEST + +START_TEST(test_madt_falls_back_to_the_bsp) +{ + const struct madt_lapic *l; + + reset_mocks(); + g_cpus = 0; + acpi_build_madt(g_buf); + l = (const struct madt_lapic *)(g_buf + sizeof(struct acpi_madt)); + ck_assert_uint_eq(l[0].apic_id, 5); + ck_assert_uint_eq(l[1].type, MADT_TYPE_IOAPIC); +} +END_TEST + +START_TEST(test_tpm2_and_hpet) +{ + const struct acpi_tpm2 *tpm2 = (const struct acpi_tpm2 *)g_buf; + const struct acpi_hpet *hpet = (const struct acpi_hpet *)g_buf; + + reset_mocks(); + ck_assert_ptr_eq(acpi_build_tpm2(g_buf), g_buf + sizeof(*tpm2)); + ck_assert(table_ok(g_buf, "TPM2", sizeof(*tpm2))); + ck_assert_uint_eq(tpm2->start_method, ACPI_TPM2_START_TIS); + ck_assert_uint_eq(tpm2->control_address, 0); + + reset_mocks(); + ck_assert_ptr_eq(acpi_build_hpet(g_buf), g_buf + sizeof(*hpet)); + ck_assert(table_ok(g_buf, "HPET", sizeof(*hpet))); + ck_assert_uint_eq(hpet->block_id, 0x8086a701U); + ck_assert_uint_eq(hpet->base.address, ACPI_HPET_BASE); +} +END_TEST + +START_TEST(test_dmar_with_igd) +{ + const struct dmar_drhd *gfx, *def; + const struct dmar_rmrr *rmrr; + const struct dmar_scope *sc; + uint8_t *end; + + reset_mocks(); + end = acpi_build_dmar(g_buf); + ck_assert(table_ok(g_buf, "DMAR", (uint32_t)(end - g_buf))); + + gfx = (const struct dmar_drhd *)(g_buf + sizeof(struct acpi_dmar)); + ck_assert_uint_eq(gfx->base, DMAR_GFX_UNIT); + ck_assert_uint_eq(gfx->flags, 0); + sc = (const struct dmar_scope *)(gfx + 1); + ck_assert_uint_eq(sc->type, DMAR_SCOPE_ENDPOINT); + ck_assert_uint_eq(sc->dev, 2); + + def = (const struct dmar_drhd *)((const uint8_t *)gfx + gfx->length); + ck_assert_uint_eq(def->base, DMAR_DEFAULT_UNIT); + ck_assert_uint_eq(def->flags, DMAR_INCLUDE_PCI_ALL); + sc = (const struct dmar_scope *)(def + 1); + ck_assert_uint_eq(sc[0].type, DMAR_SCOPE_IOAPIC); + ck_assert_uint_eq(sc[0].enumeration_id, ACPI_IOAPIC_ID); + ck_assert_uint_eq(sc[1].type, DMAR_SCOPE_HPET); + + rmrr = (const struct dmar_rmrr *)((const uint8_t *)def + def->length); + ck_assert_uint_eq(rmrr->type, DMAR_RMRR); + ck_assert_uint_eq(rmrr->base, 0x4c000000U); + ck_assert_uint_eq(rmrr->limit, 0x503fffffU); + ck_assert_ptr_eq((const uint8_t *)rmrr + rmrr->length, end); +} +END_TEST + +START_TEST(test_dmar_without_igd) +{ + const struct dmar_drhd *def; + uint8_t *end; + + reset_mocks(); + g_igd = 0; + end = acpi_build_dmar(g_buf); + ck_assert(table_ok(g_buf, "DMAR", (uint32_t)(end - g_buf))); + def = (const struct dmar_drhd *)(g_buf + sizeof(struct acpi_dmar)); + ck_assert_uint_eq(def->base, DMAR_DEFAULT_UNIT); + /* no graphics unit and no reserved region after the default unit */ + ck_assert_ptr_eq((const uint8_t *)def + def->length, end); +} +END_TEST + +static Suite *acpi_suite(void) +{ + Suite *s = suite_create("x86-acpi"); + TCase *tc = tcase_create("x86-acpi"); + + tcase_add_test(tc, test_madt_lists_present_cpus); + tcase_add_test(tc, test_madt_falls_back_to_the_bsp); + tcase_add_test(tc, test_tpm2_and_hpet); + tcase_add_test(tc, test_dmar_with_igd); + tcase_add_test(tc, test_dmar_without_igd); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = acpi_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-x86-apic-ids.c b/tools/unit-tests/unit-x86-apic-ids.c new file mode 100644 index 0000000000..943e678dee --- /dev/null +++ b/tools/unit-tests/unit-x86-apic-ids.c @@ -0,0 +1,112 @@ +/* unit-x86-apic-ids.c + * + * Unit tests for the CPU list the x86 FSP MADT and MP table are built from + * (x86_apic_ids_from_topology() in src/x86/common.c, extracted by the + * Makefile): enabled cores and threads from MSR_CORE_THREAD_COUNT, APIC ID + * layout from the SMT shift, primary threads first. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#include "x86_apic_ids_extract.h" + +#define COUNTS(cores, threads) (((uint32_t)(cores) << 16) | (threads)) + +START_TEST(test_ht_off_four_cores) +{ + const uint8_t want[] = { 0, 2, 4, 6 }; + uint8_t ids[8]; + + memset(ids, 0xee, sizeof(ids)); + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(4, 4), 1, ids, 8), 4); + ck_assert_int_eq(memcmp(ids, want, sizeof(want)), 0); +} +END_TEST + +START_TEST(test_ht_on_primaries_first) +{ + const uint8_t want[] = { 0, 2, 4, 6, 1, 3, 5, 7 }; + uint8_t ids[8]; + + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(4, 8), 1, ids, 8), 8); + ck_assert_int_eq(memcmp(ids, want, sizeof(want)), 0); +} +END_TEST + +START_TEST(test_fewer_cores) +{ + const uint8_t want[] = { 0, 2, 1, 3 }; + uint8_t ids[8]; + + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(2, 4), 1, ids, 8), 4); + ck_assert_int_eq(memcmp(ids, want, sizeof(want)), 0); +} +END_TEST + +START_TEST(test_capacity_is_respected) +{ + const uint8_t want[] = { 0, 2, 4 }; + uint8_t ids[8]; + + memset(ids, 0xee, sizeof(ids)); + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(4, 8), 1, ids, 3), 3); + ck_assert_int_eq(memcmp(ids, want, sizeof(want)), 0); + ck_assert_uint_eq(ids[3], 0xee); +} +END_TEST + +START_TEST(test_unusable_counts) +{ + uint8_t ids[8]; + + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(0, 4), 1, ids, 8), 0); + ck_assert_int_eq(x86_apic_ids_from_topology(COUNTS(4, 2), 1, ids, 8), 0); + ck_assert_int_eq(x86_apic_ids_from_topology(0, 1, ids, 8), 0); +} +END_TEST + +static Suite *apic_ids_suite(void) +{ + Suite *s = suite_create("x86-apic-ids"); + TCase *tc = tcase_create("x86-apic-ids"); + + tcase_add_test(tc, test_ht_off_four_cores); + tcase_add_test(tc, test_ht_on_primaries_first); + tcase_add_test(tc, test_fewer_cores); + tcase_add_test(tc, test_capacity_is_respected); + tcase_add_test(tc, test_unusable_counts); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = apic_ids_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +}