Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -8457,6 +8457,52 @@ Note:
- This feature requires `NASM` to be installed on the machine building wolfBoot.


### Booting a Linux bzImage payload (with an optional initrd)

Set `LINUX_PAYLOAD=1` to boot a signed Linux `bzImage` from the disk A/B slot
instead of an ELF/Multiboot2 image. Both the 32-bit and the 64-bit (`64BIT=1`)
boot protocols are supported; a 64-bit build requires a kernel with a 64-bit
entry point (`xloadflags` bit 0).

The kernel must be able to reach its root filesystem. A monolithic kernel with
the storage and filesystem drivers built in needs nothing further. To boot a
modular distribution kernel that relies on an initramfs, wrap the kernel and
the initrd into a single signed image using the container header below, then
sign that image as usual (all fields little-endian):

```
magic u32 0x3150584C ("LXP1")
kernel_size u32 exact bzImage byte length
initrd_size u32 exact initrd byte length
reserved u32 0
<bzImage bytes>
<initrd bytes>
```

wolfBoot detects the magic, loads the kernel, places the initrd below the
kernel's `initrd_addr_max` (and below the top of usable RAM), and hands its
address to the kernel. An image without the magic is treated as a bare bzImage
with no initrd, so existing payloads are unaffected.

### Measuring the OS image into a PCR

With `MEASURED_BOOT=1`, set `MEASURED_PCR_OS=<n>` to extend the verified disk OS
image digest into PCR `<n>`, in addition to the firmware measurement in
`MEASURED_PCR_A`. PCR 4 follows the TCG PC Client convention for the boot
payload the boot manager launches. The digest is re-hashed into the PCR bank
algorithm when it is wider than the bank. A failed extend is fail-secure: the
image is not booted.

### Debugging the FSP UPD configuration

`tools/x86_fsp/decode_fsp_upd.py` decodes an FSP UPD block into named fields,
using the offset comments that ship in `FspmUpd.h` / `FspsUpd.h`. It accepts
either the hex block a bootloader prints over the debug UART or a UPD region
lifted from a flash image, and `--diff` reports only the fields that differ
between two captures. This is useful when tuning the memory or silicon
configuration for a new board.


### Running on 64-bit QEMU

