drivers/smmstore: Implement SMMSTORE version 2

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:
eb1127744a

Change-Id: I25e49d184135710f3e6dd1ad3bed95de950fe057
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Signed-off-by: Christian Walter <christian.walter@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40520
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Reviewed-by: Matt DeVillier <matt.devillier@gmail.com>
This commit is contained in:
Patrick Rudolph 2020-04-17 16:16:49 +02:00 committed by Michał Żygowski
parent a693fa06cd
commit bc744f5893
12 changed files with 702 additions and 8 deletions

View File

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

View File

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

View File

@ -79,6 +79,7 @@ enum {
CB_TAG_MMC_INFO = 0x0035,
CB_TAG_TCPA_LOG = 0x0036,
CB_TAG_FMAP = 0x0037,
CB_TAG_SMMSTOREV2 = 0x0039,
CB_TAG_CMOS_OPTION_TABLE = 0x00c8,
CB_TAG_OPTION = 0x00c9,
CB_TAG_OPTION_ENUM = 0x00ca,

View File

@ -68,6 +68,7 @@
#define CBMEM_ID_ROM3 0x524f4d33
#define CBMEM_ID_FMAP 0x464d4150
#define CBMEM_ID_FSP_LOGO 0x4c4f474f
#define CBMEM_ID_SMM_COMBUFFER 0x53534d32
#define CBMEM_ID_TO_NAME_TABLE \
{ CBMEM_ID_ACPI, "ACPI " }, \

View File

@ -80,6 +80,7 @@ enum {
LB_TAG_TCPA_LOG = 0x0036,
LB_TAG_FMAP = 0x0037,
LB_TAG_PLATFORM_BLOB_VERSION = 0x0038,
LB_TAG_SMMSTOREV2 = 0x0039,
LB_TAG_CMOS_OPTION_TABLE = 0x00c8,
LB_TAG_OPTION = 0x00c9,
LB_TAG_OPTION_ENUM = 0x00ca,
@ -484,4 +485,20 @@ struct cmos_checksum {
#define CHECKSUM_PCBIOS 1
};
/* SMMSTOREv2 record
* This record contains information to use SMMSTOREv2.
*/
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 bytes */
uint8_t apm_cmd; /* The command byte to write to the APM I/O port */
uint8_t unused[3]; /* Set to zero */
};
#endif

View File

@ -6,6 +6,18 @@ config SMMSTORE
default y if PAYLOAD_TIANOCORE
select SPI_FLASH_SMM if BOOT_DEVICE_SPI_FLASH_RW_NOMMAP
config SMMSTORE_V2
bool "Use version 2 of SMMSTORE API"
depends on SMMSTORE
default n
help
Version 2 of SMMSTORE allows secure communication with SMM and
makes no assumptions on the structure of the data stored within.
It splits the store into chunks to allows fault tolerant writes.
By using version 2 you cannot make use of software that expects
a version 1 SMMSTORE.
config SMMSTORE_IN_CBFS
bool
default n

View File

@ -1,3 +1,4 @@
ramstage-$(CONFIG_SMMSTORE) += store.c
ramstage-$(CONFIG_SMMSTORE_V2) += ramstage.c
smm-$(CONFIG_SMMSTORE) += store.c smi.c

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <bootstate.h>
#include <cpu/x86/smm.h>
#include <commonlib/helpers.h>
#include <commonlib/region.h>
#include <console/console.h>
#include <smmstore.h>
#include <types.h>
#include <cbmem.h>
static struct smmstore_params_info info;
void lb_smmstorev2(struct lb_header *header)
{
struct lb_record *rec;
struct lb_smmstorev2 *store;
const struct cbmem_entry *e;
e = cbmem_entry_find(CBMEM_ID_SMM_COMBUFFER);
if (!e)
return;
rec = lb_new_record(header);
store = (struct lb_smmstorev2 *)rec;
store->tag = LB_TAG_SMMSTOREV2;
store->size = sizeof(*store);
store->com_buffer = (uintptr_t)cbmem_entry_start(e);
store->com_buffer_size = cbmem_entry_size(e);
store->mmap_addr = info.mmap_addr;
store->num_blocks = info.num_blocks;
store->block_size = info.block_size;
store->apm_cmd = APM_CNT_SMMSTORE;
}
static void init_store(void *unused)
{
struct smmstore_params_init args;
uint32_t eax = ~0;
uint32_t ebx;
if (smmstore_get_info(&info) < 0) {
printk(BIOS_INFO, "SMMSTORE: Failed to get meta data\n");
return;
}
void *ptr = cbmem_add(CBMEM_ID_SMM_COMBUFFER, info.block_size);
if (!ptr) {
printk(BIOS_ERR, "SMMSTORE: Failed to add com buffer\n");
return;
}
args.com_buffer = (uintptr_t)ptr;
args.com_buffer_size = info.block_size;
ebx = (uintptr_t)&args;
printk(BIOS_INFO, "SMMSTORE: Setting up SMI handler\n");
/* Issue SMI using APM to update the com buffer and to lock the SMMSTORE */
__asm__ __volatile__ (
"outb %%al, %%dx"
: "=a" (eax)
: "a" ((SMMSTORE_CMD_INIT << 8) | APM_CNT_SMMSTORE),
"b" (ebx),
"d" (APM_CNT)
: "memory");
if (eax != SMMSTORE_RET_SUCCESS) {
printk(BIOS_ERR, "SMMSTORE: Failed to install com buffer\n");
return;
}
}
/* The SMI APM handler is installed at DEV_INIT phase */
BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, init_store, NULL);

