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;