Two example configuration files are available: `config/examples/x86_fsp_qemu.config` and `config/examples/x86_fsp_qemu_seal.config`.
Expand Down
3 changes: 3 additions & 0 deletions include/stage2_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct stage2_parameter {
uint32_t hobList;
uint32_t page_table;
uint32_t tolum;
/* Verified payload length, set by the disk loader before do_boot so the
* Linux loader can bound the signed container header against the image. */
uint32_t payload_size;
#ifdef WOLFBOOT_TPM_SEAL
uint32_t tpm_policy;
uint16_t tpm_policy_size;
Expand Down
3 changes: 3 additions & 0 deletions include/x86/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ uint16_t io_read16(uint16_t port);
void io_write32(uint16_t port, uint32_t value);
uint32_t io_read32(uint16_t port);
void reset(uint8_t warm);
#ifdef WOLFBOOT_TGL
int global_reset(void);
#endif
void delay(int msec);
__attribute__((noreturn)) void panic(void);
void cpuid(uint32_t eax_param,
Expand Down
12 changes: 12 additions & 0 deletions include/x86/fsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ int fsp_get_image_revision(struct fsp_info_header *h, int *build,
void print_fsp_image_revision(struct fsp_info_header *h);
void fsp_init_silicon(void);

/* Reset requests (not failures) from FspMemInit and NotifyPhase. */
#define FSP_STATUS_RESET_REQUIRED_COLD 0x40000001
#define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002
/* Codes 3..8 are platform-defined. On Intel client SoCs FSP-S returns _3 to
* request a global reset (host + CSME), needed for the ChipsetInit sync. */
#define FSP_STATUS_RESET_REQUIRED_3 0x40000003
#define FSP_STATUS_RESET_REQUIRED_4 0x40000004
#define FSP_STATUS_RESET_REQUIRED_5 0x40000005
#define FSP_STATUS_RESET_REQUIRED_6 0x40000006
#define FSP_STATUS_RESET_REQUIRED_7 0x40000007
#define FSP_STATUS_RESET_REQUIRED_8 0x40000008

#endif /* FSP_H */
3 changes: 2 additions & 1 deletion include/x86/linux_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ struct boot_params {
uint8_t _pad9[276];
} __attribute__((packed));

void load_linux(uint8_t *linux_image, void *params, const char *cmd_line);
void load_linux(uint8_t *linux_image, uint32_t image_size, void *params,
const char *cmd_line);

#endif /* LINUX_LOADER_H */
3 changes: 3 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ ifeq ($(MEASURED_BOOT),1)
WOLFTPM:=1
CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT"
CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_A=$(MEASURED_PCR_A)"
ifneq ($(MEASURED_PCR_OS),)
CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_OS=$(MEASURED_PCR_OS)"
endif
ifeq ($(MEASURED_BOOT_APP_PARTITION),1)
CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT_APP_PARTITION"
endif
Expand Down
7 changes: 4 additions & 3 deletions src/boot_x86_fsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ const uint8_t __attribute__((section(".sig_wolfboot_raw")))
/* offset of the header from the base image */
#define FSP_INFO_HEADER_OFFSET 0x94
#define EFI_SUCCESS 0x0
#define FSP_STATUS_RESET_REQUIRED_COLD 0x40000001
#define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002
/* FSP_STATUS_RESET_REQUIRED_* are defined in x86/fsp.h */
#define MEMORY_4GB (4ULL * 1024 * 1024 * 1024)
#define ENDLINE "\r\n"
/* Standard PCI capabilities live in conventional config space at 0x40-0xFC,
Expand Down Expand Up @@ -534,7 +533,9 @@ void start(uint32_t stack_base, uint32_t stack_top, uint64_t timestamp,
struct stage2_ptr_holder stage2_holder;
struct stage2_parameter temp_params;
uint8_t *fsp_m_base, done = 0;
struct efi_hob *hobList, *it;
/* FspMemInit writes hobList only on EFI_SUCCESS; init so the reset-required
* and error paths never carry a stale pointer. */
struct efi_hob *hobList = NULL, *it;
memory_init_cb MemoryInit;
uint64_t top_address = MEMORY_4GB;
uint32_t new_stack;
Expand Down
3 changes: 2 additions & 1 deletion src/boot_x86_fsp_payload.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ void do_boot(const uint32_t *app)
stage2_params = stage2_get_parameters();
#if defined(WOLFBOOT_LINUX_PAYLOAD)
mptable_setup();
load_linux((uint8_t *)app, stage2_params, cmdline);
load_linux((uint8_t *)app, stage2_params->payload_size, stage2_params,
cmdline);
#elif defined(WOLFBOOT_ELF)
int r;
uint64_t e;
Expand Down
3 changes: 3 additions & 0 deletions src/tpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ int NOINLINEFUNCTION wolfBoot_constant_compare(const uint8_t* a, const uint8_t*

return (diff != 0U) ? 1 : 0;
}
#endif

#if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) || \
defined(WOLFBOOT_MEASURED_BOOT)
void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz,
unsigned long maxLine)
{
Expand Down
60 changes: 60 additions & 0 deletions src/update_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
#include "spi_flash.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"
#include "tpm.h"
#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS) && \
(WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ)
#include <wolfssl/wolfcrypt/sha256.h>
#endif
#include "disk.h"
#ifdef WOLFBOOT_DISK_FS
#include "disk_fs.h"
Expand Down Expand Up @@ -484,6 +489,22 @@ void RAMFUNCTION wolfBoot_start(void)
uintptr_t bl31_entry = 0;
#endif
char part_name[4] = {'P', ':', 'X', '\0'};
#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS)
int measure_ret;
#if defined(WOLFBOOT_SKIP_BOOT_VERIFY)
#error "measured boot: WOLFBOOT_MEASURED_PCR_OS needs the OS digest, which WOLFBOOT_SKIP_BOOT_VERIFY does not produce"
#endif
#if WOLFBOOT_SHA_DIGEST_SIZE < WOLFBOOT_TPM_PCR_DIG_SZ
#error "measured boot: image digest narrower than the PCR bank is not supported"
#endif
#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ
#if WOLFBOOT_TPM_PCR_DIG_SZ != 32
#error "measured boot: the re-hash path only implements a SHA-256 PCR bank"
#endif
uint8_t os_pcr[WOLFBOOT_TPM_PCR_DIG_SZ];
wc_Sha256 os_sha;
#endif
#endif
BENCHMARK_DECLARE();

#ifdef DISK_ENCRYPT
Expand Down Expand Up @@ -814,6 +835,40 @@ void RAMFUNCTION wolfBoot_start(void)
* disk_close(BOOT_DISK) is deferred to just before hal_prepare_boot(). */
wolfBoot_printf("Firmware Valid.\r\n");

#if defined(WOLFBOOT_MEASURED_BOOT) && defined(WOLFBOOT_MEASURED_PCR_OS)
/* Measure the verified OS image into its own PCR, separate from the
* firmware measurement in WOLFBOOT_MEASURED_PCR_A. PCR4 is the TCG slot for
* the boot payload the boot manager launches. Re-hash to the PCR bank
* algorithm when the image digest is wider, so a SHA-256 bank is not
* extended with a truncated wider digest. */
/* sha_hash is set only by a successful verify; NULL under
* WOLFBOOT_SKIP_BOOT_VERIFY. Fail-secure rather than dereference it. */
if (os_image.sha_hash == NULL) {
wolfBoot_printf("No OS digest available to measure\r\n");
wolfBoot_panic();
}
#if WOLFBOOT_SHA_DIGEST_SIZE > WOLFBOOT_TPM_PCR_DIG_SZ
if (wc_InitSha256(&os_sha) != 0 ||
wc_Sha256Update(&os_sha, os_image.sha_hash,
WOLFBOOT_SHA_DIGEST_SIZE) != 0 ||
wc_Sha256Final(&os_sha, os_pcr) != 0) {
wolfBoot_printf("Failed to re-hash the OS digest\r\n");
wolfBoot_panic();
}
measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS, os_pcr,
__LINE__);
#else
measure_ret = wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_OS,
os_image.sha_hash, __LINE__);
#endif
if (measure_ret != 0) {
/* Fail-secure, as stage1 does for its own measurement: a working
* TPM that cannot record the OS measurement must not boot it. */
wolfBoot_printf("Failed to measure the OS image into its PCR\r\n");
wolfBoot_panic();
}
#endif /* WOLFBOOT_MEASURED_BOOT && WOLFBOOT_MEASURED_PCR_OS */

load_address = (uint32_t*)os_image.fw_base;

#ifdef WOLFBOOT_FDT
Expand Down Expand Up @@ -983,6 +1038,11 @@ void RAMFUNCTION wolfBoot_start(void)
zynqmp_atf_handoff(bl31_entry, (uintptr_t)load_address,
(uintptr_t)dts_addr, ZYNQMP_ATF_EL2);
}
#endif
#ifdef WOLFBOOT_FSP
/* Hand the verified payload length to the Linux loader (via do_boot) so it
* can bound the signed container header against the image. */
stage2_params->payload_size = (uint32_t)os_image.fw_size;
#endif
do_boot((uint32_t*)load_address
#if defined(MMU) || defined(WOLFBOOT_FDT)
Expand Down
27 changes: 27 additions & 0 deletions src/x86/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,33 @@ void reset(uint8_t warm)
while(1){};
}

