From 0cc0e6996c8ef2e46bc776f405c8384077f90225 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Wed, 26 Jun 2024 19:52:19 +0200 Subject: [PATCH] drivers/smmstore: allow full flash access for capsule updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With DRIVERS_EFI_UPDATE_CAPSULES enabled and when at least one capsule was found, SMMSTORE SMI handler can use commands with the highest bit (0x80) set to access the whole flash instead of just the SMMSTORE region. The rest of the interface is identical to regular SMMSTORE v2 except for a new call to control full flash access. The added call saves information about the availability of capsules in SMM memory. The call is ignored when run more than once, meaning there should be no way of enabling full flash handling after it was disabled and vice versa. The call should always be made by the firmware to lock further calls, so that an OS could not gain full flash access. This is done on entry to BS_POST_DEVICE after capsules are obtained in BS_DEV_INIT. Change-Id: I7f3dbfa965b9dcbade8b2f06a5bd2ac1345c7972 Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk Reviewed-on: https://review.coreboot.org/c/coreboot/+/83424 Tested-by: build bot (Jenkins) Reviewed-by: Michał Żygowski --- Documentation/drivers/smmstorev2.md | 39 +++++++++++++++++++++++++++++ src/drivers/efi/capsules.c | 16 +++++++++++- src/drivers/smmstore/smi.c | 3 +++ src/drivers/smmstore/store.c | 35 ++++++++++++++++++++++++++ src/include/smmstore.h | 12 +++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) diff --git a/Documentation/drivers/smmstorev2.md b/Documentation/drivers/smmstorev2.md index af6e0ce7a6..cf714835a5 100644 --- a/Documentation/drivers/smmstorev2.md +++ b/Documentation/drivers/smmstorev2.md @@ -197,6 +197,45 @@ coreboot tables, there's no risk that a malicious application capable of issuing SMIs could extract arbitrary data or modify the currently running kernel. +## Capsule update API + +Availability of this command is tied to `CONFIG_DRIVERS_EFI_UPDATE_CAPSULES`. + +To allow updating full flash content (except if locked at hardware +level), few new calls were added. They reuse communication buffer, SMI +command, return values and calling arguments of SMMSTORE commands listed +above, with the exception of subcommand passed via `%ah`. If the +subcommand is to operate on full flash size, it has the highest bit set, +e.g. it is `0x85` for `SMMSTORE_CMD_RAW_READ` and `0x86` for +`SMMSTORE_CMD_RAW_WRITE`. Every `block_id` describes block relative to +the beginning of a flash, maximum value depends on its size. + +Attempts to write the protected memory regions can lead to undesired +consequences ranging from system instability to bricking and security +vulnerabilities. When this feature is used, care must be taken to temporarily +lift protections for the duration of an update when the whole flash is +rewritten or the update must be constrained to affect only writable portions of +the flash (e.g., "BIOS" region). + +There is one new subcommand that must be called before any other subcommands +with highest bit set can be used. + +### - SMMSTORE_CMD_USE_FULL_FLASH = 0x80 + +This command can only be executed once and is done by the firmware. +Calling this function at runtime has no effect. It takes one additional +parameter that, contrary to other commands, isn't a pointer. Instead, +`%ebx` indicates requested state of full flash access. If it equals 0, +commands for accessing full flash are permanently disabled, otherwise +they are permanently enabled until the next boot. + +The assumption is that if capsule updates are enabled at build time and +whole flash access is enabled at runtime, a UEFI payload (highly likely +EDK2 or its derivative) won't allow a regular OS to boot if the handler is +enabled without rebooting first. There could be a way of deactivating the +handler, but coreboot, having no way of enforcing its usage, might as well +permit access until a reboot and rely on the payload to do the right thing. + ## External links * [A Tour Beyond BIOS Implementing UEFI Authenticated Variables in SMM with EDK II](https://github.com/tianocore-docs/Docs/raw/master/White_Papers/A_Tour_Beyond_BIOS_Implementing_UEFI_Authenticated_Variables_in_SMM_with_EDKII_V2.pdf) diff --git a/src/drivers/efi/capsules.c b/src/drivers/efi/capsules.c index e8078bbd48..c92438311c 100644 --- a/src/drivers/efi/capsules.c +++ b/src/drivers/efi/capsules.c @@ -10,9 +10,10 @@ #include #include #include +#include +#include #include #include -#include #include #include @@ -786,3 +787,16 @@ static void parse_capsules(void *unused) BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, parse_capsules, NULL); #endif + +static void enable_capsule_smi(void *unused) +{ + uint32_t ret; + + ret = call_smm(APM_CNT_SMMSTORE, SMMSTORE_CMD_USE_FULL_FLASH, + (void *)(uintptr_t)uefi_capsule_count); + + printk(BIOS_INFO, "%sabled capsule update SMI handler\n", + ret == SMMSTORE_RET_SUCCESS ? "En" : "Dis"); +} + +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_ENTRY, enable_capsule_smi, NULL); diff --git a/src/drivers/smmstore/smi.c b/src/drivers/smmstore/smi.c index 4f7b4bdac0..3ac52dc345 100644 --- a/src/drivers/smmstore/smi.c +++ b/src/drivers/smmstore/smi.c @@ -138,6 +138,9 @@ static uint32_t smmstorev2_exec(uint8_t command, void *param) uint32_t smmstore_exec(uint8_t command, void *param) { + if (smmstore_preprocess_cmd(&command, param)) + return SMMSTORE_RET_SUCCESS; + if (command != SMMSTORE_CMD_CLEAR && !param) return SMMSTORE_RET_FAILURE; diff --git a/src/drivers/smmstore/store.c b/src/drivers/smmstore/store.c index f1e07e41dd..35887b3c7f 100644 --- a/src/drivers/smmstore/store.c +++ b/src/drivers/smmstore/store.c @@ -41,8 +41,43 @@ _Static_assert(SMM_BLOCK_SIZE <= FMAP_SECTION_SMMSTORE_SIZE, * crash/reboot could clear out all variables. */ +static int smmstore_use_full_flash; +static int has_capsules = -1; + +int smmstore_preprocess_cmd(uint8_t *cmd, void *param) +{ + if (CONFIG(DRIVERS_EFI_UPDATE_CAPSULES)) { + if (has_capsules == -1 && *cmd == SMMSTORE_CMD_USE_FULL_FLASH) { + has_capsules = !!(uintptr_t)param; + /* + * If we have capsules, return success, otherwise let smmstore_exec() + * fail on !param check, which will be 0 in that case. This informs + * the caller whether capsule handling was enabled or not. + */ + return has_capsules; + } else if (has_capsules == 1 && *cmd & SMMSTORE_CMD_USE_FULL_FLASH) { + smmstore_use_full_flash = 1; + *cmd &= ~SMMSTORE_CMD_USE_FULL_FLASH; + } else { + smmstore_use_full_flash = 0; + } + } + + return 0; +} + static enum cb_err lookup_store_region(struct region *region) { + if (CONFIG(DRIVERS_EFI_UPDATE_CAPSULES) && smmstore_use_full_flash) { + const struct region_device *rdev = boot_device_rw(); + + if (rdev == NULL) + return CB_ERR; + + *region = *region_device_region(rdev); + return CB_SUCCESS; + } + if (fmap_locate_area(SMMSTORE_REGION, region)) { printk(BIOS_WARNING, "smm store: Unable to find SMM store FMAP region '%s'\n", diff --git a/src/include/smmstore.h b/src/include/smmstore.h index 6805cbc069..595fe8b29b 100644 --- a/src/include/smmstore.h +++ b/src/include/smmstore.h @@ -21,6 +21,16 @@ #define SMMSTORE_CMD_RAW_WRITE 6 #define SMMSTORE_CMD_RAW_CLEAR 7 +/* + * Used by capsule updates as a standalone command or modifier to v2 commands. + * + * Availability depends on CONFIG(DRIVERS_EFI_UPDATE_CAPSULES). Usage of this + * extension requires considering which portions of the flash is read-only or + * otherwise protected to avoid causing problems while trying to overwrite + * them. + */ +#define SMMSTORE_CMD_USE_FULL_FLASH 0x80 + /* Version 1 */ struct smmstore_params_read { void *buf; @@ -117,6 +127,8 @@ int smmstore_get_info(struct smmstore_params_info *info); #endif struct region_device; int smmstore_lookup_region(struct region_device *rstore); +/* Returns 0 if normal parsing should continue, 1 otherwise */ +int smmstore_preprocess_cmd(uint8_t *cmd, void *param); /* Advertise SMMSTORE v2 support */ struct lb_header;