Michał Żygowski has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/68987 )
Change subject: soc/intel/alderlake/hsphy: Add possibility to cache HSPHY in flash ......................................................................
soc/intel/alderlake/hsphy: Add possibility to cache HSPHY in flash
Tge patch adds a possibility to cache the PCIe 5.0 HSPHY firmware in the SPI flash. New flashmap region is created for that purpose. The goal of caching is to reduce the dependency on CSME and the HECI IP LOAD command which may fail when the CSME is disabled, e.g. soft disabled by HECI command or HAP disabled. This change allows to keep PCIe 5.0 functioning even if CSME/HECI is not functional.
TEST=Boot Ubuntu 22.04 on MSI PRO Z690-A and notice PCIe 5.0 port is functional after loading the HSPHY from cache.
Signed-off-by: Michał Żygowski michal.zygowski@3mdeb.com Change-Id: I5a37f5b06706ff30d92f60f1bf5dc900edbde96f --- M Makefile.inc M src/soc/intel/alderlake/Kconfig M src/soc/intel/alderlake/Makefile.inc M src/soc/intel/alderlake/hsphy.c M util/cbfstool/default-x86.fmd 5 files changed, 217 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/87/68987/1
diff --git a/Makefile.inc b/Makefile.inc index 563a2bc..0c7b82d 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -1003,6 +1003,15 @@ FMAP_VPD_ENTRY := endif
+ifeq ($(CONFIG_INCLUDE_HSPHY_IN_FMAP),y) +FMAP_HSPHY_FW_BASE := $(call int-align, $(FMAP_CURRENT_BASE), 0x1000) +FMAP_HSPHY_FW_SIZE := $(CONFIG_HSPHY_FW_MAX_SIZE) +FMAP_HSPHY_FW_ENTRY := HSPHY_FW@$(FMAP_HSPHY_FW_BASE) $(FMAP_HSPHY_FW_SIZE) +FMAP_CURRENT_BASE := $(call int-add, $(FMAP_HSPHY_FW_BASE) $(FMAP_HSPHY_FW_SIZE)) +else +FMAP_HSPHY_FW_ENTRY := +endif + # # X86 FMAP region # @@ -1081,6 +1090,7 @@ -e "s,##SMMSTORE_ENTRY##,$(FMAP_SMMSTORE_ENTRY)," \ -e "s,##SPD_CACHE_ENTRY##,$(FMAP_SPD_CACHE_ENTRY)," \ -e "s,##VPD_ENTRY##,$(FMAP_VPD_ENTRY)," \ + -e "s,##HSPHY_FW_ENTRY##,$(FMAP_HSPHY_FW_ENTRY)," \ -e "s,##CBFS_BASE##,$(FMAP_CBFS_BASE)," \ -e "s,##CBFS_SIZE##,$(FMAP_CBFS_SIZE)," \ $(DEFAULT_FLASHMAP) > $@.tmp diff --git a/src/soc/intel/alderlake/Kconfig b/src/soc/intel/alderlake/Kconfig index cbc02e7..8354583 100644 --- a/src/soc/intel/alderlake/Kconfig +++ b/src/soc/intel/alderlake/Kconfig @@ -484,4 +484,27 @@ help Set this option if debug build of FSP is used.
+config INCLUDE_HSPHY_IN_FMAP + bool "Include PCIe 5.0 HSPHy firmware in flash" + default n + help + Set this option to avoid communication with ME to fetch the PCIe 5.0 + HSPHY firmware on each boot and load it directly from flashmap + region. May be useful if ME is soft disable or disable with HAP bit. + If possible, the HSPHY FW will be saved to flashmap region if the + firmware file is not provided in the Kconfig. + +config HSPHY_FW_FILE + string "HSPHY firmware file path" + depends on INCLUDE_HSPHY_IN_FMAP + help + Path pointing to the PCIe 5.0 HSPHY file. The file can be extracted + from full firmware image or ME region using UEFITool. If left empty, + HSPHY loading procedure will try to save the firmware to the flashmap + region if fetched successfully from ME. + +config HSPHY_FW_MAX_SIZE + hex + default 0x8000 + endif diff --git a/src/soc/intel/alderlake/Makefile.inc b/src/soc/intel/alderlake/Makefile.inc index e9266c0..f43e59f 100644 --- a/src/soc/intel/alderlake/Makefile.inc +++ b/src/soc/intel/alderlake/Makefile.inc @@ -111,4 +111,28 @@
endif
+ifeq ($(CONFIG_INCLUDE_HSPHY_IN_FMAP),y) +ifneq ($(call strip_quotes,$(CONFIG_HSPHY_FW_FILE)),) + +# Create the target HSPHY file that will be put into flashmap region. +# First goes the HSPHY size, then hash algorithm (3 - SHA384, default for now), +# the hash digest, padding to max digest size (SHA512 - 64 bytes) and at last the +# HSPHY firmware itself +$(obj)/hsphy_fw.bin: $(call strip_quotes,$(top)/$(CONFIG_HSPHY_FW_FILE)) + printf " HSPHY $(obj)/hsphy_fw.bin\n" + $(shell wc -c $< | awk '{print $$1}' | tr -d '\n' | xargs -0 printf '%08X' | \ + tac -rs .. | xxd -r -p > $@) + $(shell printf '%02X' 3 | xxd -r -p >> $@) + $(shell sha384sum $< | awk '{print $$1}' | tac -rs .. | xxd -r -p >> $@) + $(shell dd if=/dev/zero bs=1 count=16 2> /dev/null >> $@) + $(shell cat $< >> $@) + +add_hsphy_firmware: $(obj)/hsphy_fw.bin $(obj)/fmap.fmap $(obj)/coreboot.pre $(CBFSTOOL) + $(CBFSTOOL) $(obj)/coreboot.pre write -u -r HSPHY_FW -f $(obj)/hsphy_fw.bin + +$(call add_intermediate, add_hsphy_firmware) + +endif +endif + endif diff --git a/src/soc/intel/alderlake/hsphy.c b/src/soc/intel/alderlake/hsphy.c index 9d41600..78a0f2b 100644 --- a/src/soc/intel/alderlake/hsphy.c +++ b/src/soc/intel/alderlake/hsphy.c @@ -8,6 +8,7 @@ #include <device/mmio.h> #include <device/pci_def.h> #include <device/pci_ops.h> +#include <fmap.h> #include <intelblocks/cse.h> #include <intelblocks/systemagent.h> #include <security/vboot/misc.h> @@ -28,6 +29,13 @@
#define CPU_PID_PCIE_PHYX16_BROADCAST 0x55
+struct hsphy_cache { + uint32_t hsphy_size; + uint8_t hash_algo; + uint8_t digest[MAX_HASH_SIZE]; + uint8_t hsphy_fw[0]; +} __packed; + struct ip_push_model { uint16_t count; uint16_t address; @@ -155,6 +163,131 @@ } }
+static bool hsphy_cache_valid(struct hsphy_cache *hsphy_fw_cache) +{ + if (!hsphy_fw_cache) + return false; + + if (hsphy_fw_cache->hsphy_size == 0 || + hsphy_fw_cache->hsphy_size > HSPHY_PAYLOAD_SIZE || + hsphy_fw_cache->hash_algo <= HASHALG_SHA1 || + hsphy_fw_cache->hash_algo > HASHALG_SHA512) + return false; + + if (verify_hsphy_hash(hsphy_fw_cache->hsphy_fw, hsphy_fw_cache->hsphy_size, + hsphy_fw_cache->digest, hsphy_fw_cache->hash_algo)) + return false; + + return true; +} + +static bool load_hsphy_from_cache(void) +{ + struct region_device rdev; + struct hsphy_cache *hsphy_fw_cache; + + if (fmap_locate_area_as_rdev("HSPHY_FW", &rdev) < 0) { + printk(BIOS_ERR, "HSPHY: Cannot find HSPHY_FW region\n"); + printk(BIOS_ERR, "HSPHY: Falling back to HECI HSPHY firmware load\n"); + return false; + } + + hsphy_fw_cache = (struct hsphy_cache *)rdev_mmap_full(&rdev); + + if (!hsphy_cache_valid(hsphy_fw_cache)) { + printk(BIOS_ERR, "HSPHY: HSPHY cache invalid\n"); + printk(BIOS_ERR, "HSPHY: Falling back to HECI HSPHY firmware load\n"); + return false; + } + + printk(BIOS_INFO, "Loading HSPHY firmware from cache\n"); + upload_hsphy_to_cpu_pcie(hsphy_fw_cache->hsphy_fw, hsphy_fw_cache->hsphy_size); + + return true; +} + +static void cache_hsphy_fw_in_flash(void *buf, uint32_t buf_size, uint8_t *hash_buf, + uint8_t hash_alg) +{ + struct region_device rdev; + struct hsphy_cache *hsphy_fw_cache; + size_t ret; + + if (!buf || buf_size == 0 || buf_size > (HSPHY_PAYLOAD_SIZE - sizeof(*hsphy_fw_cache)) + || !hash_buf || hash_alg <= HASHALG_SHA1 || hash_alg > HASHALG_SHA512) { + printk(BIOS_ERR, "Invalid parameters, HSPHY will not be cached in flash.\n"); + } + + if (fmap_locate_area_as_rdev_rw("HSPHY_FW", &rdev) < 0) { + printk(BIOS_ERR, "HSPHY: Could not find HSPHY_FW region\n"); + printk(BIOS_ERR, "HSPHY will not be cached in flash\n"); + return; + } + + hsphy_fw_cache = (struct hsphy_cache *)rdev_mmap_full(&rdev); + + if (hsphy_cache_valid(hsphy_fw_cache)) { + printk(BIOS_INFO, "HSPHY: HSPHY cache valid, skipping update\n"); + return; + } + + if (region_device_sz(&rdev) < (buf_size + sizeof(*hsphy_fw_cache))) { + printk(BIOS_ERR, "HSPHY: HSPHY_FW region too small\n"); + printk(BIOS_ERR, "HSPHY will not be cached in flash\n"); + return; + } + + hsphy_fw_cache = malloc(sizeof(*hsphy_fw_cache)); + + if (!hsphy_fw_cache) { + printk(BIOS_ERR, "HSPHY: Could not allocate memory for HSPHY cache buffer\n"); + printk(BIOS_ERR, "HSPHY will not be cached in flash\n"); + return; + } + + hsphy_fw_cache->hsphy_size = buf_size; + hsphy_fw_cache->hash_algo = hash_alg; + + switch (hash_alg) { + case HASHALG_SHA256: + hash_alg = VB2_HASH_SHA256; + break; + case HASHALG_SHA384: + hash_alg = VB2_HASH_SHA384; + break; + case HASHALG_SHA512: + hash_alg = VB2_HASH_SHA512; + break; + } + + memset(hsphy_fw_cache->digest, 0, sizeof(hsphy_fw_cache->digest)); + memcpy(hsphy_fw_cache->digest, hash_buf, vb2_digest_size(hash_alg)); + + if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0) { + printk(BIOS_ERR, "Failed to erase HSPHY cache region\n"); + free(hsphy_fw_cache); + return; + } + + ret = rdev_writeat(&rdev, hsphy_fw_cache, 0, sizeof(*hsphy_fw_cache)); + if (ret != sizeof(*hsphy_fw_cache)) { + printk(BIOS_ERR, "Failed to write HSPHY cache metadata\n"); + free(hsphy_fw_cache); + return; + } + + ret = rdev_writeat(&rdev, buf, sizeof(*hsphy_fw_cache), buf_size); + if (ret != buf_size) { + printk(BIOS_ERR, "Failed to write HSPHY firmware to cache\n"); + free(hsphy_fw_cache); + return; + } + + printk(BIOS_INFO, "HSPHY cached to flash successfully\n"); + + free(hsphy_fw_cache); +} + void load_and_init_hsphy(void) { void *hsphy_buf; @@ -171,6 +304,9 @@ return; }
+ if (CONFIG(INCLUDE_HSPHY_IN_FMAP) && load_hsphy_from_cache()) + return; + /* Align the buffer to page size, otherwise the HECI command will fail */ hsphy_buf = memalign(4 * KiB, HSPHY_PAYLOAD_SIZE);
@@ -215,5 +351,8 @@
upload_hsphy_to_cpu_pcie(hsphy_buf, buf_size);
+ if (CONFIG(INCLUDE_HSPHY_IN_FMAP)) + cache_hsphy_fw_in_flash(hsphy_buf, buf_size, hsphy_hash, hash_type); + free(hsphy_buf); } diff --git a/util/cbfstool/default-x86.fmd b/util/cbfstool/default-x86.fmd index 41be782..f008889 100644 --- a/util/cbfstool/default-x86.fmd +++ b/util/cbfstool/default-x86.fmd @@ -14,6 +14,7 @@ ##SMMSTORE_ENTRY## ##SPD_CACHE_ENTRY## ##VPD_ENTRY## + ##HSPHY_FW_ENTRY## FMAP@##FMAP_BASE## ##FMAP_SIZE## COREBOOT(CBFS)@##CBFS_BASE## ##CBFS_SIZE## }