[Skiboot] [PATCH V2 2/2] core/pldm: Register PLDM as a blocklevel device

Christophe Lombard clombard at linux.vnet.ibm.com
Sat Mar 19 01:39:56 AEDT 2022


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 at linux.vnet.ibm.com>
---
 core/pldm/pldm-lid-files.c | 226 +++++++++++++++++++++++++++++++++++++
 include/pldm.h             |   5 +
 2 files changed, 231 insertions(+)

diff --git a/core/pldm/pldm-lid-files.c b/core/pldm/pldm-lid-files.c
index bdadb2bf..5c94c529 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,17 @@ static LIST_HEAD(lid_files);
  */
 #define VMM_SIZE_RESERVED_PER_SECTION (16 * MEGABYTE)
 
+/* The version of this partition implementation */
+#define FFS_VERSION_1 1
+
+/* Magic number for the partition header (ASCII 'PART') */
+#define FFS_MAGIC 0x50415254
+
+/* pid of logical partitions/containers */
+#define FFS_PID_TOPLEVEL 0xFFFFFFFF
+
+#define ERASE_GRANULE_DEF 0x1000
+
 /*
  * Print the attributes of lid files.
  */
@@ -144,8 +163,195 @@ 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 __ffs_hdr *raw_hdr;
+	struct pldm_lid *lid = NULL;
+	uint32_t count, part_id, i;
+	uint32_t block_size;
+
+	/* number of lid files */
+	count = get_lids_count();
+
+	/* last member of struct __ffs_hdr is a flexible array member */
+	raw_hdr = zalloc(sizeof(struct __ffs_hdr) + (count * sizeof(struct __ffs_entry)));
+
+	/* complete header flash */
+	block_size = sizeof(struct __ffs_hdr) + (sizeof(struct __ffs_entry) * count);
+	block_size = ALIGN_UP(block_size, 1 << 13);
+
+	raw_hdr->magic = cpu_to_be32(FFS_MAGIC);
+	raw_hdr->version = cpu_to_be32(FFS_VERSION_1);
+	raw_hdr->size = cpu_to_be32(0x1);
+	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);
+	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];
+
+		strcpy(entry->name, lid->name);
+		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++;
+	}
+
+	memcpy(buf, raw_hdr, len);
+
+	return OPAL_SUCCESS;
+}
+
+/*
+ * Search lid member from the virtual address.
+ */
+static struct pldm_lid *vaddr_to_lid_id(uint64_t pos)
+{
+	struct pldm_lid *lid = NULL;
+
+	list_for_each(&lid_files, lid, list)
+		if ((pos >= lid->start) && (pos < lid->start + VMM_SIZE_RESERVED_PER_SECTION))
+			break;
+
+	return lid;
+}
+
+static int lid_files_read(struct blocklevel_device *bl __unused,
+			  uint64_t pos, void *buf, uint64_t len)
+{
+	struct pldm_lid *lid;
+	uint64_t offset;
+	int rc = 0;
+
+	/* LPC is only 32bit */
+	if (pos > UINT_MAX || 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) {
+		/* return a 'fake' header flash */
+		rc = lid_ids_to_header_flash(buf, len);
+	} else {
+		/* convert offset to lid id */
+		lid = vaddr_to_lid_id(pos);
+		if (!lid)
+			return OPAL_PARAMETER;
+
+		/* read lid file */
+		offset = pos - lid->start;
+		rc = pldm_file_io_read_file(lid->handle, lid->length, buf, offset, len);
+	}
+
+	return rc;
+}
+
+static int lid_files_write(struct blocklevel_device *bl __unused,
+			   uint64_t pos, const void *buf, uint64_t len)
+{
+	struct pldm_lid *lid;
+	uint64_t offset;
+
+	/* LPC is only 32bit */
+	if (pos > UINT_MAX || len > UINT_MAX)
+		return FLASH_ERR_PARM_ERROR;
+
+	prlog(PR_TRACE, "lid files write at 0x%llx for 0x%llx\n", pos, len);
+
+	/* convert offset to lid id */
+	lid = vaddr_to_lid_id(pos);
+	if (!lid)
+		return OPAL_PARAMETER;
+
+	/* write lid file */
+	offset = pos - lid->start;
+	return pldm_file_io_write_file(lid->handle, buf, offset, len);
+}
+
+static int lid_files_erase(struct blocklevel_device *bl __unused,
+			   uint64_t pos, uint64_t len)
+{
+
+	prlog(PR_TRACE, "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_lid *lid;
+	bool status = true;
+
+	struct pldm_ctx_data *ctx;
+	if (bl) {
+		ctx = container_of(bl, struct pldm_ctx_data, bl);
+		free(ctx);
+	}
+
+	/* free all lid entries */
+	list_for_each(&lid_files, lid, list)
+		free(lid);
+
+	return status;
+}
+
 int pldm_lid_files_init(struct blocklevel_device **bl)
 {
+	struct pldm_ctx_data *ctx;
 	uint32_t lid_files_count;
 	int rc;
 
@@ -154,6 +360,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)
@@ -164,8 +382,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 568f7cf2..c0c4644e 100644
--- a/include/pldm.h
+++ b/include/pldm.h
@@ -12,6 +12,11 @@
  */
 int pldm_mctp_init(void);
 
+/**
+ * Exit PLDM over MCTP
+ */
+bool pldm_lid_files_exit(struct blocklevel_device *bl);
+
 /**
  * Enable PLDM over MCTP
  */
-- 
2.35.1



More information about the Skiboot mailing list