View File

@ -23,8 +23,7 @@ static int range_check(void *start, size_t size)
return 0;
}
/* Param is usually EBX, ret in EAX */
uint32_t smmstore_exec(uint8_t command, void *param)
static uint32_t smmstorev1_exec(uint8_t command, void *param)
{
uint32_t ret = SMMSTORE_RET_FAILURE;
@ -66,13 +65,89 @@ uint32_t smmstore_exec(uint8_t command, void *param)
ret = SMMSTORE_RET_SUCCESS;
break;
}
default:
printk(BIOS_DEBUG,
"Unknown SMM store command: 0x%02x\n", command);
"Unknown SMM store v1 command: 0x%02x\n", command);
ret = SMMSTORE_RET_UNSUPPORTED;
break;
}
return ret;
}
static uint32_t smmstorev2_exec(uint8_t command, void *param)
{
uint32_t ret = SMMSTORE_RET_FAILURE;
switch (command) {
case SMMSTORE_CMD_INIT: {
printk(BIOS_DEBUG, "Init SMM store\n");
struct smmstore_params_init *params = param;
if (range_check(params, sizeof(*params)) != 0)
break;
void *buf = (void *)(uintptr_t)params->com_buffer;
if (range_check(buf, params->com_buffer_size) != 0)
break;
if (smmstore_init(buf, params->com_buffer_size) == 0)
ret = SMMSTORE_RET_SUCCESS;
break;
}
case SMMSTORE_CMD_RAW_READ: {
printk(BIOS_DEBUG, "Raw read from SMM store, param = %p\n", param);
struct smmstore_params_raw_read *params = param;
if (range_check(params, sizeof(*params)) != 0)
break;
if (smmstore_rawread_region(params->block_id, params->bufoffset,
params->bufsize) == 0)
ret = SMMSTORE_RET_SUCCESS;
break;
}
case SMMSTORE_CMD_RAW_WRITE: {
printk(BIOS_DEBUG, "Raw write to SMM store, param = %p\n", param);
struct smmstore_params_raw_write *params = param;
if (range_check(params, sizeof(*params)) != 0)
break;
if (smmstore_rawwrite_region(params->block_id, params->bufoffset,
params->bufsize) == 0)
ret = SMMSTORE_RET_SUCCESS;
break;
}
case SMMSTORE_CMD_RAW_CLEAR: {
printk(BIOS_DEBUG, "Raw clear SMM store, param = %p\n", param);
struct smmstore_params_raw_clear *params = param;
if (range_check(params, sizeof(*params)) != 0)
break;
if (smmstore_rawclear_region(params->block_id) == 0)
ret = SMMSTORE_RET_SUCCESS;
break;
}
default:
printk(BIOS_DEBUG,
"Unknown SMM store v2 command: 0x%02x\n", command);
ret = SMMSTORE_RET_UNSUPPORTED;
break;
}
return ret;
}
uint32_t smmstore_exec(uint8_t command, void *param)
{
if (!param)
return SMMSTORE_RET_FAILURE;
if (CONFIG(SMMSTORE_V2))
return smmstorev2_exec(command, param);
else
return smmstorev1_exec(command, param);
}

View File

