From e5e82862627eaf6d7edeb29af8b5c71d628e8c3c Mon Sep 17 00:00:00 2001 From: Matt DeVillier Date: Fri, 16 Dec 2022 17:14:35 -0600 Subject: [PATCH] soc/amd/common/gfx: add support for VBIOS caching, selective GOP init One of the main functions performed by the FSP GOP driver is to modify the ATOMBIOS tables (part of the VBIOS) in memory based on the display output configuration. This device-specific modified VBIOS can be cached in a FMAP region specific for that purpose, then loaded into memory instead of the "generic" VBIOS, saving the ~130ms execution time of the GOP driver. As this approach only works when no pre-OS display output is needed, limit its use to ChromeOS builds, with the GOP driver enabled, and not booting in either recovery or developer modes. SoCs supporting this feature will need to selectively run the FSP GOP driver as needed, using the same criteria used here to determine whether to load the VBIOS from CBFS or from the FMAP cache. Boards utilizing this feature will need to add a dedicated FMAP region with the appropriate name/size, and select the required Kconfig options. BUG=b:255812886 TEST=tested with rest of patch train Change-Id: Ib9cfd192500d411655a3c8fa436098897428109e Signed-off-by: Matt DeVillier Reviewed-on: https://review.coreboot.org/c/coreboot/+/70858 Tested-by: build bot (Jenkins) Reviewed-by: Jon Murphy --- src/soc/amd/common/block/graphics/Kconfig | 16 +++ src/soc/amd/common/block/graphics/graphics.c | 102 +++++++++++++++++- .../block/include/amdblocks/vbios_cache.h | 21 ++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/soc/amd/common/block/include/amdblocks/vbios_cache.h diff --git a/src/soc/amd/common/block/graphics/Kconfig b/src/soc/amd/common/block/graphics/Kconfig index c277a0b23f..511f304373 100644 --- a/src/soc/amd/common/block/graphics/Kconfig +++ b/src/soc/amd/common/block/graphics/Kconfig @@ -10,3 +10,19 @@ config SOC_AMD_COMMON_BLOCK_GRAPHICS_ATIF Select this option to provide ATIF method with display brightness querying. Currently, the exported values only open up 0-255 as the brightness range for the display. + +config SOC_AMD_GFX_CACHE_VBIOS_IN_FMAP + bool "Support for caching modified VBIOS tables in flash" + depends on SOC_AMD_COMMON_BLOCK_GRAPHICS && CHROMEOS && RUN_FSP_GOP + default n + help + Enable support for flash based VBIOS cache. + +config USE_SELECTIVE_GOP_INIT + bool "Run FSP GOP driver only when needed for recovery/developer modes" + depends on SOC_AMD_GFX_CACHE_VBIOS_IN_FMAP + default n + help + Select this option to only run the FSP GOP driver when needed for pre-OS display init + (eg, Recovery and Developer Modes). Otherwise, use cached VBIOS/ATOMBIOS tables. + Selecting this option will save approx. 130ms boot time on the normal boot path. diff --git a/src/soc/amd/common/block/graphics/graphics.c b/src/soc/amd/common/block/graphics/graphics.c index 8910240d76..843bf787ee 100644 --- a/src/soc/amd/common/block/graphics/graphics.c +++ b/src/soc/amd/common/block/graphics/graphics.c @@ -2,13 +2,20 @@ #include #include +#include #include -#include +#include #include +#include +#include #include +#include #include #include +static bool vbios_loaded_from_cache = false; +static uint8_t vbios_data[VBIOS_CACHE_FMAP_SIZE]; + #define ATIF_FUNCTION_VERIFY_INTERFACE 0x0 struct atif_verify_interface_output { uint16_t size; /* Size of this object, including size field */ @@ -142,6 +149,13 @@ static void graphics_set_resources(struct device *const dev) return; timestamp_add_now(TS_OPROM_INITIALIZE); + if (CONFIG(USE_SELECTIVE_GOP_INIT) && vbios_cache_is_valid()) { + if (!vboot_recovery_mode_enabled() && !vboot_developer_mode_enabled()) { + vbios_load_from_cache(); + timestamp_add_now(TS_OPROM_COPY_END); + return; + } + } rom = pci_rom_probe(dev); if (rom == NULL) return; @@ -167,6 +181,92 @@ static void graphics_dev_init(struct device *const dev) pci_dev_init(dev); } +static void read_vbios_cache_from_fmap(void *unused) +{ + struct region_device rw_vbios_cache; + int32_t region_size; + + if (!CONFIG(SOC_AMD_GFX_CACHE_VBIOS_IN_FMAP)) + return; + + if (fmap_locate_area_as_rdev(VBIOS_CACHE_FMAP_NAME, &rw_vbios_cache)) { + printk(BIOS_ERR, "%s: No %s FMAP section.\n", __func__, VBIOS_CACHE_FMAP_NAME); + return; + } + + region_size = region_device_sz(&rw_vbios_cache); + + if (region_size != VBIOS_CACHE_FMAP_SIZE) { + printk(BIOS_ERR, "%s: %s FMAP size mismatch for VBIOS cache (%d vs %d).\n", + __func__, VBIOS_CACHE_FMAP_NAME, VBIOS_CACHE_FMAP_SIZE, region_size); + return; + } + + /* Read cached VBIOS data into buffer */ + if (rdev_readat(&rw_vbios_cache, &vbios_data, 0, VBIOS_CACHE_FMAP_SIZE) != VBIOS_CACHE_FMAP_SIZE) { + printk(BIOS_ERR, "Failed to read vbios data from flash; rdev_readat() failed.\n"); + return; + } + + printk(BIOS_SPEW, "VBIOS cache successfully read from FMAP.\n"); +} + +static void write_vbios_cache_to_fmap(void *unused) +{ + if (!CONFIG(SOC_AMD_GFX_CACHE_VBIOS_IN_FMAP)) + return; + + /* Don't save if VBIOS loaded from cache / data unchanged */ + if (vbios_loaded_from_cache == true) { + printk(BIOS_SPEW, "VBIOS data loaded from cache; not saving\n"); + return; + } + + struct region_device rw_vbios_cache; + + if (fmap_locate_area_as_rdev_rw(VBIOS_CACHE_FMAP_NAME, &rw_vbios_cache)) { + printk(BIOS_ERR, "%s: No %s FMAP section.\n", __func__, VBIOS_CACHE_FMAP_NAME); + return; + } + + /* copy from PCI_VGA_RAM_IMAGE_START to rdev */ + if (rdev_writeat(&rw_vbios_cache, (void *)PCI_VGA_RAM_IMAGE_START, 0, + VBIOS_CACHE_FMAP_SIZE) != VBIOS_CACHE_FMAP_SIZE) + printk(BIOS_ERR, "Failed to save vbios data to flash; rdev_writeat() failed.\n"); + + printk(BIOS_SPEW, "VBIOS cache successfully written to FMAP.\n"); +} + +/* + * Loads cached VBIOS data into legacy oprom location. + * + * Assumes user has called vbios_cache_is_valid() and checked for success + */ +void vbios_load_from_cache(void) +{ + /* copy cached vbios data from buffer to PCI_VGA_RAM_IMAGE_START */ + memcpy((void *)PCI_VGA_RAM_IMAGE_START, vbios_data, VBIOS_CACHE_FMAP_SIZE); + + /* mark cache as used so we know not to write it later */ + vbios_loaded_from_cache = true; +} + +/* + * Return true if VBIOS cache data is valid + * + * For now, just compare first 2 bytes of data + * TODO: replace with TPM hash verification once implemented + */ +bool vbios_cache_is_valid(void) +{ + bool is_valid = vbios_data[0] == 0x55 && vbios_data[1] == 0xaa; + printk(BIOS_SPEW, "VBIOS cache is %s\n", is_valid ? "valid" : "invalid"); + return is_valid; +} + +BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_EXIT, read_vbios_cache_from_fmap, NULL); +BOOT_STATE_INIT_ENTRY(BS_OS_RESUME_CHECK, BS_ON_ENTRY, write_vbios_cache_to_fmap, NULL); + const struct device_operations amd_graphics_ops = { .read_resources = pci_dev_read_resources, .set_resources = graphics_set_resources, diff --git a/src/soc/amd/common/block/include/amdblocks/vbios_cache.h b/src/soc/amd/common/block/include/amdblocks/vbios_cache.h new file mode 100644 index 0000000000..e9425723b9 --- /dev/null +++ b/src/soc/amd/common/block/include/amdblocks/vbios_cache.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __VBIOS_CACHE_H__ +#define __VBIOS_CACHE_H__ + +#include + +#define VBIOS_CACHE_FMAP_NAME "RW_VBIOS_CACHE" +#define VBIOS_CACHE_FMAP_SIZE 0x10000 + +/* + * Return true if VBIOS cache contains valid data + */ +bool vbios_cache_is_valid(void); + +/* + * Loads cached VBIOS data into legacy oprom location. + */ +void vbios_load_from_cache(void); + +#endif /* __VBIOS_CACHE_H__ */