From e172f39a881f8096c3a4f0924e43a55336873871 Mon Sep 17 00:00:00 2001 From: Christophe Lombard Date: Tue, 29 Aug 2023 11:23:54 +0200 Subject: 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 Signed-off-by: Reza Arbab --- core/pldm/pldm-lid-files.c | 247 +++++++++++++++++++++++++++++++++++++++++++++ include/pldm.h | 5 + 2 files changed, 252 insertions(+) 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__ */ -- cgit v1.1