@ -262,3 +262,200 @@ int smmstore_clear_region(void)
return 0;
}
/* Implementation of Version 2 */
static bool store_initialized;
static struct mem_region_device mdev_com_buf;
static int smmstore_rdev_chain(struct region_device *rdev)
{
if (!store_initialized)
return -1;
return rdev_chain_full(rdev, &mdev_com_buf.rdev);
}
/**
* Call once before using the store. In SMM this must be called through an
* APM SMI handler providing the communication buffer address and length.
*/
int smmstore_init(void *buf, size_t len)
{
if (!buf || len < SMM_BLOCK_SIZE)
return -1;
if (store_initialized)
return -1;
mem_region_device_rw_init(&mdev_com_buf, buf, len);
store_initialized = true;
return 0;
}
#if ENV_RAMSTAGE
/**
* Provide metadata for the coreboot tables.
* Must only be called in ramstage, but not in SMM.
*/
int smmstore_get_info(struct smmstore_params_info *out)
{
struct region_device store;
if (lookup_store(&store) < 0) {
printk(BIOS_ERR, "smm store: lookup of store failed\n");
return -1;
}
if (!IS_ALIGNED(region_device_offset(&store), SMM_BLOCK_SIZE)) {
printk(BIOS_ERR, "smm store: store not aligned to block size\n");
return -1;
}
out->block_size = SMM_BLOCK_SIZE;
out->num_blocks = region_device_sz(&store) / SMM_BLOCK_SIZE;
/* FIXME: Broken EDK2 always assumes memory mapped Firmware Block Volumes */
out->mmap_addr = (uintptr_t)rdev_mmap_full(&store);
printk(BIOS_DEBUG, "smm store: %d # blocks with size 0x%x\n",
out->num_blocks, out->block_size);
return 0;
}
#endif
/* Returns -1 on error, 0 on success */
static int lookup_block_in_store(struct region_device *store, uint32_t block_id)
{
if (lookup_store(store) < 0) {
printk(BIOS_ERR, "smm store: lookup of store failed\n");
return -1;
}
if ((block_id * SMM_BLOCK_SIZE) >= region_device_sz(store)) {
printk(BIOS_ERR, "smm store: block ID out of range\n");
return -1;
}
return 0;
}
/* Returns NULL on error, pointer from rdev_mmap on success */
static void *mmap_com_buf(struct region_device *com_buf, uint32_t offset, uint32_t bufsize)
{
if (smmstore_rdev_chain(com_buf) < 0) {
printk(BIOS_ERR, "smm store: lookup of com buffer failed\n");
return NULL;
}
if (offset >= region_device_sz(com_buf)) {
printk(BIOS_ERR, "smm store: offset out of range\n");
return NULL;
}
void *ptr = rdev_mmap(com_buf, offset, bufsize);
if (!ptr)
printk(BIOS_ERR, "smm store: not enough space for new data\n");
return ptr;
}
/**
* Reads the specified block of the SMMSTORE and places it in the communication
* buffer.
* @param block_id The id of the block to operate on
* @param offset Offset within the block.
* Must be smaller than the block size.
* @param bufsize Size of chunk to read within the block.
* Must be smaller than the block size.
* @return Returns -1 on error, 0 on success.
*/
int smmstore_rawread_region(uint32_t block_id, uint32_t offset, uint32_t bufsize)
{
struct region_device store;
struct region_device com_buf;
if (lookup_block_in_store(&store, block_id) < 0)
return -1;
void *ptr = mmap_com_buf(&com_buf, offset, bufsize);
if (!ptr)
return -1;
printk(BIOS_DEBUG, "smm store: reading %p block %d, offset=0x%x, size=%x\n",
ptr, block_id, offset, bufsize);
ssize_t ret = rdev_readat(&store, ptr, block_id * SMM_BLOCK_SIZE + offset, bufsize);
rdev_munmap(&com_buf, ptr);
if (ret < 0)
return -1;
return 0;
}
/**
* Writes the specified block of the SMMSTORE by reading it from the communication
* buffer.
* @param block_id The id of the block to operate on
* @param offset Offset within the block.
* Must be smaller than the block size.
* @param bufsize Size of chunk to read within the block.
* Must be smaller than the block size.
* @return Returns -1 on error, 0 on success.
*/
int smmstore_rawwrite_region(uint32_t block_id, uint32_t offset, uint32_t bufsize)
{
struct region_device store;
struct region_device com_buf;
if (lookup_block_in_store(&store, block_id) < 0)
return -1;
if (rdev_chain(&store, &store, block_id * SMM_BLOCK_SIZE + offset, bufsize)) {
printk(BIOS_ERR, "smm store: not enough space for new data\n");
return -1;
}
void *ptr = mmap_com_buf(&com_buf, offset, bufsize);
if (!ptr)
return -1;
printk(BIOS_DEBUG, "smm store: writing %p block %d, offset=0x%x, size=%x\n",
ptr, block_id, offset, bufsize);
ssize_t ret = rdev_writeat(&store, ptr, 0, bufsize);
rdev_munmap(&com_buf, ptr);
if (ret < 0)
return -1;
return 0;
}
/**
* Erases the specified block of the SMMSTORE. The communication buffer remains untouched.
*
* @param block_id The id of the block to operate on
*
* @return Returns -1 on error, 0 on success.
*/
int smmstore_rawclear_region(uint32_t block_id)
{
struct region_device store;
if (lookup_block_in_store(&store, block_id) < 0)
return -1;
ssize_t ret = rdev_eraseat(&store, block_id * SMM_BLOCK_SIZE, SMM_BLOCK_SIZE);
if (ret != SMM_BLOCK_SIZE) {
printk(BIOS_ERR, "smm store: erasing block failed\n");
return -1;
}
return 0;
}

