drivers/smmstore: allow full flash access for capsule updates

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 <krystian.hebel@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/83424
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Michał Żygowski <michal.zygowski@3mdeb.com>
This commit is contained in:
Krystian Hebel 2024-06-26 19:52:19 +02:00 committed by Matt DeVillier
commit 0cc0e6996c
5 changed files with 104 additions and 1 deletions

View file

@ -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)

View file

@ -10,9 +10,10 @@
#include <drivers/efi/efivars.h>
#include <drivers/efi/capsules.h>
#include <memrange.h>
#include <smm_call.h>
#include <smmstore.h>
#include <string.h>
#include <stdio.h>
#include <smmstore.h>
#include <types.h>
#include <Uefi/UefiSpec.h>
@ -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);

View file

@ -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;

View file

@ -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",

View file

@ -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;