#ifdef WOLFBOOT_TGL
/* PMC ETR3 (PWRMBASE 0xFE000000 + 0x1048). CF9_GLB_RST makes the next 0xCF9
* full reset a global reset (host + CSME); CF9_LOCK makes ETR3 read-only. */
#define TGL_PCH_PWRM_BASE 0xFE000000u
#define TGL_PWRM_ETR3 0x1048u
#define ETR3_CF9_GLB_RST (1u << 20)
#define ETR3_CF9_LOCK (1u << 31)

/* Global reset (host + CSME), which FSP-S needs after a ChipsetInit sync.
* Returns non-zero if ETR3 is locked and the global bit could not be set;
* otherwise does not return. */
int global_reset(void)
{
volatile uint32_t *etr3 =
(volatile uint32_t *)(uintptr_t)(TGL_PCH_PWRM_BASE + TGL_PWRM_ETR3);
uint32_t v;

v = *etr3;
if ((v & ETR3_CF9_LOCK) != 0) {
return -1;
}
*etr3 = v | ETR3_CF9_GLB_RST;
reset(0);
return 0;
}
#endif /* WOLFBOOT_TGL */

/**
* @brief Delay the execution for a specified number of milliseconds.
*
Expand Down
49 changes: 37 additions & 12 deletions src/x86/fsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <x86/fsp/FspCommon.h>
#include <x86/fsp.h>
#include <stage2_params.h>
#include <x86/common.h>
#include <pci.h>
Expand Down Expand Up @@ -99,6 +100,39 @@ void print_fsp_image_revision(struct fsp_info_header *h)
wolfBoot_printf("%x.%x.%x build %x\r\n", maj, min, rev, build);
}

/* Act on a reset request returned by an FSP NotifyPhase call. WARM/COLD do the
* matching reset; RESET_REQUIRED_3 is the Tiger Lake global reset (host + CSME)
* needed after the FSP-S ChipsetInit sync. A non-success, non-reset status is
* fatal. Returns only on EFI_SUCCESS. */
static void notify_phase_handle_reset(uint32_t status)
{
if (status == FSP_STATUS_RESET_REQUIRED_WARM) {
wolfBoot_printf("notify phase: warm reset required\n");
reset(1);
}
if (status == FSP_STATUS_RESET_REQUIRED_COLD) {
wolfBoot_printf("notify phase: cold reset required\n");
reset(0);
}
if (status == FSP_STATUS_RESET_REQUIRED_3) {
wolfBoot_printf("notify phase: global reset required\n");
#ifdef WOLFBOOT_TGL
/* global_reset() returns only if ETR3 is locked and the global bit
* could not be armed. A plain reset would not satisfy the FSP-S
* request and would loop forever, so halt instead (fail-secure). */
global_reset();
wolfBoot_printf("ETR3 is locked, cannot arm a global reset\n");
panic();
#else
reset(0);
#endif
}
if (status != EFI_SUCCESS) {
wolfBoot_printf("notify phase failed %d\n", status);
panic();
}
}

void fsp_init_silicon(void)
{
uint8_t silicon_init_parameter[FSP_S_PARAM_SIZE];
Expand Down Expand Up @@ -167,20 +201,11 @@ void fsp_init_silicon(void)
notify_phase = _start_fsp_s + notify_phase_off;
param.Phase = EnumInitPhaseAfterPciEnumeration;
status = x86_run_fsp_32bit(notify_phase, &param);
if (status != EFI_SUCCESS) {
wolfBoot_printf("notify phase failed %d\n", status);
panic();
}
notify_phase_handle_reset(status);
param.Phase = EnumInitPhaseReadyToBoot;
status = x86_run_fsp_32bit(notify_phase, &param);
if (status != EFI_SUCCESS) {
wolfBoot_printf("notify phase failed %d\n", status);
panic();
}
notify_phase_handle_reset(status);
param.Phase = EnumInitPhaseEndOfFirmware;
status = x86_run_fsp_32bit(notify_phase, &param);
if (status != EFI_SUCCESS) {
wolfBoot_printf("notify phase failed %d\n", status);
panic();
}
notify_phase_handle_reset(status);
}
Loading
Loading