View File

@ -10,10 +10,18 @@
#define SMMSTORE_RET_FAILURE 1
#define SMMSTORE_RET_UNSUPPORTED 2
/* Version 1 */
#define SMMSTORE_CMD_CLEAR 1
#define SMMSTORE_CMD_READ 2
#define SMMSTORE_CMD_APPEND 3
/* Version 2 */
#define SMMSTORE_CMD_INIT 4
#define SMMSTORE_CMD_RAW_READ 5
#define SMMSTORE_CMD_RAW_WRITE 6
#define SMMSTORE_CMD_RAW_CLEAR 7
/* Version 1 */
struct smmstore_params_read {
void *buf;
ssize_t bufsize;
@ -26,12 +34,90 @@ struct smmstore_params_append {
size_t valsize;
};
/* SMM responder */
/* Version 2 */
/*
* The Version 2 protocol separates the SMMSTORE into 64KiB blocks, each
* of which can be read/written/cleared in an independent manner. The
* data format isn't specified. See documentation page for more details.
*/
#define SMM_BLOCK_SIZE (64 * KiB)
/*
* Sets the communication buffer to use for read and write operations.
*/
struct smmstore_params_init {
uint32_t com_buffer;
uint32_t com_buffer_size;
} __packed;
/*
* Returns the number of blocks the SMMSTORE supports and their size.
* For EDK2 this should be at least two blocks with 64 KiB each.
* The mmap_addr is set the memory mapped physical address of the SMMSTORE.
*/
struct smmstore_params_info {
uint32_t num_blocks;
uint32_t block_size;
uint32_t mmap_addr;
} __packed;
/*
* Reads a chunk of raw data with size @bufsize from the block specified by
* @block_id starting at @bufoffset.
* The read data is placed in memory pointed to by @buf.
*
* @block_id must be less than num_blocks
* @bufoffset + @bufsize must be less than block_size
*/
struct smmstore_params_raw_write {
uint32_t bufsize;
uint32_t bufoffset;
uint32_t block_id;
} __packed;
/*
* Writes a chunk of raw data with size @bufsize to the block specified by
* @block_id starting at @bufoffset.
*
* @block_id must be less than num_blocks
* @bufoffset + @bufsize must be less than block_size
*/
struct smmstore_params_raw_read {
uint32_t bufsize;
uint32_t bufoffset;
uint32_t block_id;
} __packed;
/*
* Erases the specified block.
*
* @block_id must be less than num_blocks
*/
struct smmstore_params_raw_clear {
uint32_t block_id;
} __packed;
/* SMM handler */
uint32_t smmstore_exec(uint8_t command, void *param);
/* implementation */
/* Implementation of Version 1 */
int smmstore_read_region(void *buf, ssize_t *bufsize);
int smmstore_append_data(void *key, uint32_t key_sz,
void *value, uint32_t value_sz);
int smmstore_append_data(void *key, uint32_t key_sz, void *value, uint32_t value_sz);
int smmstore_clear_region(void);
/* Implementation of Version 2 */
int smmstore_init(void *buf, size_t len);
int smmstore_rawread_region(uint32_t block_id, uint32_t offset, uint32_t bufsize);
int smmstore_rawwrite_region(uint32_t block_id, uint32_t offset, uint32_t bufsize);
int smmstore_rawclear_region(uint32_t block_id);
#if ENV_RAMSTAGE
int smmstore_get_info(struct smmstore_params_info *info);
#endif
/* Advertise SMMSTORE v2 support */
struct lb_header;
void lb_smmstorev2(struct lb_header *header);
#endif

View File

@ -20,6 +20,8 @@
#include <spi_flash.h>
#include <security/vboot/misc.h>
#include <security/vboot/vbnv_layout.h>
#include <smmstore.h>
#if CONFIG(USE_OPTION_TABLE)
#include <option_table.h>
#endif
@ -548,6 +550,10 @@ static uintptr_t write_coreboot_table(uintptr_t rom_table_end)
add_cbmem_pointers(head);
/* SMMSTORE v2 */
if (CONFIG(SMMSTORE_V2))
lb_smmstorev2(head);
/* Add board-specific table entries, if any. */
lb_board(head);