Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/40520 )
Change subject: [RFC]drivers/smmstore: Implement SMMSTORE version 2 ......................................................................
[RFC]drivers/smmstore: Implement SMMSTORE version 2
Seperates the SMMSTORE into 64KiB 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/c7cbcd527af397d5d8473f3e52720ffde...
Change-Id: I25e49d184135710f3e6dd1ad3bed95de950fe057 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/drivers/smmstore/smi.c M src/drivers/smmstore/store.c M src/include/smmstore.h 3 files changed, 227 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/20/40520/1
diff --git a/src/drivers/smmstore/smi.c b/src/drivers/smmstore/smi.c index 57f79b8..501f3d3 100644 --- a/src/drivers/smmstore/smi.c +++ b/src/drivers/smmstore/smi.c @@ -60,6 +60,47 @@ break; }
+ case SMMSTORE_CMD_INFO: { + printk(BIOS_DEBUG, "Returning SMM store info\n"); + struct smmstore_params_info *params = param; + if (smmstore_update_info(params) == 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; + void *buf = (void *)(uintptr_t)params->buf; + + if (range_check(buf, params->bufsize) != 0) + break; + + if (smmstore_rawread_region(buf, params->bufoffset, + params->block_id, ¶ms->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; + void *buf = (void *)(uintptr_t)params->buf; + + if (range_check(buf, params->bufsize) != 0) + break; + + if (smmstore_rawwrite_region(buf, params->bufoffset, + params->block_id, ¶ms->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 (smmstore_rawclear_region(params->block_id) == 0) + ret = SMMSTORE_RET_SUCCESS; + break; + } default: printk(BIOS_DEBUG, "Unknown SMM store command: 0x%02x\n", command); diff --git a/src/drivers/smmstore/store.c b/src/drivers/smmstore/store.c index dbad085..e10f133 100644 --- a/src/drivers/smmstore/store.c +++ b/src/drivers/smmstore/store.c @@ -263,3 +263,112 @@
return 0; } + + +/* implementation of Version 2 */ + +static struct smmstore_params_info info_params; + +int smmstore_update_info(struct smmstore_params_info *out) +{ + struct region_device store; + + if (!info_params.block_size) { + if (lookup_store(&store) < 0) { + printk(BIOS_WARNING, "smm store: reading region failed\n"); + return -1; + } + info_params.block_size = 64 * KiB; + info_params.num_blocks = region_device_sz(&store) / info_params.block_size; + printk(BIOS_DEBUG, "smm store: %d # blocks with size 0x%x\n", + info_params.num_blocks, info_params.block_size); + } + + out->block_size = info_params.block_size; + out->num_blocks = info_params.num_blocks; + /* FIXME: Broken EDK2 always assumes memory mapped Firmware Block Volumes */ + out->mmap_addr = (uintptr_t)rdev_mmap_full(&store); + return 0; +} + +int smmstore_rawread_region(void *buf, uint32_t offset, uint32_t block_id, uint32_t *bufsize) +{ + struct region_device store; + ssize_t ret; + + if (lookup_store(&store) < 0) { + printk(BIOS_WARNING, "reading region failed\n"); + return -1; + } + + if (offset >= info_params.block_size || block_id >= info_params.num_blocks) { + printk(BIOS_WARNING, "access out of region requested\n"); + return -1; + } + + ssize_t tx = MIN(*bufsize, (block_id + 1) * info_params.block_size - offset); + printk(BIOS_DEBUG, "smm store: reading %p block %d, offset=0x%x, size=%zx\n", + buf, block_id, offset, tx); + ret = rdev_readat(&store, buf, block_id * info_params.block_size + offset, tx); + if (ret < 0) + return -1; + + *bufsize = ret; + + return 0; +} + +int smmstore_rawwrite_region(void *buf, uint32_t offset, uint32_t block_id, uint32_t *bufsize) +{ + struct region_device store; + ssize_t ret; + + if (lookup_store(&store) < 0) { + printk(BIOS_WARNING, "writing region failed\n"); + return -1; + } + printk(BIOS_DEBUG, "smm store: writing %p block %d, offset=0x%x, size=%x\n", + buf, block_id, offset, *bufsize); + + if (offset >= info_params.block_size || block_id >= info_params.num_blocks) { + printk(BIOS_WARNING, "access out of region requested\n"); + return -1; + } + + if (rdev_chain(&store, &store, block_id * info_params.block_size + offset, *bufsize)) { + printk(BIOS_WARNING, "not enough space for new data\n"); + return -1; + } + + ret = rdev_writeat(&store, buf, 0, *bufsize); + if (ret < 0) + return -1; + + *bufsize = ret; + + return 0; +} + +int smmstore_rawclear_region(uint32_t block_id) +{ + struct region_device store; + + if (block_id >= info_params.num_blocks) { + printk(BIOS_WARNING, "access out of region requested\n"); + return -1; + } + + if (lookup_store(&store) < 0) { + printk(BIOS_WARNING, "smm store: reading region failed\n"); + return -1; + } + + ssize_t res = rdev_eraseat(&store, block_id * info_params.block_size, + info_params.block_size); + if (res != info_params.block_size) { + printk(BIOS_WARNING, "smm store: erasing block failed\n"); + return -1; + } + + return 0; +} diff --git a/src/include/smmstore.h b/src/include/smmstore.h index af07ff0..6a32008 100644 --- a/src/include/smmstore.h +++ b/src/include/smmstore.h @@ -11,10 +11,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_INFO 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; @@ -27,12 +35,80 @@ size_t valsize; };
+/* Version 2 */ +/* + * Version 2 of the SMM requires as SMMSTORE of at least 256KiB of size, that + * has to be aligned to 64KiB. + * This allows the payload to store raw data in the SMMSTORE flash region. + * This can be used by a FaultTolerantWrite implementation, that uses at least + * two regions in an A/B update scheme. + * + * The Version 2 protocol seperates the SMMSTORE into 64KiB blocks, where each block + * can be read/written/cleared in an independent manner. + * FIXME: The data format isn't specified. + * + */ +/* + * Returns the number of blocks the SMMSTORE supports and their size. + * For EDK2 this should be at least two blocks with 64KiB each. + */ +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 buf; + 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 buf; + 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 responder */ 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_clear_region(void); +/* implementation of Version 2 */ +int smmstore_update_info(struct smmstore_params_info *info); +int smmstore_rawread_region(void *buf, uint32_t offset, uint32_t block_id, uint32_t *bufsize); +int smmstore_rawwrite_region(void *buf, uint32_t offset, uint32_t block_id, uint32_t *bufsize); +int smmstore_rawclear_region(uint32_t block_id); + #endif