Patrick Rudolph has uploaded this change for review.

View Change

[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/c7cbcd527af397d5d8473f3e52720ffde32d7a1c

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, &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;
+ 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, &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 (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

To view, visit change 40520. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I25e49d184135710f3e6dd1ad3bed95de950fe057
Gerrit-Change-Number: 40520
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <patrick.rudolph@9elements.com>
Gerrit-MessageType: newchange