Skip to content

Commit 540fdae

Browse files
committed
x86 fsp: cache the FSP-M memory training data in SPI flash and stop the NAI 68INT6 per-boot global reset
1 parent 8ca589d commit 540fdae

17 files changed

Lines changed: 1211 additions & 0 deletions

File tree

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,9 @@ tools/unit-tests/unit-hifive1-flash-write
625625
tools/unit-tests/unit-hifive1-hfrosccfg
626626
tools/unit-tests/unit-keygen-keystore
627627
tools/unit-tests/unit-kontron-tgl-spi
628+
tools/unit-tests/unit-fsp-nvs
629+
tools/unit-tests/unit-fsp-nvs-save
630+
tools/unit-tests/unit-tgl-spi-hwseq
628631
tools/unit-tests/unit-nrf5340-flash-protect
629632
tools/unit-tests/unit-nrf5340-uart-crlf
630633
tools/unit-tests/unit-nrf54l-uart-write
@@ -670,6 +673,7 @@ tools/unit-tests/keystore_gen
670673
tools/unit-tests/kinetis_erase_extract.h
671674
tools/unit-tests/kontron_spi_extract.h
672675
tools/unit-tests/kontron_spi_fn_extract.h
676+
tools/unit-tests/tgl_spi_hwseq_extract.h
673677
tools/unit-tests/nrf5340_protect_fn_extract.h
674678
tools/unit-tests/nrf54l_uart_write_extract.h
675679
tools/unit-tests/p1021_erase_extract.h

‎arch.mk‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2500,6 +2500,9 @@ ifeq ("${FSP}", "1")
25002500
OBJS+=src/x86/tgl_fsp.o
25012501
OBJS+=src/ucode0.o
25022502
CFLAGS += -DUCODE0_ADDRESS=$(UCODE0_BASE)
2503+
ifeq ($(FSP_NVS_CACHE),1)
2504+
OBJS+=src/x86/fsp_nvs.o
2505+
endif
25032506
endif
25042507
else
25052508
# building wolfBoot
@@ -2546,6 +2549,9 @@ ifeq ("${FSP}", "1")
25462549
endif
25472550
ifeq ($(FSP_TGL), 1)
25482551
OBJS+=src/x86/tgl_fsp.o
2552+
ifeq ($(FSP_NVS_CACHE),1)
2553+
OBJS+=src/x86/fsp_nvs.o
2554+
endif
25492555
endif
25502556
endif
25512557
ifeq ($(64BIT),1)

‎config/examples/nai_68int6.config‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ BIOS_REGION_SIZE=0xc00000
4141
UCODE0_BASE=0xffd90000
4242
UCODE0_BIN=src/x86/ucode0.bin
4343

44+
# FSP memory training data cache: 128 KB of the BIOS region just below the
45+
# wolfBoot window, so it survives a reflash of the window.
46+
FSP_NVS_CACHE=1
47+
FSP_NVS_BASE=0xff9e0000
48+
FSP_NVS_SIZE=0x20000
49+
4450
FSP_T_BIN=./src/x86/fsp_t.bin
4551
FSP_M_BIN=./src/x86/fsp_m.bin
4652
FSP_S_BIN=./src/x86/fsp_s.bin

