aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Lombard <clombard@linux.ibm.com>2023-08-29 11:23:54 +0200
committerReza Arbab <arbab@linux.ibm.com>2023-09-12 14:22:11 -0500
commite172f39a881f8096c3a4f0924e43a55336873871 (patch)
treec25738993842da03abc793f4951e7bc56a4329bd
parent49059a6e457325d3f9926f6f15446d772ab88d6a (diff)
downloadskiboot-e172f39a881f8096c3a4f0924e43a55336873871.zip
skiboot-e172f39a881f8096c3a4f0924e43a55336873871.tar.gz
skiboot-e172f39a881f8096c3a4f0924e43a55336873871.tar.bz2
core/pldm: Register PLDM as a blocklevel device
In the same way that ipmi-hiomap implements the PNOR access control protocol, this patch allows to "virtualize" the content of a BMC flash based on lid files. Previously, flash PNOR partitions were viewed this way: partitionXX=NAME, start address, end address, flags The content of each partition is now stored in a lid file. In order to continue to use the libflash library, we manually fill in the contents of a fake flash header when accessing offset 0. This reproduces the behavior via ipmi-hiomap of reading the flash header on the BMC. For the reading and writing of BMC lids files, we convert the virtual addresses of these 'fake' partitions by identifying: lid id. Signed-off-by: Christophe Lombard <clombard@linux.ibm.com> Signed-off-by: Reza Arbab <arbab@linux.ibm.com>
-rw-r--r--core/pldm/pldm-lid-files.c247
-rw-r--r--include/pldm.h5
2 files changed, 252 insertions, 0 deletions
diff --git a/core/pldm/pldm-lid-files.c b/core/pldm/pldm-lid-files.c
index a46a731..19f0a9e 100644
--- a/core/pldm/pldm-lid-files.c
+++ b/core/pldm/pldm-lid-files.c
@@ -25,6 +25,14 @@ struct pldm_lid {
static LIST_HEAD(lid_files);
+struct pldm_ctx_data {
+ /* Members protected by the blocklevel lock */
+ struct blocklevel_device bl;
+ uint32_t total_size;
+ uint32_t erase_granule;
+ struct lock lock;
+};
+
#define MEGABYTE (1024*1024)
/*
@@ -34,6 +42,12 @@ static LIST_HEAD(lid_files);
*/
#define VMM_SIZE_RESERVED_PER_SECTION (32 * MEGABYTE)
+#define ERASE_GRANULE_DEF 0x1000
+
+/* 'fake' header flash */
+struct __ffs_hdr *raw_hdr;
+size_t raw_hdr_size;
+
/*
* Print the attributes of lid files.
*/
@@ -159,8 +173,221 @@ out:
return rc;
}
+static uint32_t checksum(void *data, size_t size)
+{
+ uint32_t i, csum = 0;
+
+ for (i = csum = 0; i < (size/4); i++)
+ csum ^= ((uint32_t *)data)[i];
+ return csum;
+}
+
+/* Helper functions for typesafety and size safety */
+static uint32_t hdr_checksum(struct __ffs_hdr *hdr)
+{
+ return checksum(hdr, sizeof(struct __ffs_hdr));
+}
+
+static uint32_t entry_checksum(struct __ffs_entry *ent)
+{
+ return checksum(ent, sizeof(struct __ffs_entry));
+}
+
+/*
+ * Fill __ffs structures in order to return a 'fake' header flash
+ */
+static int lid_ids_to_header_flash(void *buf, uint64_t len)
+{
+ struct __ffs_entry *entry;
+ struct pldm_lid *lid = NULL;
+ uint32_t count, part_id, i;
+ uint32_t block_size;
+
+ /* reading the flash header has already been requested */
+ if (raw_hdr) {
+ (raw_hdr_size < len) ? memcpy(buf, raw_hdr, raw_hdr_size) :
+ memcpy(buf, raw_hdr, len);
+ return OPAL_SUCCESS;
+ }
+
+ /* number of lid files */
+ count = get_lids_count();
+
+ /* last member of struct __ffs_hdr is a flexible array member */
+ raw_hdr_size = sizeof(struct __ffs_hdr) + (count * sizeof(struct __ffs_entry));
+ raw_hdr = zalloc(raw_hdr_size);
+ if (!raw_hdr)
+ return OPAL_NO_MEM;
+
+ /* complete header flash
+ * Represents the on flash layout of FFS structures
+ * Note: Beware that the size of the partition table is in units of block_size
+ *
+ * @magic: Eye catcher/corruption detector
+ * @version: Version of the structure
+ * @size: Size of partition table (in block_size)
+ * @entry_size: Size of struct __ffs_entry element (in bytes)
+ * @entry_count: Number of struct __ffs_entry elements in @entries array
+ * @block_size: Size of block on device (in bytes)
+ * @block_count: Number of blocks on device
+ * @checksum: Header checksum
+ */
+ /* size of the cached map: block_size * raw_hdr->size
+ * raw_hdr->size = 0x3: we take a little margin if the number
+ * of element would increase
+ */
+ block_size = ERASE_GRANULE_DEF;
+
+ raw_hdr->magic = cpu_to_be32(FFS_MAGIC);
+ raw_hdr->version = cpu_to_be32(FFS_VERSION_1);
+ raw_hdr->size = cpu_to_be32(0x3);
+ raw_hdr->entry_size = cpu_to_be32(sizeof(struct __ffs_entry));
+ raw_hdr->entry_count = cpu_to_be32(count);
+ raw_hdr->block_size = cpu_to_be32(block_size);
+ raw_hdr->block_count = cpu_to_be32(0x4000); /* value from IPMI/PNOR protocol */
+ raw_hdr->checksum = hdr_checksum(raw_hdr);
+
+ lid = list_top(&lid_files, struct pldm_lid, list);
+ part_id = 1;
+
+ for (i = 0; i < count; i++) {
+ entry = &raw_hdr->entries[i];
+
+ memcpy(entry->name, lid->name, sizeof(entry->name));
+ entry->name[FFS_PART_NAME_MAX] = '\0';
+ entry->base = cpu_to_be32(lid->start / block_size);
+ entry->size = cpu_to_be32(lid->length / block_size);
+ entry->pid = cpu_to_be32(FFS_PID_TOPLEVEL);
+ entry->id = cpu_to_be32(part_id);
+ entry->type = cpu_to_be32(0x1);
+ entry->flags = cpu_to_be32(0x0);
+ entry->actual = cpu_to_be32(lid->length);
+ entry->checksum = entry_checksum(entry);
+
+ lid = list_next(&lid_files, lid, list);
+ part_id++;
+ }
+
+ /* fill in rquester buffer */
+ (raw_hdr_size < len) ? memcpy(buf, raw_hdr, raw_hdr_size) :
+ memcpy(buf, raw_hdr, len);
+
+ return OPAL_SUCCESS;
+}
+
+/*
+ * Search lid member from the virtual address.
+ */
+static int vaddr_to_lid_id(uint64_t pos, uint32_t *start, uint32_t *handle,
+ uint32_t *length)
+{
+ struct pldm_lid *lid = NULL;
+
+ list_for_each(&lid_files, lid, list) {
+ if ((pos >= lid->start) && (pos < lid->start + VMM_SIZE_RESERVED_PER_SECTION)) {
+ *start = lid->start;
+ *handle = lid->handle;
+ *length = lid->length;
+ return OPAL_SUCCESS;
+ }
+ }
+
+ return OPAL_PARAMETER;
+}
+
+static int lid_files_read(struct blocklevel_device *bl __unused,
+ uint64_t pos, void *buf, uint64_t len)
+{
+ uint32_t lid_start, lid_handle, lid_length;
+ int rc = OPAL_SUCCESS;
+ uint64_t offset;
+
+ /* LPC is only 32bit */
+ if (pos > UINT_MAX || (pos + len) > UINT_MAX)
+ return FLASH_ERR_PARM_ERROR;
+
+ prlog(PR_TRACE, "lid files read at 0x%llx for 0x%llx\n",
+ pos, len);
+
+ if ((pos == 0) || (pos <= (ERASE_GRANULE_DEF * 0x3))) {
+ /* return a 'fake' header flash or cached map */
+ rc = lid_ids_to_header_flash(buf, len);
+ } else {
+ /* convert offset to lid id */
+ rc = vaddr_to_lid_id(pos, &lid_start,
+ &lid_handle, &lid_length);
+ if (rc)
+ return rc;
+
+ /* read lid file */
+ offset = pos - lid_start;
+ rc = pldm_file_io_read_file(lid_handle, lid_length,
+ offset, buf, len);
+ }
+
+ return rc;
+}
+
+static int lid_files_write(struct blocklevel_device *bl __unused,
+ uint64_t pos, const void *buf __unused,
+ uint64_t len)
+{
+ prlog(PR_ERR, "lid files writes at 0x%llx for 0x%llx\n",
+ pos, len);
+ return OPAL_UNSUPPORTED;
+}
+
+static int lid_files_erase(struct blocklevel_device *bl __unused,
+ uint64_t pos, uint64_t len)
+{
+
+ prlog(PR_ERR, "lid files erase at 0x%llx for 0x%llx\n",
+ pos, len);
+ return OPAL_UNSUPPORTED;
+}
+
+static int get_lid_files_info(struct blocklevel_device *bl,
+ const char **name, uint64_t *total_size,
+ uint32_t *erase_granule)
+{
+ struct pldm_ctx_data *ctx;
+
+ ctx = container_of(bl, struct pldm_ctx_data, bl);
+ ctx->bl.erase_mask = ctx->erase_granule - 1;
+
+ if (name)
+ *name = NULL;
+ if (total_size)
+ *total_size = ctx->total_size;
+ if (erase_granule)
+ *erase_granule = ctx->erase_granule;
+
+ return OPAL_SUCCESS;
+}
+
+bool pldm_lid_files_exit(struct blocklevel_device *bl)
+{
+ struct pldm_ctx_data *ctx;
+ struct pldm_lid *lid, *tmp;
+
+ if (bl) {
+ ctx = container_of(bl, struct pldm_ctx_data, bl);
+ free(ctx);
+ }
+
+ /* free all lid entries */
+ list_for_each_safe(&lid_files, lid, tmp, list)
+ free(lid);
+
+ if (raw_hdr)
+ free(raw_hdr);
+
+ return true;
+}
+
int pldm_lid_files_init(struct blocklevel_device **bl)
{
+ struct pldm_ctx_data *ctx;
uint32_t lid_files_count;
int rc;
@@ -169,6 +396,18 @@ int pldm_lid_files_init(struct blocklevel_device **bl)
*bl = NULL;
+ ctx = zalloc(sizeof(struct pldm_ctx_data));
+ if (!ctx)
+ return FLASH_ERR_MALLOC_FAILED;
+
+ init_lock(&ctx->lock);
+
+ ctx->bl.read = &lid_files_read;
+ ctx->bl.write = &lid_files_write;
+ ctx->bl.erase = &lid_files_erase;
+ ctx->bl.get_info = &get_lid_files_info;
+ ctx->bl.exit = &pldm_lid_files_exit;
+
/* convert lid ids data to pnor structure */
rc = lid_ids_to_vaddr_mapping();
if (rc)
@@ -179,8 +418,16 @@ int pldm_lid_files_init(struct blocklevel_device **bl)
prlog(PR_NOTICE, "Number of lid files: %d\n", lid_files_count);
print_lid_files_attr();
+ ctx->total_size = lid_files_count * VMM_SIZE_RESERVED_PER_SECTION;
+ ctx->erase_granule = ERASE_GRANULE_DEF;
+
+ ctx->bl.keep_alive = 0;
+
+ *bl = &(ctx->bl);
+
return OPAL_SUCCESS;
err:
+ free(ctx);
return rc;
}
diff --git a/include/pldm.h b/include/pldm.h
index 9c5369e..8622453 100644
--- a/include/pldm.h
+++ b/include/pldm.h
@@ -43,4 +43,9 @@ int pldm_fru_dt_add_bmc_version(void);
*/
int pldm_lid_files_init(struct blocklevel_device **bl);
+/**
+ * Remove lid ids data
+ */
+bool pldm_lid_files_exit(struct blocklevel_device *bl);
+
#endif /* __PLDM_H__ */