From 5856f4c2f6cb2e187c7ca2a65498d8d06d9245af Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 27 Oct 2026 17:36:55 -0700 Subject: [PATCH 1/3] nai_68int6: add the I2C0/GSPI0 board devices and pick the Linux or Multiboot2 payload per image --- docs/Targets.md | 23 +++++++- include/x86/acpi_dsdt.h | 73 +++++++++++++++++++++++-- include/x86/gpio_config.h | 109 +++++++++++++++++++++++++++++++++++++ src/boot_x86_fsp_payload.c | 58 +++++++++++++------- src/x86/acpi_dsdt.asl | 92 ++++++++++++++++++++++++++++++- src/x86/tgl_fsp.c | 2 + 6 files changed, 331 insertions(+), 26 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index 53026cd31c..753c7398ed 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -9096,7 +9096,7 @@ 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 and a minimal DSDT); 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) | | `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` | @@ -9117,7 +9117,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. 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 | + +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/include/x86/acpi_dsdt.h b/include/x86/acpi_dsdt.h index 71b4ebbc20..af6aa92c94 100644 --- a/include/x86/acpi_dsdt.h +++ b/include/x86/acpi_dsdt.h @@ -22,10 +22,10 @@ * 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, 0x29, 0x05, 0x00, 0x00, 0x02, 0xa7, 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, 0x44, 0x50, 0x5f, 0x53, 0x42, 0x5f, 0x5b, 0x82, 0x4c, 0x4f, 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, @@ -67,6 +67,71 @@ static const unsigned char acpi_dsdt_aml[] = { 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 + 0x79, 0x00, 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 }; -static const unsigned int acpi_dsdt_aml_len = 542; +static const unsigned int acpi_dsdt_aml_len = 1321; diff --git a/include/x86/gpio_config.h b/include/x86/gpio_config.h index 73bcd468fe..9b60d90c2b 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) @@ -688,6 +694,109 @@ 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}, }; #else /* TARGET_kontron_vx3060_s2 */ diff --git a/src/boot_x86_fsp_payload.c b/src/boot_x86_fsp_payload.c index c976687fc3..ecdbe6c418 100644 --- a/src/boot_x86_fsp_payload.c +++ b/src/boot_x86_fsp_payload.c @@ -94,29 +94,16 @@ void jump(uintptr_t entry) : "g"(entry)); } +#if defined(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. + * @brief Load an ELF image and boot it, through Multiboot2 when enabled. * - * @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; uint8_t *mb2_header; @@ -163,7 +150,40 @@ 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, + cmdline); +#elif defined(WOLFBOOT_ELF) + boot_elf(app, stage2_params); #else #error "No payload compiled in" #endif /* WOLFBOOT_LINUX_PAYLOAD */ diff --git a/src/x86/acpi_dsdt.asl b/src/x86/acpi_dsdt.asl index 0a9b66f121..6242953d9f 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 @@ -84,6 +85,95 @@ DefinitionBlock ("", "DSDT", 2, "WOLFBT", "NAITGL", 0x00000001) 0x0000000000000000, 0x0000004000000000, 0x0000007FFFFFFFFF, 0x0000000000000000, 0x0000004000000000) }) + + /* 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") + }) + } + } } } } diff --git a/src/x86/tgl_fsp.c b/src/x86/tgl_fsp.c index 03066e6f82..c9092ba221 100644 --- a/src/x86/tgl_fsp.c +++ b/src/x86/tgl_fsp.c @@ -1035,6 +1035,8 @@ int fsp_machine_update_s_parameters(uint8_t *default_s_params) 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; } From 535b942af8ce13072adad408f9d2734390b6fd7d Mon Sep 17 00:00:00 2001 From: David Garske Date: Sat, 31 Oct 2026 10:38:32 -0700 Subject: [PATCH 2/3] x86 fsp: cache the FSP-M memory training data in SPI flash and stop the NAI 68INT6 per-boot global reset --- .gitignore | 4 + arch.mk | 6 + config/examples/nai_68int6.config | 6 + docs/Targets.md | 1 + hal/x86_fsp_tgl.c | 151 ++++++++++++++ include/stage2_params.h | 3 + include/x86/fsp_nvs.h | 47 +++++ include/x86/tgl_fsp.h | 2 + options.mk | 11 + src/boot_x86_fsp.c | 7 + src/x86/fsp.c | 6 + src/x86/fsp_nvs.c | 178 ++++++++++++++++ src/x86/tgl_fsp.c | 6 + tools/unit-tests/Makefile | 29 +++ tools/unit-tests/unit-fsp-nvs-save.c | 252 ++++++++++++++++++++++ tools/unit-tests/unit-fsp-nvs.c | 213 +++++++++++++++++++ tools/unit-tests/unit-tgl-spi-hwseq.c | 289 ++++++++++++++++++++++++++ 17 files changed, 1211 insertions(+) create mode 100644 include/x86/fsp_nvs.h create mode 100644 src/x86/fsp_nvs.c create mode 100644 tools/unit-tests/unit-fsp-nvs-save.c create mode 100644 tools/unit-tests/unit-fsp-nvs.c create mode 100644 tools/unit-tests/unit-tgl-spi-hwseq.c diff --git a/.gitignore b/.gitignore index d33c896740..45bce6158f 100644 --- a/.gitignore +++ b/.gitignore @@ -625,6 +625,9 @@ 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-nrf5340-flash-protect tools/unit-tests/unit-nrf5340-uart-crlf tools/unit-tests/unit-nrf54l-uart-write @@ -670,6 +673,7 @@ 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/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..457c59f723 100644 --- a/config/examples/nai_68int6.config +++ b/config/examples/nai_68int6.config @@ -41,6 +41,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 diff --git a/docs/Targets.md b/docs/Targets.md index 753c7398ed..bb9e433dea 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -9097,6 +9097,7 @@ Notable options in [config/examples/nai_68int6.config](../config/examples/nai_68 |---|---| | `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. 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 | | `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` | 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..cb172bcc0c 100644 --- a/include/stage2_params.h +++ b/include/stage2_params.h @@ -31,6 +31,9 @@ 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; +#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/fsp_nvs.h b/include/x86/fsp_nvs.h new file mode 100644 index 0000000000..e2ed37152f --- /dev/null +++ b/include/x86/fsp_nvs.h @@ -0,0 +1,47 @@ +/* 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); +void fsp_nvs_apply(uint8_t *udp_m, uint32_t udp_size, const uint8_t *fsp_m, + uint32_t fsp_m_size); +void fsp_nvs_save(void); + +#endif /* FSP_NVS_H */ 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..ec1a72e499 100644 --- a/options.mk +++ b/options.mk @@ -1937,6 +1937,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..9e1e4dc81a 100644 --- a/src/boot_x86_fsp.c +++ b/src/boot_x86_fsp.c @@ -42,6 +42,9 @@ #include #include #endif +#ifdef WOLFBOOT_FSP_NVS_CACHE +#include +#endif #define WOLFBOOT_X86_STACK_SIZE 0x10000 @@ -586,6 +589,10 @@ 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) + 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); diff --git a/src/x86/fsp.c b/src/x86/fsp.c index 0ed59ec439..4158f32c65 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 @@ -162,6 +165,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; diff --git a/src/x86/fsp_nvs.c b/src/x86/fsp_nvs.c new file mode 100644 index 0000000000..b29e00221b --- /dev/null +++ b/src/x86/fsp_nvs.c @@ -0,0 +1,178 @@ +/* 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 + +#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 + +#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 +void 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 (!fsp_nvs_record_valid(rec, key, FSP_NVS_SIZE)) { + wolfBoot_printf("FSP NVS: no saved training data\r\n"); + return; + } + 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); +} +#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/tgl_fsp.c b/src/x86/tgl_fsp.c index c9092ba221..29cb99f000 100644 --- a/src/x86/tgl_fsp.c +++ b/src/x86/tgl_fsp.c @@ -677,7 +677,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; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 12a69a11e7..d4a6dffce5 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -156,6 +156,9 @@ 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-nrf5340-flash-protect TESTS+=unit-keygen-keystore TESTS+=unit-samr21-erase-advance @@ -2125,6 +2128,32 @@ 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-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-fsp-nvs-save.c b/tools/unit-tests/unit-fsp-nvs-save.c new file mode 100644 index 0000000000..91e7d7c6b6 --- /dev/null +++ b/tools/unit-tests/unit-fsp-nvs-save.c @@ -0,0 +1,252 @@ +/* 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" +#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..bc15c55e2f --- /dev/null +++ b/tools/unit-tests/unit-fsp-nvs.c @@ -0,0 +1,213 @@ +/* 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; +} + +#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)); +} + +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)); + 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); + 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); + fsp_nvs_apply(g_udp, UDP_SIZE, g_fspm, FSPM_SIZE); + 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_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); + 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; +} From a0008f6a12b749907a8d4260fd151c2949e26fb9 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 2 Nov 2026 12:18:20 -0800 Subject: [PATCH 3/3] nai_68int6: drive GPIO output levels and add the stock BIOS PCIe switch strap and PCIE_RDY sequence --- config/examples/nai_68int6.config | 9 + docs/Targets.md | 3 + include/x86/fsp_config.h | 75 +++++++ include/x86/gpio_config.h | 344 ++++++++++++++++++++++++++++-- options.mk | 11 + src/x86/fsp.c | 4 + src/x86/qemu_fsp.c | 5 + src/x86/tgl_fsp.c | 116 +++++++++- 8 files changed, 541 insertions(+), 26 deletions(-) diff --git a/config/examples/nai_68int6.config b/config/examples/nai_68int6.config index 457c59f723..b4fc1e4d34 100644 --- a/config/examples/nai_68int6.config +++ b/config/examples/nai_68int6.config @@ -74,6 +74,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 bb9e433dea..d8942e0584 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -9098,6 +9098,9 @@ Notable options in [config/examples/nai_68int6.config](../config/examples/nai_68 | `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. 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 | +| `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` | diff --git a/include/x86/fsp_config.h b/include/x86/fsp_config.h index 3689c638fd..8f28e72942 100644 --- a/include/x86/fsp_config.h +++ b/include/x86/fsp_config.h @@ -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/gpio_config.h b/include/x86/gpio_config.h index 9b60d90c2b..aaf2f5a861 100644 --- a/include/x86/gpio_config.h +++ b/include/x86/gpio_config.h @@ -71,13 +71,29 @@ #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_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 @@ -127,8 +143,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. */ @@ -261,19 +511,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 | @@ -312,13 +563,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 = @@ -363,13 +615,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 = @@ -388,11 +641,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 | @@ -405,11 +658,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 | @@ -575,7 +828,7 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .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, @@ -584,13 +837,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 = @@ -797,6 +1051,60 @@ static const struct tgl_gpio_conf gpio_table_presilicon[] = { .gpio_rxinv = 0x00, .gpio_rxevconf = 0x02, .gpio_reset = GPIO_RESET_HOSTDEEPRESET}, + /* 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 */ @@ -926,7 +1234,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), @@ -1006,7 +1314,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), @@ -1017,7 +1325,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/options.mk b/options.mk index ec1a72e499..7baee86e56 100644 --- a/options.mk +++ b/options.mk @@ -63,6 +63,17 @@ 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,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 # TPM NV version floor and boot audit counters (see docs/Targets.md). diff --git a/src/x86/fsp.c b/src/x86/fsp.c index 4158f32c65..35540b8ca7 100644 --- a/src/x86/fsp.c +++ b/src/x86/fsp.c @@ -44,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. * @@ -195,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/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 29cb99f000..75b5469feb 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) @@ -1097,7 +1099,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 { @@ -1111,6 +1114,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) @@ -1237,7 +1241,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), @@ -1321,7 +1325,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), @@ -1332,7 +1336,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), @@ -1555,7 +1559,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); @@ -1585,6 +1589,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); @@ -1596,6 +1605,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. @@ -1789,6 +1831,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; } @@ -1804,4 +1853,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 */