‎docs/Targets.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9097,6 +9097,7 @@ Notable options in [config/examples/nai_68int6.config](../config/examples/nai_68
90979097
|---|---|
90989098
| `X86_UART_NUMBER=2` | LPSS UART2 for all three FSP phases (Kontron uses 0) |
90999099
| `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) |
9100+
| `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 |
91009101
| `ANTI_ROLLBACK=1` | Persistent version floor and boot audit counters in TPM NV |
91019102
| `MEASURED_BOOT=1`, `MEASURED_PCR_OS=4` | Firmware into PCR 0, the verified OS image into PCR 4 |
91029103
| `WOLFBOOT_BOOT_DISK=0` | Boot from GPT partitions; the A/B indices (sda3/sda4) are set in `src/update_disk.c` |

‎hal/x86_fsp_tgl.c‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "wolfboot/wolfboot.h"
2323
#include <stdint.h>
24+
#include <string.h>
2425

2526
#ifdef __WOLFBOOT
2627

@@ -190,6 +191,156 @@ int hal_flash_protect(haladdr_t address, int len)
190191
* hook's address/len are the same range FREG1 describes. */
191192
return tgl_lock_bios_region();
192193
}
194+
195+
#ifdef WOLFBOOT_FSP_NVS_CACHE
196+
/* Hardware-sequencing flash cycles, used to save the FSP memory training
197+
* data before the BIOS region is locked. Runs from RAM (stage 2) only. */
198+
#define SPI_BIOS_CNTL (0xdc)
199+
#define SPI_BIOS_CNTL_WPD (1U << 0)
200+
#define SPI_FADDR (0x8)
201+
#define SPI_FDATA0 (0x10)
202+
#define SPI_HSFSTS_FDONE (1U << 0)
203+
#define SPI_HSFSTS_FCERR (1U << 1)
204+
#define SPI_HSFSTS_AEL (1U << 2)
205+
#define SPI_HSFSTS_SCIP (1U << 5)
206+
#define SPI_HSFSTS_FGO (1U << 16)
207+
#define SPI_HSFSTS_FCYCLE_SHIFT (17)
208+
#define SPI_HSFSTS_FDBC_SHIFT (24)
209+
#define SPI_FCYCLE_WRITE (2)
210+
#define SPI_FCYCLE_ERASE_4K (3)
211+
#define SPI_HWSEQ_MAX_DATA (64)
212+
#define SPI_ERASE_SIZE (0x1000)
213+
#define SPI_HWSEQ_TIMEOUT_MS (1000)
214+
215+
static uint32_t tgl_spi_begin(uint32_t *cmd)
216+
{
217+
uint32_t spi_bar, cntl;
218+
219+
spi_bar = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_BAR0_OFFSET);
220+
spi_bar &= PCI_BAR_MASK;
221+
*cmd = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET);
222+
pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET,
223+
*cmd | PCI_COMMAND_MEM_SPACE);
224+
cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL);
225+
pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL,
226+
cntl | SPI_BIOS_CNTL_WPD);
227+
if ((pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL) &
228+
SPI_BIOS_CNTL_WPD) == 0)
229+
return 0;
230+
return spi_bar;
231+
}
232+
233+
static void tgl_spi_end(uint32_t cmd)
234+
{
235+
uint8_t cntl;
236+
237+
cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL);
238+
pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL,
239+
cntl & ~SPI_BIOS_CNTL_WPD);
240+
pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET, cmd);
241+
}
242+
243+
/* The BIOS region is mapped so that its last byte sits at 4 GB - 1;
244+
* returns the flash linear address of the memory-mapped address, or -1
245+
* when the range is outside the BIOS region. */
246+
static int64_t tgl_spi_linear(uint32_t spi_bar, uint32_t address,
247+
uint32_t len)
248+
{
249+
uint32_t reg, base, top;
250+
uint64_t lin;
251+
252+
reg = mmio_read32(spi_bar + SPI_FREG1);
253+
base = (reg & SPI_FREG_BASE_MASK) << SPI_FREG_ADDR_SHIFT;
254+
top = (((reg & SPI_FREG_LIMIT_MASK) >> SPI_FREG_LIMIT_SHIFT)
255+
<< SPI_FREG_ADDR_SHIFT) | 0xfffU;
256+
if (top < base || len == 0)
257+
return -1;
258+
lin = (uint64_t)top - (0xffffffffULL - address);
259+
if (lin < base || lin + len - 1 > top)
260+
return -1;
261+
return (int64_t)lin;
262+
}
263+
264+
static int tgl_spi_cycle(uint32_t spi_bar, uint32_t cycle, uint32_t faddr,
265+
uint32_t len)
266+
{
267+
uint32_t sts = 0;
268+
int ms;
269+
270+
for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) {
271+
if ((mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL) &
272+
SPI_HSFSTS_SCIP) == 0)
273+
break;
274+
delay(1);
275+
}
276+
if (ms == SPI_HWSEQ_TIMEOUT_MS)
277+
return -1;
278+
mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL,
279+
SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL);
280+
mmio_write32(spi_bar + SPI_FADDR, faddr);
281+
mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL,
282+
((len - 1) << SPI_HSFSTS_FDBC_SHIFT) |
283+
(cycle << SPI_HSFSTS_FCYCLE_SHIFT) | SPI_HSFSTS_FGO);
284+
for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) {
285+
sts = mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL);
286+
if (sts & (SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR))
287+
break;
288+
delay(1);
289+
}
290+
mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL,
291+
SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL);
292+
if (ms == SPI_HWSEQ_TIMEOUT_MS || (sts & SPI_HSFSTS_FCERR))
293+
return -1;
294+
return 0;
295+
}
296+
297+
int tgl_spi_flash_erase(uint32_t address, uint32_t len)
298+
{
299+
uint32_t spi_bar, cmd, off;
300+
int64_t lin;
301+
int ret = 0;
302+
303+
if ((address | len) & (SPI_ERASE_SIZE - 1))
304+
return -1;
305+
spi_bar = tgl_spi_begin(&cmd);
306+
lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1;
307+
if (lin < 0)
308+
ret = -1;
309+
for (off = 0; ret == 0 && off < len; off += SPI_ERASE_SIZE)
310+
ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_ERASE_4K,
311+
(uint32_t)lin + off, 1);
312+
tgl_spi_end(cmd);
313+
return ret;
314+
}
315+
316+
int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len)
317+
{
318+
uint32_t spi_bar, cmd, off, chunk, word, i;
319+
int64_t lin;
320+
int ret = 0;
321+
322+
spi_bar = tgl_spi_begin(&cmd);
323+
lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1;
324+
if (lin < 0)
325+
ret = -1;
326+
for (off = 0; ret == 0 && off < len; off += chunk) {
327+
/* 64-byte aligned cycles never cross a flash page */
328+
chunk = SPI_HWSEQ_MAX_DATA - (((uint32_t)lin + off) &
329+
(SPI_HWSEQ_MAX_DATA - 1));
330+
if (chunk > len - off)
331+
chunk = len - off;
332+
for (i = 0; i < chunk; i += 4) {
333+
word = 0xffffffffU;
334+
memcpy(&word, buf + off + i, (chunk - i < 4) ? chunk - i : 4);
335+
mmio_write32(spi_bar + SPI_FDATA0 + i, word);
336+
}
337+
ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_WRITE, (uint32_t)lin + off,
338+
chunk);
339+
}
340+
tgl_spi_end(cmd);
341+
return ret;
342+
}
343+
#endif /* WOLFBOOT_FSP_NVS_CACHE */
193344
#endif /* WOLFBOOT_TGL */
194345
#endif
195346

