diff options
Diffstat (limited to 'src/mainboard')
-rw-r--r-- | src/mainboard/emulation/qemu-i440fx/Kconfig | 1 | ||||
-rw-r--r-- | src/mainboard/emulation/qemu-i440fx/Makefile.mk | 1 | ||||
-rw-r--r-- | src/mainboard/emulation/qemu-i440fx/rom_media.c | 212 | ||||
-rw-r--r-- | src/mainboard/emulation/qemu-q35/Kconfig | 1 | ||||
-rw-r--r-- | src/mainboard/emulation/qemu-q35/Makefile.mk | 2 |
5 files changed, 217 insertions, 0 deletions
diff --git a/src/mainboard/emulation/qemu-i440fx/Kconfig b/src/mainboard/emulation/qemu-i440fx/Kconfig index 24dfa7a2fc..842c053185 100644 --- a/src/mainboard/emulation/qemu-i440fx/Kconfig +++ b/src/mainboard/emulation/qemu-i440fx/Kconfig @@ -19,6 +19,7 @@ config BOARD_SPECIFIC_OPTIONS select NO_SMM select BOOT_DEVICE_NOT_SPI_FLASH select BOOT_DEVICE_MEMORY_MAPPED + select BOOT_DEVICE_SUPPORTS_WRITES config VBOOT select VBOOT_MUST_REQUEST_DISPLAY diff --git a/src/mainboard/emulation/qemu-i440fx/Makefile.mk b/src/mainboard/emulation/qemu-i440fx/Makefile.mk index 7d6041b62d..6ff2f73f11 100644 --- a/src/mainboard/emulation/qemu-i440fx/Makefile.mk +++ b/src/mainboard/emulation/qemu-i440fx/Makefile.mk @@ -7,6 +7,7 @@ postcar-y += exit_car.S ramstage-y += memmap.c ramstage-y += northbridge.c +ramstage-y += rom_media.c all-y += fw_cfg.c all-y += bootmode.c diff --git a/src/mainboard/emulation/qemu-i440fx/rom_media.c b/src/mainboard/emulation/qemu-i440fx/rom_media.c new file mode 100644 index 0000000000..d2469bade2 --- /dev/null +++ b/src/mainboard/emulation/qemu-i440fx/rom_media.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Inspired by OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c from edk2 */ + +#include <arch/mmio.h> +#include <boot_device.h> +#include <console/console.h> +#include <commonlib/helpers.h> +#include <commonlib/region.h> +#include <string.h> + +#define WRITE_BYTE_CMD 0x10 +#define BLOCK_ERASE_CMD 0x20 +#define CLEAR_STATUS_CMD 0x50 +#define READ_STATUS_CMD 0x70 +#define BLOCK_ERASE_CONFIRM_CMD 0xD0 +#define READ_ARRAY_CMD 0xFF + +#define CLEARED_ARRAY_STATUS 0x00 + +#define QEMU_FLASH_BLOCK_SIZE 0x1000 + +#if CONFIG(ELOG) +#include <southbridge/intel/common/pmutil.h> + +/* + * ELOG and VBOOT options are automatically enabled when building with + * CHROMEOS=y. While the former allows for logging PCH state (not that there is + * much to log on QEMU), the latter currently forces 16 MiB ROM size, which in + * turn doesn't allow mounting as pflash in QEMU. Using pflash is required to + * have writable flash, which means that the following function will not be + * able to write to the flash based log until ROM size and layout is changed in + * Flashmap used when building for vboot. + */ +void pch_log_state(void) {} +#endif + +static ssize_t qemu_writeat(const struct region_device *rd, const void *b, + size_t offset, size_t size) +{ + const struct mem_region_device *mdev; + size_t i; + volatile char *ptr; + const char *buf = b; + + mdev = container_of(rd, __typeof__(*mdev), rdev); + ptr = &mdev->base[offset]; + + for (i = 0; i < size; i++) { + write8(ptr, WRITE_BYTE_CMD); + write8(ptr, buf[i]); + ptr++; + } + + /* Restore flash to read mode. */ + if (size > 0) { + write8(ptr - 1, READ_ARRAY_CMD); + } + + return size; +} + +static ssize_t qemu_eraseat(const struct region_device *rd, size_t offset, + size_t size) +{ + const struct mem_region_device *mdev; + size_t i; + volatile char *ptr; + + mdev = container_of(rd, __typeof__(*mdev), rdev); + ptr = &mdev->base[offset]; + + if (!IS_ALIGNED(offset, QEMU_FLASH_BLOCK_SIZE)) { + printk(BIOS_ERR, "%s: erased offset isn't multiple of block size\n", + __func__); + return -1; + } + + if (!IS_ALIGNED(size, QEMU_FLASH_BLOCK_SIZE)) { + printk(BIOS_ERR, "%s: erased size isn't multiple of block size\n", + __func__); + return -1; + } + + for (i = 0; i < size; i += QEMU_FLASH_BLOCK_SIZE) { + write8(ptr, BLOCK_ERASE_CMD); + write8(ptr, BLOCK_ERASE_CONFIRM_CMD); + ptr += QEMU_FLASH_BLOCK_SIZE; + } + + /* Restore flash to read mode. */ + if (size > 0) { + write8(ptr - QEMU_FLASH_BLOCK_SIZE, READ_ARRAY_CMD); + } + + return size; +} + +static struct region_device_ops flash_ops; +static struct mem_region_device boot_dev; + +/* + * Depending on how firmware image was passed to QEMU, it may behave as: + * + * - ROM - memory mapped reads, writes are ignored (FW image mounted with + * '-bios'); + * - RAM - memory mapped reads and writes (FW image mounted with e.g. + * '-device loader'); + * - flash - memory mapped reads, write and erase possible through commands. + * Contrary to physical flash devices erase is not required before writing, + * but it also doesn't hurt. Flash may be split into read-only and read-write + * parts, like OVMF_CODE.fd and OVMF_VARS.fd. Maximal combined size of system + * firmware is hardcoded (QEMU < 5.0.0) or set by default to 8 MiB. On QEMU + * version 5.0.0 or newer, it is configurable with `max-fw-size` machine + * configuration option, up to 16 MiB to not overlap with IOAPIC memory range + * (FW image(s) mounted with '-drive if=pflash'). + * + * This function detects which of the above applies and fills region_device_ops + * accordingly. + */ +void boot_device_init(void) +{ + volatile char *ptr; + char original, readback; + static bool initialized = false; + + if (initialized) + return; + + /* + * mmap, munmap and readat are always identical to mem_rdev_rw_ops, other + * functions may vary. + */ + flash_ops = mem_rdev_rw_ops; + + boot_dev.base = (void *)(uintptr_t)(0x100000000ULL - CONFIG_ROM_SIZE); + boot_dev.rdev.ops = &flash_ops; + boot_dev.rdev.region.size = CONFIG_ROM_SIZE; + + /* + * Find first byte different than any of the commands, simplified. + * + * Detection code few lines below writes commands and tries to read back + * the response. To make that code simpler, make sure that original byte + * is different than any of the commands or expected responses. It is + * expected that such byte will always be found - it is virtually + * impossible to write valid x86 code with just bytes ending with 0, and + * there are also ASCII characters in metadata (CBFS, FMAP) that has bytes + * matching those assumptions. + */ + ptr = (volatile char *)boot_dev.base; + original = read8(ptr); + while (original == (char)0xFF || (original & 0x0F) == 0) + original = read8(++ptr); + + /* + * Detect what type of flash we're dealing with. This also clears any stale + * status bits, so the next read of status register should return known + * value (if pflash is used). + */ + write8(ptr, CLEAR_STATUS_CMD); + readback = read8(ptr); + if (readback == CLEAR_STATUS_CMD) { + printk(BIOS_DEBUG, "QEMU flash behaves as RAM\n"); + /* Restore original content. */ + write8(ptr, original); + } else { + /* Either ROM or QEMU flash implementation. */ + write8(ptr, READ_STATUS_CMD); + readback = read8(ptr); + if (readback == original) { + printk(BIOS_DEBUG, "QEMU flash behaves as ROM\n"); + /* ROM means no writing nor erasing. */ + flash_ops.writeat = NULL; + flash_ops.eraseat = NULL; + } else if (readback == CLEARED_ARRAY_STATUS) { + /* Try writing original value to test whether flash is writable. */ + write8(ptr, WRITE_BYTE_CMD); + write8(ptr, original); + write8(ptr, READ_STATUS_CMD); + readback = read8(ptr); + if (readback & 0x10 /* programming error */) { + printk(BIOS_DEBUG, + "QEMU flash behaves as write-protected flash\n"); + flash_ops.writeat = NULL; + flash_ops.eraseat = NULL; + } else { + printk(BIOS_DEBUG, "QEMU flash behaves as writable flash\n"); + flash_ops.writeat = qemu_writeat; + flash_ops.eraseat = qemu_eraseat; + } + /* Restore flash to read mode. */ + write8(ptr, READ_ARRAY_CMD); + } else { + printk(BIOS_ERR, "Unexpected QEMU flash behavior, assuming ROM\n"); + /* + * This shouldn't happen and first byte of flash may already be + * corrupted by testing, but don't take any further risk. + */ + flash_ops.writeat = NULL; + flash_ops.eraseat = NULL; + } + } + + initialized = true; +} + +/* boot_device_ro() is defined in arch/x86/mmap_boot.c */ +const struct region_device *boot_device_rw(void) +{ + return &boot_dev.rdev; +} diff --git a/src/mainboard/emulation/qemu-q35/Kconfig b/src/mainboard/emulation/qemu-q35/Kconfig index 11ea750496..2fb180b7ec 100644 --- a/src/mainboard/emulation/qemu-q35/Kconfig +++ b/src/mainboard/emulation/qemu-q35/Kconfig @@ -17,6 +17,7 @@ config BOARD_SPECIFIC_OPTIONS select MAINBOARD_HAS_CHROMEOS select BOOT_DEVICE_NOT_SPI_FLASH select BOOT_DEVICE_MEMORY_MAPPED + select BOOT_DEVICE_SUPPORTS_WRITES config VBOOT select VBOOT_MUST_REQUEST_DISPLAY diff --git a/src/mainboard/emulation/qemu-q35/Makefile.mk b/src/mainboard/emulation/qemu-q35/Makefile.mk index bc73edcbbb..a60fbd6367 100644 --- a/src/mainboard/emulation/qemu-q35/Makefile.mk +++ b/src/mainboard/emulation/qemu-q35/Makefile.mk @@ -12,6 +12,7 @@ postcar-y += memmap.c ramstage-y += ../qemu-i440fx/memmap.c ramstage-y += ../qemu-i440fx/northbridge.c +ramstage-y += ../qemu-i440fx/rom_media.c ramstage-y += memmap.c ramstage-y += cpu.c @@ -20,4 +21,5 @@ all-y += ../qemu-i440fx/bootmode.c ramstage-$(CONFIG_CHROMEOS) += chromeos.c +smm-y += ../qemu-i440fx/rom_media.c smm-y += memmap.c |