diff options
-rw-r--r-- | Makefile.inc | 10 | ||||
-rw-r--r-- | src/soc/intel/alderlake/Kconfig | 26 | ||||
-rw-r--r-- | src/soc/intel/alderlake/Makefile.inc | 24 | ||||
-rw-r--r-- | src/soc/intel/alderlake/hsphy.c | 284 | ||||
-rw-r--r-- | util/cbfstool/default-x86.fmd | 1 |
5 files changed, 308 insertions, 37 deletions
diff --git a/Makefile.inc b/Makefile.inc index b35ec86a11..ab57abd4ce 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -1029,6 +1029,15 @@ else 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 # @@ -1107,6 +1116,7 @@ $(obj)/fmap.fmd: $(top)/Makefile.inc $(DEFAULT_FLASHMAP) $(obj)/config.h -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 0e5a671be2..664876a003 100644 --- a/src/soc/intel/alderlake/Kconfig +++ b/src/soc/intel/alderlake/Kconfig @@ -527,4 +527,30 @@ config FSP_PUBLISH_MBP_HOB later platforms so creation of MBP HOB can be skipped for ADL-N and RPL based platforms. +config INCLUDE_HSPHY_IN_FMAP + bool "Include PCIe 5.0 HSPHY firmware in flash" + default n + help + Set this option to cache the PCIe 5.0 HSPHY firmware after it is + fetched from ME during boot. By default coreboot will fetch the + HSPHY FW from ME, but if for some reason ME is not enabled or + visible, the cached blob will be attempted to initialize the PCIe + 5.0 root port. Select it if ME is soft disabled or disabled with HAP + bit. If possible, the HSPHY FW will be saved to flashmap region if + the firmware file is not provided directly in the HSPHY_FW_FILE + 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 dbee074e1e..886d532e60 100644 --- a/src/soc/intel/alderlake/Makefile.inc +++ b/src/soc/intel/alderlake/Makefile.inc @@ -114,4 +114,28 @@ $(eval $(call cse_add_input,bp2,IUNP)) 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 dacb64399d..5333a8bf2f 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 <intelblocks/vtd.h> @@ -29,6 +30,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; @@ -105,7 +113,7 @@ static int heci_get_hsphy_payload(void *buf, uint32_t *buf_size, uint8_t *hash_b return 0; } -static int verify_hsphy_hash(void *buf, uint32_t buf_size, uint8_t *hash_buf, uint8_t hash_alg) +static bool verify_hsphy_hash(void *buf, uint32_t buf_size, uint8_t *hash_buf, uint8_t hash_alg) { struct vb2_hash hash; @@ -125,14 +133,13 @@ static int verify_hsphy_hash(void *buf, uint32_t buf_size, uint8_t *hash_buf, ui hash.algo = VB2_HASH_SHA384; break; } + memcpy(hash.raw, hash_buf, vb2_digest_size(hash.algo)); - if (vb2_hash_verify(vboot_hwcrypto_allowed(), buf, buf_size, &hash) != VB2_SUCCESS) { - printk(BIOS_ERR, "HSPHY SHA hashes do not match\n"); - return -1; - } + if (vb2_hash_verify(vboot_hwcrypto_allowed(), buf, buf_size, &hash) != VB2_SUCCESS) + return false; - return 0; + return true; } static void upload_hsphy_to_cpu_pcie(void *buf, uint32_t buf_size) @@ -156,31 +163,168 @@ static void upload_hsphy_to_cpu_pcie(void *buf, uint32_t buf_size) } } -void load_and_init_hsphy(void) +static bool hsphy_cache_valid(struct hsphy_cache *hsphy_fw_cache) { - void *hsphy_buf; - uint8_t hsphy_hash[MAX_HASH_SIZE] = { 0 }; - uint8_t hash_type; - uint32_t buf_size = HSPHY_PAYLOAD_SIZE; - size_t dma_buf_size; - pci_devfn_t dev = PCH_DEV_CSE; - const uint16_t pci_cmd_bme_mem = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; - uint32_t status; + if (!hsphy_fw_cache) { + printk(BIOS_WARNING, "Failed to mmap HSPHY cache\n"); + return false; + } - if (!is_devfn_enabled(SA_DEVFN_CPU_PCIE1_0) && - !is_devfn_enabled(SA_DEVFN_CPU_PCIE1_1)) { - printk(BIOS_DEBUG, "All HSPHY ports disabled, skipping HSPHY loading\n"); + 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"); + 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"); + if (hsphy_fw_cache) + rdev_munmap(&rdev, hsphy_fw_cache); + return false; + } + + printk(BIOS_INFO, "Loading HSPHY FW from cache\n"); + upload_hsphy_to_cpu_pcie(hsphy_fw_cache->hsphy_fw, hsphy_fw_cache->hsphy_size); + + rdev_munmap(&rdev, hsphy_fw_cache); + + 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"); return; } + /* Locate the area as RO rdev, otherwise mmap will fail */ + if (fmap_locate_area_as_rdev("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)) { + /* If the cache is valid, check the buffer against the cache hash */ + if (verify_hsphy_hash(buf, buf_size, hsphy_fw_cache->digest, + hsphy_fw_cache->hash_algo)) { + printk(BIOS_INFO, "HSPHY: cache does not need update\n"); + rdev_munmap(&rdev, hsphy_fw_cache); + return; + } else { + printk(BIOS_INFO, "HSPHY: cache needs update\n"); + } + } else { + printk(BIOS_INFO, "HSPHY: cache invalid, updating\n"); + } + + if (region_device_sz(&rdev) < (buf_size + sizeof(*hsphy_fw_cache))) { + printk(BIOS_ERR, "HSPHY: HSPHY_FW region too small: %zx < %zx\n", + region_device_sz(&rdev), buf_size + sizeof(*hsphy_fw_cache)); + printk(BIOS_ERR, "HSPHY will not be cached in flash\n"); + rdev_munmap(&rdev, hsphy_fw_cache); + return; + } + + rdev_munmap(&rdev, hsphy_fw_cache); + 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)); + + /* Now that we want to write to flash, locate the area as RW rdev */ + 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"); + free(hsphy_fw_cache); + return; + } + + 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 FW to cache\n"); + free(hsphy_fw_cache); + return; + } + + printk(BIOS_INFO, "HSPHY cached to flash successfully\n"); + + free(hsphy_fw_cache); +} + +static void *allocate_hsphy_buf(void) +{ + void *hsphy_buf; + size_t dma_buf_size; + if (CONFIG(ENABLE_EARLY_DMA_PROTECTION)) { hsphy_buf = vtd_get_dma_buffer(&dma_buf_size); if (!hsphy_buf || dma_buf_size < HSPHY_PAYLOAD_SIZE) { printk(BIOS_ERR, "DMA protection enabled but DMA buffer does not" " exist or is too small\n"); - printk(BIOS_ERR, "Aborting HSPHY firmware loading, " - "PCIe Gen5 won't work.\n"); - return; + return NULL; } /* Rather impossible scenario, but check alignment anyways */ @@ -192,20 +336,73 @@ void load_and_init_hsphy(void) hsphy_buf = memalign(4 * KiB, HSPHY_PAYLOAD_SIZE); if (!hsphy_buf) { - printk(BIOS_ERR, "Could not allocate memory for HSPHY blob\n"); - printk(BIOS_ERR, "Aborting HSPHY firmware loading, " - "PCIe Gen5 won't work.\n"); - return; + printk(BIOS_ERR, "Failed to allocate memory for HSPHY blob\n"); + return NULL; + } + } + + return hsphy_buf; +} + +void load_and_init_hsphy(void) +{ + void *hsphy_buf; + uint8_t hsphy_hash[MAX_HASH_SIZE] = { 0 }; + uint8_t hash_type; + uint32_t buf_size = HSPHY_PAYLOAD_SIZE; + pci_devfn_t dev = PCH_DEV_CSE; + const uint16_t pci_cmd_bme_mem = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + uint32_t status; + + if (!is_devfn_enabled(SA_DEVFN_CPU_PCIE1_0) && + !is_devfn_enabled(SA_DEVFN_CPU_PCIE1_1)) { + printk(BIOS_DEBUG, "All HSPHY ports disabled, skipping HSPHY loading\n"); + return; + } + + /* + * Try to get HSPHY payload from CSME first, so we can always keep our + * HSPHY cache up to date. If we cannot allocate the buffer for it, the + * cache is our last resort. + */ + hsphy_buf = allocate_hsphy_buf(); + if (!hsphy_buf) { + printk(BIOS_ERR, "Could not allocate memory for HSPHY blob\n"); + if (CONFIG(INCLUDE_HSPHY_IN_FMAP)) { + printk(BIOS_INFO, "Trying to load HSPHY FW from cache\n"); + if (load_hsphy_from_cache()) { + printk(BIOS_INFO, "Successfully loaded HSPHY FW from cache\n"); + return; + } + printk(BIOS_ERR, "Failed to load HSPHY FW from cache\n"); } + printk(BIOS_ERR, "Aborting HSPHY FW loading, PCIe Gen5 won't work.\n"); + return; } memset(hsphy_buf, 0, HSPHY_PAYLOAD_SIZE); + /* + * If CSME is not present, try cached HSPHY FW. We still want to use + * CSME just in case CSME is updated along with HSPHY FW, so that we + * can update our cache if needed. + */ if (!is_cse_enabled()) { + if (CONFIG(INCLUDE_HSPHY_IN_FMAP)) { + printk(BIOS_INFO, "Trying to load HSPHY FW from cache" + " because CSME is not enabled or not visible\n"); + if (load_hsphy_from_cache()) { + printk(BIOS_INFO, "Successfully loaded HSPHY FW from cache\n"); + return; + } + printk(BIOS_ERR, "Failed to load HSPHY FW from cache\n"); + } printk(BIOS_ERR, "%s: CSME not enabled or not visible, but required\n", __func__); - printk(BIOS_ERR, "Aborting HSPHY firmware loading, PCIe Gen5 won't work.\n"); - goto hsphy_exit; + printk(BIOS_ERR, "Aborting HSPHY FW loading, PCIe Gen5 won't work.\n"); + if (!CONFIG(ENABLE_EARLY_DMA_PROTECTION)) + free(hsphy_buf); + return; } /* Ensure BAR, BME and memory space are enabled */ @@ -219,19 +416,32 @@ void load_and_init_hsphy(void) pci_or_config16(dev, PCI_COMMAND, pci_cmd_bme_mem); } - if (heci_get_hsphy_payload(hsphy_buf, &buf_size, hsphy_hash, &hash_type, &status)) { - printk(BIOS_ERR, "Aborting HSPHY firmware loading, PCIe Gen5 won't work.\n"); - goto hsphy_exit; - } + /* Try to get HSPHY payload from CSME and cache it if possible. */ + if (!heci_get_hsphy_payload(hsphy_buf, &buf_size, hsphy_hash, &hash_type, &status)) { + if (verify_hsphy_hash(hsphy_buf, buf_size, hsphy_hash, hash_type)) { + 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); - if (verify_hsphy_hash(hsphy_buf, buf_size, hsphy_hash, hash_type)) { - printk(BIOS_ERR, "Aborting HSPHY firmware loading, PCIe Gen5 won't work.\n"); - goto hsphy_exit; + if (!CONFIG(ENABLE_EARLY_DMA_PROTECTION)) + free(hsphy_buf); + return; + } else { + printk(BIOS_ERR, "Failed to verify HSPHY FW hash.\n"); + } + } else { + printk(BIOS_ERR, "Failed to get HSPHY FW over HECI.\n"); } - upload_hsphy_to_cpu_pcie(hsphy_buf, buf_size); - -hsphy_exit: if (!CONFIG(ENABLE_EARLY_DMA_PROTECTION)) free(hsphy_buf); + + /* We failed to get HSPHY payload from CSME, cache is our last chance. */ + if (CONFIG(INCLUDE_HSPHY_IN_FMAP) && load_hsphy_from_cache()) { + printk(BIOS_INFO, "Successfully loaded HSPHY FW from cache\n"); + return; + } + + printk(BIOS_ERR, "Failed to load HSPHY FW, PCIe Gen5 won't work.\n"); } diff --git a/util/cbfstool/default-x86.fmd b/util/cbfstool/default-x86.fmd index 41be782046..f008889fef 100644 --- a/util/cbfstool/default-x86.fmd +++ b/util/cbfstool/default-x86.fmd @@ -14,6 +14,7 @@ FLASH@##ROM_BASE## ##ROM_SIZE## { ##SMMSTORE_ENTRY## ##SPD_CACHE_ENTRY## ##VPD_ENTRY## + ##HSPHY_FW_ENTRY## FMAP@##FMAP_BASE## ##FMAP_SIZE## COREBOOT(CBFS)@##CBFS_BASE## ##CBFS_SIZE## } |