‎include/stage2_params.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ struct stage2_parameter {
3131
/* Verified payload length, set by the disk loader before do_boot so the
3232
* Linux loader can bound the signed container header against the image. */
3333
uint32_t payload_size;
34+
#ifdef WOLFBOOT_FSP_NVS_CACHE
35+
uint32_t fsp_nvs_key;
36+
#endif
3437
#ifdef WOLFBOOT_TPM_SEAL
3538
uint32_t tpm_policy;
3639
uint16_t tpm_policy_size;

‎include/x86/fsp_nvs.h‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* fsp_nvs.h
2+
*
3+
* Copyright (C) 2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfBoot.
6+
*
7+
* wolfBoot is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfBoot is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*/
21+
#ifndef FSP_NVS_H
22+
#define FSP_NVS_H
23+
24+
#include <stdint.h>
25+
26+
/* Flash record holding the FSP-M non-volatile storage (memory training)
27+
* data. The key covers the FSP-M image and its UPDs, so a change to either
28+
* invalidates the record. */
29+
#define FSP_NVS_MAGIC 0x5356464BU /* "KFVS" */
30+
31+
struct fsp_nvs_record {
32+
uint32_t magic;
33+
uint32_t key;
34+
uint32_t len;
35+
uint32_t crc;
36+
uint32_t reserved[4];
37+
uint8_t data[];
38+
} __attribute__((packed));
39+
40+
uint32_t fsp_nvs_crc32(uint32_t crc, const uint8_t *buf, uint32_t len);
41+
int fsp_nvs_record_valid(const struct fsp_nvs_record *rec, uint32_t key,
42+
uint32_t area_size);
43+
void fsp_nvs_apply(uint8_t *udp_m, uint32_t udp_size, const uint8_t *fsp_m,
44+
uint32_t fsp_m_size);
45+
void fsp_nvs_save(void);
46+
47+
#endif /* FSP_NVS_H */

‎include/x86/tgl_fsp.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@
2525

2626
int x86_fsp_tgl_init_sata(uint32_t *bar);
2727
int tgl_lock_bios_region(void);
28+
int tgl_spi_flash_erase(uint32_t address, uint32_t len);
29+
int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len);
2830

