From bc744f5893fc4d53275ed26dd8d968011c6a09c1 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Fri, 17 Apr 2020 16:16:49 +0200 Subject: drivers/smmstore: Implement SMMSTORE version 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SMMSTORE version 2 is a complete redesign of the current driver. It is not backwards-compatible with version 1, and only one version can be used at a time. Key features: * Uses a fixed communication buffer instead of writing to arbitrary memory addresses provided by untrusted ring0 code. * Gives the caller full control over the used data format. * Splits the store into smaller chunks to allow fault tolerant updates. * Doesn't provide feedback about the actual read/written bytes, just returns error or success in registers. * Returns an error if the requested operation would overflow the communication buffer. Separate the SMMSTORE into 64 KiB blocks that can individually be read/written/erased. To be used by payloads that implement a FaultTolerant Variable store like TianoCore. The implementation has been tested against EDK2 master. An example EDK2 implementation can be found here: https://github.com/9elements/edk2-1/commit/eb1127744a3a5d5c8ac4e8eb76f07e79c736dbe2 Change-Id: I25e49d184135710f3e6dd1ad3bed95de950fe057 Signed-off-by: Patrick Rudolph Signed-off-by: Christian Walter Reviewed-on: https://review.coreboot.org/c/coreboot/+/40520 Tested-by: build bot (Jenkins) Reviewed-by: Michał Żygowski Reviewed-by: Matt DeVillier --- Documentation/drivers/index.md | 1 + Documentation/drivers/smmstorev2.md | 221 ++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 Documentation/drivers/smmstorev2.md (limited to 'Documentation') diff --git a/Documentation/drivers/index.md b/Documentation/drivers/index.md index e215c6ab11..508beaf40a 100644 --- a/Documentation/drivers/index.md +++ b/Documentation/drivers/index.md @@ -7,3 +7,4 @@ they allow to easily reuse existing code accross platforms. * [IPMI KCS](ipmi_kcs.md) * [SMMSTORE](smmstore.md) * [SoundWire](soundwire.md) +* [SMMSTOREv2](smmstorev2.md) diff --git a/Documentation/drivers/smmstorev2.md b/Documentation/drivers/smmstorev2.md new file mode 100644 index 0000000000..cb79b8b6b8 --- /dev/null +++ b/Documentation/drivers/smmstorev2.md @@ -0,0 +1,221 @@ +# SMM based flash storage driver Version 2 + +This documents the API exposed by the x86 system management based +storage driver. + +## SMMSTOREv2 + +SMMSTOREv2 is a [SMM] mediated driver to read from, write to and erase +a predefined region in flash. It can be enabled by setting +`CONFIG_SMMSTORE=y` and `CONFIG_SMMSTORE_V2=y` in menuconfig. + +This can be used by the OS or the payload to implement persistent +storage to hold for instance configuration data, without needing to +implement a (platform specific) storage driver in the payload itself. + +### Storage size and alignment + +SMMSTORE version 2 requires a minimum alignment of 64 KiB, which should +be supported by all flash chips. Not having to perform read-modify-write +operations is desired, as it reduces complexity and potential for bugs. + +This can be used by a FTW (FaultTolerantWrite) implementation that uses +at least two regions in an A/B update scheme. The FTW implementation in +EDK2 uses three different regions in the store: + +- The variable store +- The FTW spare block +- The FTW working block + +All regions must be block-aligned, and the FTW spare size must be larger +than that of the variable store. FTW working block can be much smaller. +With 64 KiB as block size, the minimum size of the FTW-enabled store is: + +- The variable store: 1 block = 64 KiB +- The FTW spare block: 2 blocks = 2 * 64 KiB +- The FTW working block: 1 block = 64 KiB + +Therefore, the minimum size for EDK2 FTW is 4 blocks, or 256 KiB. + +## API + +The API provides read and write access to an unformatted block storage. + +### Storage region + +By default SMMSTOREv2 will operate on a separate FMAP region called +`SMMSTORE`. The default generated FMAP will include such a region. On +systems with a locked FMAP, e.g. in an existing vboot setup with a +locked RO region, the option exists to add a cbfsfile called `smm_store` +in the `RW_LEGACY` (if CHROMEOS) or in the `COREBOOT` FMAP regions. It +is recommended for new builds using a handcrafted FMD that intend to +make use of SMMSTORE to include a sufficiently large `SMMSTORE` FMAP +region. It is mandatory to align the `SMMSTORE` region to 64KiB for +compatibility with the largest flash erase operation. + +When a default generated FMAP is used, the size of the FMAP region is +equal to `CONFIG_SMMSTORE_SIZE`. UEFI payloads expect at least 64 KiB. +To support a fault tolerant write mechanism, at least a multiple of +this size is recommended. + +### Communication buffer + +To prevent malicious ring0 code to access arbitrary memory locations, +SMMSTOREv2 uses a communication buffer in CBMEM/HOB for all transfers. +This buffer has to be at least 64 KiB in size and must be installed +before calling any of the SMMSTORE read or write operations. Usually, +coreboot will install this buffer to transfer data between ring0 and +the [SMM] handler. + +In order to get the communication buffer address, the payload or OS +has to read the coreboot table with tag `0x0039`, containing: + +```C +struct lb_smmstorev2 { + uint32_t tag; + uint32_t size; + uint32_t num_blocks; /* Number of writeable blocks in SMM */ + uint32_t block_size; /* Size of a block in byte. Default: 64 KiB */ + uint32_t mmap_addr; /* MMIO address of the store for read only access */ + uint32_t com_buffer; /* Physical address of the communication buffer */ + uint32_t com_buffer_size; /* Size of the communication buffer in byte */ + uint8_t apm_cmd; /* The command byte to write to the APM I/O port */ + uint8_t unused[3]; /* Set to zero */ +}; +``` + +The absence of this coreboot table entry indicates that there's no +SMMSTOREv2 support. + +### Blocks + +The SMMSTOREv2 splits the SMMSTORE FMAP partition into smaller chunks +called *blocks*. Every block is at least the size of 64KiB to support +arbitrary NOR flash erase ops. A payload or OS must make no further +assumptions about the block or communication buffer size. + +### Generating the SMI + +SMMSTOREv2 is called via an SMI, which is generated via a write to the +IO port defined in the smi_cmd entry of the FADT ACPI table. `%al` +contains `APM_CNT_SMMSTORE=0xed` and is written to the smi_cmd IO +port. `%ah` contains the SMMSTOREv2 command. `%ebx` contains the +parameter buffer to the SMMSTOREv2 command. + +### Return values + +If a command succeeds, SMMSTOREv2 will return with +`SMMSTORE_RET_SUCCESS=0` in `%eax`. On failure SMMSTORE will return +`SMMSTORE_RET_FAILURE=1`. For unsupported SMMSTORE commands +`SMMSTORE_REG_UNSUPPORTED=2` is returned. + +**NOTE 1**: The caller **must** check the return value and should make +no assumption on the returned data if `%eax` does not contain +`SMMSTORE_RET_SUCCESS`. + +**NOTE 2**: If the SMI returns without changing `%ax`, it can be assumed +that the SMMSTOREv2 feature is not installed. + +### Calling arguments + +SMMSTOREv2 supports 3 subcommands that are passed via `%ah`, the +additional calling arguments are passed via `%ebx`. + +**NOTE**: The size of the struct entries are in the native word size of +smihandler. This means 32 bits in almost all cases. + +#### - SMMSTORE_CMD_INIT = 4 + +This installs the communication buffer to use and thus enables the +SMMSTORE handler. This command can only be executed once and is done +by the firmware. Calling this function at runtime has no effect. + +The additional parameter buffer `%ebx` contains a pointer to the +following struct: + +```C +struct smmstore_params_init { + uint32_t com_buffer; + uint32_t com_buffer_size; +} __packed; +``` + +INPUT: +- `com_buffer`: Physical address of the communication buffer (CBMEM) +- `com_buffer_size`: Size in bytes of the communication buffer + +#### - SMMSTORE_CMD_RAW_READ = 5 + +SMMSTOREv2 allows reading arbitrary data. It is up to the caller to +initialize the store with meaningful data before using it. + +The additional parameter buffer `%ebx` contains a pointer to the +following struct: + +```C +struct smmstore_params_raw_read { + uint32_t bufsize; + uint32_t bufoffset; + uint32_t block_id; +} __packed; +``` + +INPUT: +- `bufsize`: Size of data to read within the communication buffer +- `bufoffset`: Offset within the communication buffer +- `block_id`: Block to read from + +#### - SMMSTORE_CMD_RAW_WRITE = 6 + +SMMSTOREv2 allows writing arbitrary data. It is up to the caller to +erase a block before writing it. + +The additional parameter buffer `%ebx` contains a pointer to +the following struct: + +```C +struct smmstore_params_raw_write { + uint32_t bufsize; + uint32_t bufoffset; + uint32_t block_id; +} __packed; +``` + +INPUT: +- `bufsize`: Size of data to write within the communication buffer +- `bufoffset`: Offset within the communication buffer +- `block_id`: Block to write to + +#### - SMMSTORE_CMD_RAW_CLEAR = 7 + +SMMSTOREv2 allows clearing blocks. A cleared block will read as `0xff`. +By providing multiple blocks the caller can implement a fault tolerant +write mechanism. It is up to the caller to clear blocks before writing +to them. + + +```C +struct smmstore_params_raw_clear { + uint32_t block_id; +} __packed; +``` + +INPUT: +- `block_id`: Block to erase + +#### Security + +Pointers provided by the payload or OS are checked to not overlap with +SMM. This protects the SMM handler from being compromised. + +As all information is exchanged using the communication buffer and +coreboot tables, there's no risk that a malicious application capable +of issuing SMIs could extract arbitrary data or modify the currently +running kernel. + +## External links + +* [A Tour Beyond BIOS Implementing UEFI Authenticated Variables in SMM with EDKI](https://software.intel.com/sites/default/files/managed/cf/ea/a_tour_beyond_bios_implementing_uefi_authenticated_variables_in_smm_with_edkii.pdf) +Note that this differs significantly from coreboot's implementation. + +[SMM]: ../security/smm.md -- cgit v1.2.3