diff options
-rw-r--r-- | src/soc/intel/common/block/fast_spi/Kconfig | 45 | ||||
-rw-r--r-- | src/soc/intel/common/block/fast_spi/Makefile.inc | 32 | ||||
-rw-r--r-- | src/soc/intel/common/block/fast_spi/mmap_boot.c | 162 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/fast_spi.h | 7 |
4 files changed, 246 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/fast_spi/Kconfig b/src/soc/intel/common/block/fast_spi/Kconfig index 936927219d..eb2373ca8b 100644 --- a/src/soc/intel/common/block/fast_spi/Kconfig +++ b/src/soc/intel/common/block/fast_spi/Kconfig @@ -11,3 +11,48 @@ config FAST_SPI_DISABLE_WRITE_STATUS default y help Disable the write status SPI opcode in Intel Fast SPI block. + +config FAST_SPI_SUPPORTS_EXT_BIOS_WINDOW + bool + depends on SOC_INTEL_COMMON_BLOCK_FAST_SPI + help + Fast SPI controller on the platform supports additional + window for memory mapping BIOS region (region 1) on the SPI + flash beyond the standard limit of 16MiB. Depending upon the + size of the SPI flash part used by the mainboard, two decode + windows will be enabled: + 1. Fixed decode window up to a maximum size of 16MiB under + 4G boundary. + 2. Extended decode window up to a maximum size provided by + the platform to map the rest of the BIOS region. + SoC selecting this config is expected to provide the base and + maximum size of the extended window in the host address space + using configs EXT_BIOS_WIN_BASE and EXT_BIOS_WIN_SIZE. + +config EXT_BIOS_WIN_BASE + hex + help + If an additional window for mapping BIOS region greater than + 16MiB is supported, then this config is used to provide the + base address reserved for the mapping. Since the mapping is + done at the top of the window, depending upon the size of the + BIOS region, the actual base address configured in the fast + SPI controller can be higher at runtime. + +config EXT_BIOS_WIN_SIZE + hex + help + Maximum size of the extended window reserved for mapping BIOS + region greater than 16MiB. The actual mapped window might be + smaller depending upon the size of the BIOS region. + +if FAST_SPI_SUPPORTS_EXT_BIOS_WINDOW + +# Disable X86_TOP4G_BOOTMEDIA_MAP because the fast SPI controller +# driver provides a custom boot media device when multiple decode +# windows are used for the BIOS region. + +config X86_TOP4G_BOOTMEDIA_MAP + default n + +endif diff --git a/src/soc/intel/common/block/fast_spi/Makefile.inc b/src/soc/intel/common/block/fast_spi/Makefile.inc index e5b50aa0bf..79a2f97563 100644 --- a/src/soc/intel/common/block/fast_spi/Makefile.inc +++ b/src/soc/intel/common/block/fast_spi/Makefile.inc @@ -19,3 +19,35 @@ smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_FAST_SPI) += fast_spi_flash.c endif CPPFLAGS_common += -I$(src)/soc/intel/common/block/fast_spi + +ifeq ($(CONFIG_FAST_SPI_SUPPORTS_EXT_BIOS_WINDOW),y) + +# mmap_boot.c provides a custom boot media device for the platforms that support +# additional window for BIOS regions greater than 16MiB. This is used instead of +# the default boot media device in arch/x86/mmap_boot.c +bootblock-y += mmap_boot.c +verstage-y += mmap_boot.c +romstage-y += mmap_boot.c +postcar-y += mmap_boot.c +ramstage-y += mmap_boot.c +smm-y += mmap_boot.c + +# Check to ensure that no sections in the FMAP cross 16MiB boundary if +# the platform supports split decode windows for BIOS region greater +# than 16MiB. +check-fmap-16mib-crossing: $(obj)/fmap_config.h + flash_offset=$$(printf "%d" $$(cat $(obj)/fmap_config.h | grep "FMAP_SECTION_FLASH_START" | awk '{print $$NF}')); \ + for x in $$(cat $(obj)/fmap_config.h | grep "FMAP_TERMINAL_SECTIONS" | cut -d\" -f2); do \ + start=$$(printf "%d" $$(cat $(obj)/fmap_config.h | grep "FMAP_SECTION_"$$x"_START" | awk '{print $$NF}')); \ + size=$$(printf "%d" $$(cat $(obj)/fmap_config.h | grep "FMAP_SECTION_"$$x"_SIZE" | awk '{print $$NF}')); \ + start=$$((start-flash_offset)); \ + end=$$((start+size-1)); \ + if [ $$start -lt 16777216 ] && [ $$end -ge 16777216 ]; then echo "ERROR:" $$x "crosses 16MiB boundary"; fail=1; break; fi; \ + done; \ + if [ $$fail -eq 1 ]; then false; fi + +INTERMEDIATE+=check-fmap-16mib-crossing + +CBFSTOOL_ADD_CMD_OPTIONS += --ext-win-base $(CONFIG_EXT_BIOS_WIN_BASE) --ext-win-size $(CONFIG_EXT_BIOS_WIN_SIZE) + +endif # CONFIG_FAST_SPI_SUPPORTS_EXT_BIOS_WINDOW diff --git a/src/soc/intel/common/block/fast_spi/mmap_boot.c b/src/soc/intel/common/block/fast_spi/mmap_boot.c new file mode 100644 index 0000000000..8435a855f5 --- /dev/null +++ b/src/soc/intel/common/block/fast_spi/mmap_boot.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This file provides a custom boot media device for the platforms that support additional + * window for BIOS regions greater than 16MiB. If the mainboard uses a smaller BIOS region, then + * the additional window is unused. + */ + +#include <boot_device.h> +#include <commonlib/region.h> +#include <console/console.h> +#include <fmap.h> +#include <intelblocks/fast_spi.h> + +enum window_type { + /* Fixed decode window of max 16MiB size just below 4G boundary */ + FIXED_DECODE_WINDOW, + /* Additional decode window for mapping BIOS region greater than 16MiB */ + EXT_BIOS_DECODE_WINDOW, + TOTAL_DECODE_WINDOWS, +}; + +static struct xlate_region_device real_dev; +static struct mem_region_device shadow_devs[TOTAL_DECODE_WINDOWS]; +static struct xlate_window real_dev_windows[TOTAL_DECODE_WINDOWS]; + +static void initialize_window(enum window_type type, uintptr_t host_base, + uintptr_t flash_base, size_t size) +{ + mem_region_device_ro_init(&shadow_devs[type], (void *)host_base, size); + xlate_window_init(&real_dev_windows[type], &shadow_devs[type].rdev, + flash_base, size); + printk(BIOS_INFO, "MMAP window: SPI flash base=0x%lx, Host base=0x%lx, Size=0x%zx\n", + flash_base, host_base, size); +} + +/* + * + * +--------------+ + * | | + * | | + * | | + * ^ +------------+--------------------------^--------------------------------+ 0xffffffff + * | | | | | | + * | | | | | | + * | | | + | FIXED | + * | | | fixed_win_size | DECODE | + * | | BIOS | + | WINDOW | + * + | region | | | | + * bios_size | (Region 1) | | | | + * + | | | | | + * | | | | | | + * | | | fixed_win_flash_base+----v--------------------------------+ fixed_win_host_base + * | | | | | | + * | | | | | | + * | | | | | Other MMIO | + * v | | | | | + * bios_start +------------+ ext_win_flash_base | | | + * | | + | | | + * | | | | | | + * | | | | | | + * | | | +-----^------------------------------------------------------------^ + * 0 +------------+ | | | | | + * | + | EXT_BIOS | | + * SPI flash | ext_win_size | DECODE | | + * address | + | WINDOW | + + * space | | | | CONFIG_EXT_BIOS_WIN_SIZE + * +--------------v-------------------------------+ ext_win_host_base + + * | | | + * | Unused | | + * | | | + * +--------------+ CONFIG_EXT_BIOS_WIN_BASE+--v + * | | + * | | + * | | + * +--------------+ + * + * Host address + * space + */ +static void bios_mmap_init(void) +{ + static bool init_done; + + size_t bios_size, bios_start; + + uintptr_t fixed_win_host_base, fixed_win_flash_base; + uintptr_t ext_win_host_base, ext_win_flash_base; + size_t fixed_win_size, ext_win_size; + + size_t win_count = 0; + + if (init_done) + return; + + /* Read the offset and size of BIOS region in the SPI flash address space. */ + bios_start = fast_spi_get_bios_region(&bios_size); + + /* + * By default, fixed decode window (maximum size 16MiB) is mapped just below the 4G + * boundary. This window maps the top part of the BIOS region in the SPI flash address + * space to the host address space. + */ + fixed_win_size = MIN(16 * MiB, bios_size); + fixed_win_host_base = 4ULL * GiB - fixed_win_size; + fixed_win_flash_base = bios_start + bios_size - fixed_win_size; + + initialize_window(FIXED_DECODE_WINDOW, fixed_win_host_base, fixed_win_flash_base, + fixed_win_size); + win_count++; + + _Static_assert(CONFIG_EXT_BIOS_WIN_BASE != 0, "Extended BIOS window base cannot be 0!"); + _Static_assert(CONFIG_EXT_BIOS_WIN_SIZE != 0, "Extended BIOS window size cannot be 0!"); + + /* + * Remaining portion of the BIOS region up to a maximum of CONFIG_EXT_BIOS_WIN_SIZE is + * mapped at the top of the extended window if the BIOS region is greater than 16MiB. + * + * If the BIOS region is not greater than 16MiB, then the extended window is not + * enabled. + */ + ext_win_size = MIN(CONFIG_EXT_BIOS_WIN_SIZE, bios_size - fixed_win_size); + + if (ext_win_size) { + ext_win_host_base = CONFIG_EXT_BIOS_WIN_BASE + CONFIG_EXT_BIOS_WIN_SIZE - + ext_win_size; + ext_win_flash_base = fixed_win_flash_base - ext_win_size; + initialize_window(EXT_BIOS_DECODE_WINDOW, ext_win_host_base, + ext_win_flash_base, ext_win_size); + win_count++; + } + + xlate_region_device_ro_init(&real_dev, win_count, real_dev_windows, CONFIG_ROM_SIZE); + + init_done = true; +} + +const struct region_device *boot_device_ro(void) +{ + bios_mmap_init(); + + return &real_dev.rdev; +} + +void fast_spi_get_ext_bios_window(uintptr_t *base, size_t *size) +{ + const struct region_device *rd = &shadow_devs[EXT_BIOS_DECODE_WINDOW].rdev; + + bios_mmap_init(); + + *size = region_device_sz(rd); + + if (*size == 0) { + *base = 0; + } else { + /* + * This is a memory region device. So, mmap returns the base address of the + * device. Also, as this is a memory region device, unmap is a no-op. + */ + *base = (uintptr_t)rdev_mmap_full(rd); + } +} diff --git a/src/soc/intel/common/block/include/intelblocks/fast_spi.h b/src/soc/intel/common/block/include/intelblocks/fast_spi.h index 52ffdb319d..0a7e64dad4 100644 --- a/src/soc/intel/common/block/include/intelblocks/fast_spi.h +++ b/src/soc/intel/common/block/include/intelblocks/fast_spi.h @@ -74,4 +74,11 @@ bool fast_spi_wpd_status(void); */ void fast_spi_enable_wp(void); +/* + * Get base and size of extended BIOS decode window used at runtime in host address space. If + * the BIOS region is not greater than 16MiB, then this function returns 0 for both base and + * size. + */ +void fast_spi_get_ext_bios_window(uintptr_t *base, size_t *size); + #endif /* SOC_INTEL_COMMON_BLOCK_FAST_SPI_H */ |