2931
#endif /* TGL_FSP_H */

‎options.mk‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,17 @@ ifeq ($(FSP), 1)
19371937

19381938
# set CFLAGS defines for each x86_fsp option
19391939
$(foreach option,$(X86_FSP_OPTIONS),$(if $($(option)), $(eval CFLAGS += -D$(option)=$($(option)))))
1940+
1941+
ifeq ($(FSP_NVS_CACHE),1)
1942+
ifeq ($(and $(FSP_NVS_BASE),$(FSP_NVS_SIZE)),)
1943+
$(error FSP_NVS_CACHE=1 needs FSP_NVS_BASE and FSP_NVS_SIZE)
1944+
endif
1945+
ifeq ($(filter $(TARGET),kontron_vx3060_s2 nai_68int6),)
1946+
$(error FSP_NVS_CACHE=1 is only supported on the Tiger Lake targets)
1947+
endif
1948+
CFLAGS+=-DWOLFBOOT_FSP_NVS_CACHE
1949+
CFLAGS+=-DFSP_NVS_BASE=$(FSP_NVS_BASE) -DFSP_NVS_SIZE=$(FSP_NVS_SIZE)
1950+
endif
19401951
endif
19411952

19421953
ifeq ($(FLASH_MULTI_SECTOR_ERASE),1)

‎src/boot_x86_fsp.c‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
#include <loader.h>
4343
#include <tpm.h>
4444
#endif
45+
#ifdef WOLFBOOT_FSP_NVS_CACHE
46+
#include <x86/fsp_nvs.h>
47+
#endif
4548

4649

4750
#define WOLFBOOT_X86_STACK_SIZE 0x10000
@@ -586,6 +589,10 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp,
586589
if (status != 0) {
587590
panic();
588591
}
592+
#if defined(WOLFBOOT_FSP_NVS_CACHE) && defined(BUILD_LOADER_STAGE1)
593+
fsp_nvs_apply(udp_m_parameter, fsp_m_info_header->CfgRegionSize,
594+
fsp_m_base, fsp_m_info_header->ImageSize);
595+
#endif
589596

590597
#if defined(WOLFBOOT_DUMP_FSP_UPD)
591598
wolfBoot_printf("Dumping fspm udp (%d bytes)" ENDLINE, (int)fsp_m_info_header->CfgRegionSize);

0 commit comments

Comments
 (0)