/* 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 #include #include #include #include #include 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); } } uint32_t spi_flash_get_mmap_windows(struct flash_mmap_window *table) { int i; uint32_t count = 0; bios_mmap_init(); for (i = 0; i < TOTAL_DECODE_WINDOWS; i++) { if (region_sz(&real_dev_windows[i].sub_region) == 0) continue; count++; table->flash_base = region_offset(&real_dev_windows[i].sub_region); table->host_base = (uintptr_t)rdev_mmap_full(&shadow_devs[i].rdev); table->size = region_sz(&real_dev_windows[i].sub_region); table++; } return count; }