From 53c21aaa803e148c9c79cb3f5e0252d911506b10 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 26 Mar 2018 11:56:16 +1030 Subject: vpnor: Isolate relevant code in vpnor directory This is prepatory work for introducing more vpnor-specific behaviours to window handling. We will be introducing more objects to link, in order to hook some of the window command handlers. This change takes the opportunity to revert back to the upstream names for some of the original C files. Change-Id: I6b67ae466a2695054035e65ba752881be9c32d1a Signed-off-by: Andrew Jeffery --- .gitignore | 1 + Makefile.am | 15 +- mbox.h | 2 +- mboxd.c | 2 +- mboxd_flash.c | 299 ++++++++++++++++ mboxd_flash_physical.c | 299 ---------------- mboxd_flash_virtual.cpp | 235 ------------- mboxd_lpc_physical.c | 34 -- mboxd_lpc_reset.c | 34 ++ mboxd_lpc_virtual.cpp | 36 -- mboxd_pnor_partition_table.cpp | 132 -------- mboxd_pnor_partition_table.h | 67 ---- pnor_partition.cpp | 163 --------- pnor_partition.hpp | 139 -------- pnor_partition_defs.h | 136 -------- pnor_partition_table.cpp | 376 --------------------- pnor_partition_table.hpp | 345 ------------------- test/Makefile.am.include | 10 +- test/vpnor/Makefile.am.include | 229 ++++--------- test/vpnor/create_pnor_partition_table.cpp | 2 +- test/vpnor/create_read_window_oob.cpp | 2 +- test/vpnor/create_read_window_partition_exists.cpp | 2 +- .../vpnor/create_read_window_partition_invalid.cpp | 2 +- test/vpnor/create_read_window_remap.cpp | 2 +- test/vpnor/create_read_window_size.cpp | 2 +- .../create_read_window_straddle_partitions.cpp | 2 +- test/vpnor/create_read_window_toc.cpp | 2 +- test/vpnor/create_write_window_ro_partition.cpp | 2 +- test/vpnor/create_write_window_rw_partition.cpp | 2 +- test/vpnor/create_write_window_unmapped.cpp | 2 +- test/vpnor/dump_flash.cpp | 2 +- test/vpnor/read_patch.cpp | 2 +- test/vpnor/tmpd.hpp | 2 +- test/vpnor/toc_flags.cpp | 4 +- test/vpnor/toc_lookup_failed.cpp | 2 +- test/vpnor/toc_lookup_found.cpp | 2 +- test/vpnor/toc_missing_file.cpp | 2 +- test/vpnor/toc_no_end.cpp | 2 +- test/vpnor/toc_no_name.cpp | 2 +- test/vpnor/toc_no_start.cpp | 2 +- test/vpnor/toc_no_version.cpp | 2 +- test/vpnor/toc_overlap.cpp | 2 +- test/vpnor/toc_start_gt_end.cpp | 2 +- vpnor/Makefile.am.include | 10 + vpnor/mboxd_flash.cpp | 235 +++++++++++++ vpnor/mboxd_lpc_reset.cpp | 36 ++ vpnor/mboxd_pnor_partition_table.cpp | 132 ++++++++ vpnor/mboxd_pnor_partition_table.h | 67 ++++ vpnor/pnor_partition.cpp | 163 +++++++++ vpnor/pnor_partition.hpp | 139 ++++++++ vpnor/pnor_partition_defs.h | 136 ++++++++ vpnor/pnor_partition_table.cpp | 376 +++++++++++++++++++++ vpnor/pnor_partition_table.hpp | 345 +++++++++++++++++++ 53 files changed, 2074 insertions(+), 2169 deletions(-) create mode 100644 mboxd_flash.c delete mode 100644 mboxd_flash_physical.c delete mode 100644 mboxd_flash_virtual.cpp delete mode 100644 mboxd_lpc_physical.c create mode 100644 mboxd_lpc_reset.c delete mode 100644 mboxd_lpc_virtual.cpp delete mode 100644 mboxd_pnor_partition_table.cpp delete mode 100644 mboxd_pnor_partition_table.h delete mode 100644 pnor_partition.cpp delete mode 100644 pnor_partition.hpp delete mode 100644 pnor_partition_defs.h delete mode 100644 pnor_partition_table.cpp delete mode 100644 pnor_partition_table.hpp create mode 100644 vpnor/Makefile.am.include create mode 100644 vpnor/mboxd_flash.cpp create mode 100644 vpnor/mboxd_lpc_reset.cpp create mode 100644 vpnor/mboxd_pnor_partition_table.cpp create mode 100644 vpnor/mboxd_pnor_partition_table.h create mode 100644 vpnor/pnor_partition.cpp create mode 100644 vpnor/pnor_partition.hpp create mode 100644 vpnor/pnor_partition_defs.h create mode 100644 vpnor/pnor_partition_table.cpp create mode 100644 vpnor/pnor_partition_table.hpp diff --git a/.gitignore b/.gitignore index 7b18612..af6c720 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ Makefile.in *.swp /libtool /*.o +/vpnor/*.o /config.h /config.h.in~ /config.log diff --git a/Makefile.am b/Makefile.am index 84c5772..1bc69c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,19 +13,10 @@ mboxd_LDFLAGS = $(LIBSYSTEMD_LIBS) mboxd_CFLAGS = $(LIBSYSTEMD_CFLAGS) if VIRTUAL_PNOR_ENABLED -mboxd_SOURCES += pnor_partition_table.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ - mboxd_lpc_virtual.cpp - -mboxd_LDFLAGS += -lstdc++fs \ - $(SDBUSPLUS_LIBS) \ - $(PHOSPHOR_LOGGING_LIBS) \ - $(PHOSPHOR_DBUS_INTERFACES_LIBS) +include vpnor/Makefile.am.include else -mboxd_SOURCES += mboxd_flash_physical.c \ - mboxd_lpc_physical.c +mboxd_SOURCES += mboxd_flash.c \ + mboxd_lpc_reset.c endif mboxctl_SOURCES = mboxctl.c diff --git a/mbox.h b/mbox.h index eca0ea1..67d5622 100644 --- a/mbox.h +++ b/mbox.h @@ -8,7 +8,7 @@ #include #include #include -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" enum api_version { API_VERSION_INVAL = 0, diff --git a/mboxd.c b/mboxd.c index b944998..f92d9fb 100644 --- a/mboxd.c +++ b/mboxd.c @@ -35,7 +35,7 @@ #include "mboxd_lpc.h" #include "mboxd_msg.h" #include "mboxd_windows.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" #define USAGE \ "\nUsage: %s [-V | --version] [-h | --help] [-v[v] | --verbose] [-s | --syslog]\n" \ diff --git a/mboxd_flash.c b/mboxd_flash.c new file mode 100644 index 0000000..5cd5868 --- /dev/null +++ b/mboxd_flash.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2018 IBM Corp. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mbox.h" +#include "common.h" +#include "mboxd_flash.h" + +int init_flash_dev(struct mbox_context *context) +{ + char *filename = get_dev_mtd(); + int fd, rc = 0; + + if (!filename) { + MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n"); + return -1; + } + + MSG_DBG("Opening %s\n", filename); + + /* Open Flash Device */ + fd = open(filename, O_RDWR); + if (fd < 0) { + MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", + filename, strerror(errno)); + rc = -errno; + goto out; + } + context->fds[MTD_FD].fd = fd; + + /* Read the Flash Info */ + if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) { + MSG_ERR("Couldn't get information about MTD: %s\n", + strerror(errno)); + rc = -1; + goto out; + } + + if (context->flash_size == 0) { + /* + * PNOR images for current OpenPOWER systems are at most 64MB + * despite the PNOR itself sometimes being as big as 128MB. To + * ensure the image read from the PNOR is exposed in the LPC + * address space at the location expected by the host firmware, + * it is required that the image size be used for + * context->flash_size, and not the size of the flash device. + * + * However, the test cases specify the flash size via special + * test APIs (controlling flash behaviour) which don't have + * access to the mbox context. Rather than requiring + * error-prone assignments in every test case, we instead rely + * on context->flash_size being set to the size reported by the + * MEMINFO ioctl(). + * + * As this case should never be hit in production (i.e. outside + * the test environment), log an error. As a consequence, this + * error is expected in the test case output. + */ + MSG_ERR("Flash size MUST be supplied on the commandline. However, continuing by assuming flash is %u bytes\n", + context->mtd_info.size); + context->flash_size = context->mtd_info.size; + } + + /* We know the erase size so we can allocate the flash_erased bytemap */ + context->erase_size_shift = log_2(context->mtd_info.erasesize); + context->flash_bmap = calloc(context->flash_size >> + context->erase_size_shift, + sizeof(*context->flash_bmap)); + MSG_DBG("Flash erase size: 0x%.8x\n", context->mtd_info.erasesize); + +out: + free(filename); + return rc; +} + +void free_flash_dev(struct mbox_context *context) +{ + free(context->flash_bmap); + close(context->fds[MTD_FD].fd); +} + +/* Flash Functions */ + +/* + * flash_is_erased() - Check if an offset into flash is erased + * @context: The mbox context pointer + * @offset: The flash offset to check (bytes) + * + * Return: true if erased otherwise false + */ +static inline bool flash_is_erased(struct mbox_context *context, + uint32_t offset) +{ + return context->flash_bmap[offset >> context->erase_size_shift] + == FLASH_ERASED; +} + +/* + * set_flash_bytemap() - Set the flash erased bytemap + * @context: The mbox context pointer + * @offset: The flash offset to set (bytes) + * @count: Number of bytes to set + * @val: Value to set the bytemap to + * + * The flash bytemap only tracks the erased status at the erase block level so + * this will update the erased state for an (or many) erase blocks + * + * Return: 0 if success otherwise negative error code + */ +int set_flash_bytemap(struct mbox_context *context, uint32_t offset, + uint32_t count, uint8_t val) +{ + if ((offset + count) > context->flash_size) { + return -MBOX_R_PARAM_ERROR; + } + + MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n", + offset, count, val ? "ERASED" : "DIRTY"); + memset(context->flash_bmap + (offset >> context->erase_size_shift), + val, + align_up(count, 1 << context->erase_size_shift) >> + context->erase_size_shift); + + return 0; +} + +/* + * erase_flash() - Erase the flash + * @context: The mbox context pointer + * @offset: The flash offset to erase (bytes) + * @size: The number of bytes to erase + * + * Return: 0 on success otherwise negative error code + */ +int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count) +{ + const uint32_t erase_size = 1 << context->erase_size_shift; + struct erase_info_user erase_info = { 0 }; + int rc; + + MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count); + + /* + * We have an erased_bytemap for the flash so we want to avoid erasing + * blocks which we already know to be erased. Look for runs of blocks + * which aren't erased and erase the entire run at once to avoid how + * often we have to call the erase ioctl. If the block is already + * erased then there's nothing we need to do. + */ + while (count) { + if (!flash_is_erased(context, offset)) { /* Need to erase */ + if (!erase_info.length) { /* Start of not-erased run */ + erase_info.start = offset; + } + erase_info.length += erase_size; + } else if (erase_info.length) { /* Already erased|end of run? */ + /* Erase the previous run which just ended */ + MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", + erase_info.start, erase_info.length); + rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, + &erase_info); + if (rc < 0) { + MSG_ERR("Couldn't erase flash at 0x%.8x\n", + erase_info.start); + return -MBOX_R_SYSTEM_ERROR; + } + /* Mark ERASED where we just erased */ + set_flash_bytemap(context, erase_info.start, + erase_info.length, FLASH_ERASED); + erase_info.start = 0; + erase_info.length = 0; + } + + offset += erase_size; + count -= erase_size; + } + + if (erase_info.length) { + MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", + erase_info.start, erase_info.length); + rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info); + if (rc < 0) { + MSG_ERR("Couldn't erase flash at 0x%.8x\n", + erase_info.start); + return -MBOX_R_SYSTEM_ERROR; + } + /* Mark ERASED where we just erased */ + set_flash_bytemap(context, erase_info.start, erase_info.length, + FLASH_ERASED); + } + + return 0; +} + +#define CHUNKSIZE (64 * 1024) + +/* + * copy_flash() - Copy data from the flash device into a provided buffer + * @context: The mbox context pointer + * @offset: The flash offset to copy from (bytes) + * @mem: The buffer to copy into (must be of atleast 'size' bytes) + * @size: The number of bytes to copy + * Return: Number of bytes copied on success, otherwise negative error + * code. copy_flash will copy at most 'size' bytes, but it may + * copy less. + */ +int64_t copy_flash(struct mbox_context *context, uint32_t offset, void *mem, + uint32_t size) +{ + int32_t size_read; + void *start = mem; + + MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n", + mem, size, offset); + if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { + MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, + strerror(errno)); + return -MBOX_R_SYSTEM_ERROR; + } + + do { + size_read = read(context->fds[MTD_FD].fd, mem, + min_u32(CHUNKSIZE, size)); + if (size_read < 0) { + MSG_ERR("Couldn't copy mtd into ram: %s\n", + strerror(errno)); + return -MBOX_R_SYSTEM_ERROR; + } + + size -= size_read; + mem += size_read; + } while (size && size_read); + + return size_read ? mem - start : -MBOX_R_SYSTEM_ERROR; +} + +/* + * write_flash() - Write the flash from a provided buffer + * @context: The mbox context pointer + * @offset: The flash offset to write to (bytes) + * @buf: The buffer to write from (must be of atleast size) + * @size: The number of bytes to write + * + * Return: 0 on success otherwise negative error code + */ +int write_flash(struct mbox_context *context, uint32_t offset, void *buf, + uint32_t count) +{ + uint32_t buf_offset = 0; + int rc; + + MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, buf); + + if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { + MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, + strerror(errno)); + return -MBOX_R_SYSTEM_ERROR; + } + + while (count) { + rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count); + if (rc < 0) { + MSG_ERR("Couldn't write to flash, write lost: %s\n", + strerror(errno)); + return -MBOX_R_WRITE_ERROR; + } + /* Mark *NOT* erased where we just wrote */ + set_flash_bytemap(context, offset + buf_offset, rc, + FLASH_DIRTY); + count -= rc; + buf_offset += rc; + } + + return 0; +} diff --git a/mboxd_flash_physical.c b/mboxd_flash_physical.c deleted file mode 100644 index 5cd5868..0000000 --- a/mboxd_flash_physical.c +++ /dev/null @@ -1,299 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018 IBM Corp. - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mbox.h" -#include "common.h" -#include "mboxd_flash.h" - -int init_flash_dev(struct mbox_context *context) -{ - char *filename = get_dev_mtd(); - int fd, rc = 0; - - if (!filename) { - MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n"); - return -1; - } - - MSG_DBG("Opening %s\n", filename); - - /* Open Flash Device */ - fd = open(filename, O_RDWR); - if (fd < 0) { - MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", - filename, strerror(errno)); - rc = -errno; - goto out; - } - context->fds[MTD_FD].fd = fd; - - /* Read the Flash Info */ - if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) { - MSG_ERR("Couldn't get information about MTD: %s\n", - strerror(errno)); - rc = -1; - goto out; - } - - if (context->flash_size == 0) { - /* - * PNOR images for current OpenPOWER systems are at most 64MB - * despite the PNOR itself sometimes being as big as 128MB. To - * ensure the image read from the PNOR is exposed in the LPC - * address space at the location expected by the host firmware, - * it is required that the image size be used for - * context->flash_size, and not the size of the flash device. - * - * However, the test cases specify the flash size via special - * test APIs (controlling flash behaviour) which don't have - * access to the mbox context. Rather than requiring - * error-prone assignments in every test case, we instead rely - * on context->flash_size being set to the size reported by the - * MEMINFO ioctl(). - * - * As this case should never be hit in production (i.e. outside - * the test environment), log an error. As a consequence, this - * error is expected in the test case output. - */ - MSG_ERR("Flash size MUST be supplied on the commandline. However, continuing by assuming flash is %u bytes\n", - context->mtd_info.size); - context->flash_size = context->mtd_info.size; - } - - /* We know the erase size so we can allocate the flash_erased bytemap */ - context->erase_size_shift = log_2(context->mtd_info.erasesize); - context->flash_bmap = calloc(context->flash_size >> - context->erase_size_shift, - sizeof(*context->flash_bmap)); - MSG_DBG("Flash erase size: 0x%.8x\n", context->mtd_info.erasesize); - -out: - free(filename); - return rc; -} - -void free_flash_dev(struct mbox_context *context) -{ - free(context->flash_bmap); - close(context->fds[MTD_FD].fd); -} - -/* Flash Functions */ - -/* - * flash_is_erased() - Check if an offset into flash is erased - * @context: The mbox context pointer - * @offset: The flash offset to check (bytes) - * - * Return: true if erased otherwise false - */ -static inline bool flash_is_erased(struct mbox_context *context, - uint32_t offset) -{ - return context->flash_bmap[offset >> context->erase_size_shift] - == FLASH_ERASED; -} - -/* - * set_flash_bytemap() - Set the flash erased bytemap - * @context: The mbox context pointer - * @offset: The flash offset to set (bytes) - * @count: Number of bytes to set - * @val: Value to set the bytemap to - * - * The flash bytemap only tracks the erased status at the erase block level so - * this will update the erased state for an (or many) erase blocks - * - * Return: 0 if success otherwise negative error code - */ -int set_flash_bytemap(struct mbox_context *context, uint32_t offset, - uint32_t count, uint8_t val) -{ - if ((offset + count) > context->flash_size) { - return -MBOX_R_PARAM_ERROR; - } - - MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n", - offset, count, val ? "ERASED" : "DIRTY"); - memset(context->flash_bmap + (offset >> context->erase_size_shift), - val, - align_up(count, 1 << context->erase_size_shift) >> - context->erase_size_shift); - - return 0; -} - -/* - * erase_flash() - Erase the flash - * @context: The mbox context pointer - * @offset: The flash offset to erase (bytes) - * @size: The number of bytes to erase - * - * Return: 0 on success otherwise negative error code - */ -int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count) -{ - const uint32_t erase_size = 1 << context->erase_size_shift; - struct erase_info_user erase_info = { 0 }; - int rc; - - MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count); - - /* - * We have an erased_bytemap for the flash so we want to avoid erasing - * blocks which we already know to be erased. Look for runs of blocks - * which aren't erased and erase the entire run at once to avoid how - * often we have to call the erase ioctl. If the block is already - * erased then there's nothing we need to do. - */ - while (count) { - if (!flash_is_erased(context, offset)) { /* Need to erase */ - if (!erase_info.length) { /* Start of not-erased run */ - erase_info.start = offset; - } - erase_info.length += erase_size; - } else if (erase_info.length) { /* Already erased|end of run? */ - /* Erase the previous run which just ended */ - MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", - erase_info.start, erase_info.length); - rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, - &erase_info); - if (rc < 0) { - MSG_ERR("Couldn't erase flash at 0x%.8x\n", - erase_info.start); - return -MBOX_R_SYSTEM_ERROR; - } - /* Mark ERASED where we just erased */ - set_flash_bytemap(context, erase_info.start, - erase_info.length, FLASH_ERASED); - erase_info.start = 0; - erase_info.length = 0; - } - - offset += erase_size; - count -= erase_size; - } - - if (erase_info.length) { - MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", - erase_info.start, erase_info.length); - rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info); - if (rc < 0) { - MSG_ERR("Couldn't erase flash at 0x%.8x\n", - erase_info.start); - return -MBOX_R_SYSTEM_ERROR; - } - /* Mark ERASED where we just erased */ - set_flash_bytemap(context, erase_info.start, erase_info.length, - FLASH_ERASED); - } - - return 0; -} - -#define CHUNKSIZE (64 * 1024) - -/* - * copy_flash() - Copy data from the flash device into a provided buffer - * @context: The mbox context pointer - * @offset: The flash offset to copy from (bytes) - * @mem: The buffer to copy into (must be of atleast 'size' bytes) - * @size: The number of bytes to copy - * Return: Number of bytes copied on success, otherwise negative error - * code. copy_flash will copy at most 'size' bytes, but it may - * copy less. - */ -int64_t copy_flash(struct mbox_context *context, uint32_t offset, void *mem, - uint32_t size) -{ - int32_t size_read; - void *start = mem; - - MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n", - mem, size, offset); - if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { - MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, - strerror(errno)); - return -MBOX_R_SYSTEM_ERROR; - } - - do { - size_read = read(context->fds[MTD_FD].fd, mem, - min_u32(CHUNKSIZE, size)); - if (size_read < 0) { - MSG_ERR("Couldn't copy mtd into ram: %s\n", - strerror(errno)); - return -MBOX_R_SYSTEM_ERROR; - } - - size -= size_read; - mem += size_read; - } while (size && size_read); - - return size_read ? mem - start : -MBOX_R_SYSTEM_ERROR; -} - -/* - * write_flash() - Write the flash from a provided buffer - * @context: The mbox context pointer - * @offset: The flash offset to write to (bytes) - * @buf: The buffer to write from (must be of atleast size) - * @size: The number of bytes to write - * - * Return: 0 on success otherwise negative error code - */ -int write_flash(struct mbox_context *context, uint32_t offset, void *buf, - uint32_t count) -{ - uint32_t buf_offset = 0; - int rc; - - MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, buf); - - if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) { - MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset, - strerror(errno)); - return -MBOX_R_SYSTEM_ERROR; - } - - while (count) { - rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count); - if (rc < 0) { - MSG_ERR("Couldn't write to flash, write lost: %s\n", - strerror(errno)); - return -MBOX_R_WRITE_ERROR; - } - /* Mark *NOT* erased where we just wrote */ - set_flash_bytemap(context, offset + buf_offset, rc, - FLASH_DIRTY); - count -= rc; - buf_offset += rc; - } - - return 0; -} diff --git a/mboxd_flash_virtual.cpp b/mboxd_flash_virtual.cpp deleted file mode 100644 index ffe75c6..0000000 --- a/mboxd_flash_virtual.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018 IBM Corp. - -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { -#include "common.h" -} - -#include "config.h" -#include "mboxd_flash.h" -#include "mboxd_pnor_partition_table.h" -#include "pnor_partition.hpp" -#include "pnor_partition_table.hpp" -#include "xyz/openbmc_project/Common/error.hpp" -#include -#include - -#include -#include -#include -#include - -namespace err = sdbusplus::xyz::openbmc_project::Common::Error; -namespace fs = std::experimental::filesystem; -namespace vpnor = openpower::virtual_pnor; - -/** @brief unique_ptr functor to release a char* reference. */ -struct StringDeleter -{ - void operator()(char* ptr) const - { - free(ptr); - } -}; -using StringPtr = std::unique_ptr; - -int init_flash_dev(struct mbox_context* context) -{ - StringPtr filename(get_dev_mtd()); - int fd = 0; - int rc = 0; - - if (!filename) - { - MSG_ERR("Couldn't find the flash /dev/mtd partition\n"); - return -1; - } - - MSG_DBG("Opening %s\n", filename.get()); - - fd = open(filename.get(), O_RDWR); - if (fd < 0) - { - MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename.get(), - strerror(errno)); - return -errno; - } - - // Read the Flash Info - if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) - { - MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno)); - close(fd); - return -errno; - } - - if (context->flash_size == 0) - { - // See comment in mboxd_flash_physical.c on why - // this is needed. - context->flash_size = context->mtd_info.size; - } - - // Hostboot requires a 4K block-size to be used in the FFS flash structure - context->mtd_info.erasesize = 4096; - context->erase_size_shift = log_2(context->mtd_info.erasesize); - context->flash_bmap = NULL; - context->fds[MTD_FD].fd = -1; - - close(fd); - return rc; -} - -void free_flash_dev(struct mbox_context* context) -{ - // No-op -} - -int set_flash_bytemap(struct mbox_context* context, uint32_t offset, - uint32_t count, uint8_t val) -{ - // No-op - return 0; -} - -int erase_flash(struct mbox_context* context, uint32_t offset, uint32_t count) -{ - // No-op - return 0; -} - -/* - * copy_flash() - Copy data from the virtual pnor into a provided buffer - * @context: The mbox context pointer - * @offset: The pnor offset to copy from (bytes) - * @mem: The buffer to copy into (must be of atleast 'size' bytes) - * @size: The number of bytes to copy - * Return: Number of bytes copied on success, otherwise negative error - * code. copy_flash will copy at most 'size' bytes, but it may - * copy less. - */ -int64_t copy_flash(struct mbox_context* context, uint32_t offset, void* mem, - uint32_t size) -{ - vpnor::partition::Table* table; - int rc = size; - - if (!(context && context->vpnor && context->vpnor->table)) - { - MSG_ERR("Trying to copy data with uninitialised context!\n"); - return -MBOX_R_SYSTEM_ERROR; - } - - table = context->vpnor->table; - - MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem, - size, offset); - - /* The virtual PNOR partition table starts at offset 0 in the virtual - * pnor image. Check if host asked for an offset that lies within the - * partition table. - */ - size_t sz = table->size(); - if (offset < sz) - { - const pnor_partition_table& toc = table->getHostTable(); - rc = std::min(sz - offset, static_cast(size)); - memcpy(mem, ((uint8_t*)&toc) + offset, rc); - return rc; - } - - try - { - vpnor::Request req(context, offset); - rc = req.read(mem, size); - } - catch (vpnor::UnmappedOffset& e) - { - /* - * Hooo boy. Pretend that this is valid flash so we don't have - * discontiguous regions presented to the host. Instead, fill a window - * with 0xff so the 'flash' looks erased. Writes to such regions are - * dropped on the floor, see the implementation of write_flash() below. - */ - MSG_INFO("Host requested unmapped region of %" PRId32 - " bytes at offset 0x%" PRIx32 "\n", - size, offset); - uint32_t span = e.next - e.base; - rc = std::min(size, span); - memset(mem, 0xff, rc); - } - catch (std::exception& e) - { - MSG_ERR("%s\n", e.what()); - phosphor::logging::commit(); - rc = -MBOX_R_SYSTEM_ERROR; - } - return rc; -} - -/* - * write_flash() - Write to the virtual pnor from a provided buffer - * @context: The mbox context pointer - * @offset: The flash offset to write to (bytes) - * @buf: The buffer to write from (must be of atleast size) - * @size: The number of bytes to write - * - * Return: 0 on success otherwise negative error code - */ - -int write_flash(struct mbox_context* context, uint32_t offset, void* buf, - uint32_t count) -{ - - if (!(context && context->vpnor && context->vpnor->table)) - { - MSG_ERR("Trying to write data with uninitialised context!\n"); - return -MBOX_R_SYSTEM_ERROR; - } - - vpnor::partition::Table* table = context->vpnor->table; - - try - { - const struct pnor_partition& part = table->partition(offset); - if (part.data.user.data[1] & PARTITION_READONLY) - { - /* FIXME: This should be done on CREATE_WRITE_WINDOW, not here */ - return -MBOX_R_WRITE_ERROR; - } - - MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, - buf); - vpnor::Request req(context, offset); - req.write(buf, count); - } - catch (vpnor::UnmappedOffset& e) - { - /* Paper over the fact that the write isn't persistent */ - MSG_INFO("Dropping %d bytes host wrote to unmapped offset 0x%" PRIx32 - "\n", - count, offset); - return 0; - } - catch (const vpnor::OutOfBoundsOffset& e) - { - MSG_ERR("%s\n", e.what()); - return -MBOX_R_PARAM_ERROR; - } - catch (const std::exception& e) - { - MSG_ERR("%s\n", e.what()); - phosphor::logging::commit(); - return -MBOX_R_SYSTEM_ERROR; - } - return 0; -} diff --git a/mboxd_lpc_physical.c b/mboxd_lpc_physical.c deleted file mode 100644 index 5f5dc01..0000000 --- a/mboxd_lpc_physical.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Mailbox Daemon LPC Helpers - * - * Copyright 2017 IBM - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#define _GNU_SOURCE - -#include "mbox.h" -#include "mboxd_lpc.h" - -/* - * reset_lpc() - Reset the lpc bus mapping - * @context: The mbox context pointer - * - * Return: 0 on success otherwise negative error code - */ -int reset_lpc(struct mbox_context *context) -{ - return point_to_flash(context); -} diff --git a/mboxd_lpc_reset.c b/mboxd_lpc_reset.c new file mode 100644 index 0000000..5f5dc01 --- /dev/null +++ b/mboxd_lpc_reset.c @@ -0,0 +1,34 @@ +/* + * Mailbox Daemon LPC Helpers + * + * Copyright 2017 IBM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define _GNU_SOURCE + +#include "mbox.h" +#include "mboxd_lpc.h" + +/* + * reset_lpc() - Reset the lpc bus mapping + * @context: The mbox context pointer + * + * Return: 0 on success otherwise negative error code + */ +int reset_lpc(struct mbox_context *context) +{ + return point_to_flash(context); +} diff --git a/mboxd_lpc_virtual.cpp b/mboxd_lpc_virtual.cpp deleted file mode 100644 index 60ce0d3..0000000 --- a/mboxd_lpc_virtual.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Mailbox Daemon LPC Helpers - * - * Copyright 2017 IBM - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "mbox.h" -#include "mboxd_lpc.h" -#include "mboxd_pnor_partition_table.h" - -/* - * reset_lpc() - Reset the lpc bus mapping - * @context: The mbox context pointer - * - * Return 0 on success otherwise negative error code - */ -int reset_lpc(struct mbox_context *context) -{ - destroy_vpnor(context); - init_vpnor(context); - vpnor_copy_bootloader_partition(context); - return point_to_memory(context); -} diff --git a/mboxd_pnor_partition_table.cpp b/mboxd_pnor_partition_table.cpp deleted file mode 100644 index db53dc8..0000000 --- a/mboxd_pnor_partition_table.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018 IBM Corp. -#include "mboxd_pnor_partition_table.h" -#include "pnor_partition_table.hpp" -#include "common.h" -#include "mbox.h" -#include "mboxd_flash.h" -#include "pnor_partition_table.hpp" -#include "config.h" -#include "xyz/openbmc_project/Common/error.hpp" -#include -#include - -int init_vpnor(struct mbox_context *context) -{ - if (context && !context->vpnor) - { - int rc; - - strncpy(context->paths.ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX); - context->paths.ro_loc[PATH_MAX - 1] = '\0'; - strncpy(context->paths.rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX); - context->paths.rw_loc[PATH_MAX - 1] = '\0'; - strncpy(context->paths.prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX); - context->paths.prsv_loc[PATH_MAX - 1] = '\0'; - strncpy(context->paths.patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX); - context->paths.prsv_loc[PATH_MAX - 1] = '\0'; - - rc = init_vpnor_from_paths(context); - if (rc < 0) - { - return rc; - } - } - - return 0; -} - -int init_vpnor_from_paths(struct mbox_context *context) -{ - namespace err = sdbusplus::xyz::openbmc_project::Common::Error; - namespace fs = std::experimental::filesystem; - namespace vpnor = openpower::virtual_pnor; - - if (context && !context->vpnor) - { - try - { - context->vpnor = new vpnor_partition_table; - context->vpnor->table = - new openpower::virtual_pnor::partition::Table(context); - } - catch (vpnor::TocEntryError &e) - { - MSG_ERR("%s\n", e.what()); - phosphor::logging::commit(); - return -MBOX_R_SYSTEM_ERROR; - } - } - - return 0; -} - -int vpnor_copy_bootloader_partition(const struct mbox_context *context) -{ - // The hostboot bootloader has certain size/offset assumptions, so - // we need a special partition table here. - // It assumes the PNOR is 64M, the TOC size is 32K, the erase block is - // 4K, the page size is 4K. - // It also assumes the TOC is at the 'end of pnor - toc size - 1 page size' - // offset, and first looks for the TOC here, before proceeding to move up - // page by page looking for the TOC. So it is optimal to place the TOC at - // this offset. - constexpr size_t eraseSize = 0x1000; - constexpr size_t pageSize = 0x1000; - constexpr size_t pnorSize = 0x4000000; - constexpr size_t tocMaxSize = 0x8000; - constexpr size_t tocStart = pnorSize - tocMaxSize - pageSize; - constexpr auto blPartitionName = "HBB"; - - namespace err = sdbusplus::xyz::openbmc_project::Common::Error; - namespace fs = std::experimental::filesystem; - namespace vpnor = openpower::virtual_pnor; - - try - { - vpnor_partition_table vtbl{}; - struct mbox_context local = *context; - local.vpnor = &vtbl; - local.block_size_shift = log_2(eraseSize); - - openpower::virtual_pnor::partition::Table blTable(&local); - - vtbl.table = &blTable; - - size_t tocOffset = 0; - - // Copy TOC - copy_flash(&local, tocOffset, - static_cast(context->mem) + tocStart, - blTable.capacity()); - const pnor_partition &partition = blTable.partition(blPartitionName); - size_t hbbOffset = partition.data.base * eraseSize; - uint32_t hbbSize = partition.data.actual; - // Copy HBB - copy_flash(&local, hbbOffset, - static_cast(context->mem) + hbbOffset, hbbSize); - } - catch (err::InternalFailure &e) - { - phosphor::logging::commit(); - return -MBOX_R_SYSTEM_ERROR; - } - catch (vpnor::TocEntryError &e) - { - MSG_ERR("%s\n", e.what()); - phosphor::logging::commit(); - return -MBOX_R_SYSTEM_ERROR; - } - - return 0; -} - -void destroy_vpnor(struct mbox_context *context) -{ - if (context && context->vpnor) - { - delete context->vpnor->table; - delete context->vpnor; - context->vpnor = nullptr; - } -} diff --git a/mboxd_pnor_partition_table.h b/mboxd_pnor_partition_table.h deleted file mode 100644 index d13a2d2..0000000 --- a/mboxd_pnor_partition_table.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* Copyright (C) 2018 IBM Corp. */ -#pragma once - -#ifdef VIRTUAL_PNOR_ENABLED - -#include -#include "pnor_partition_defs.h" - -struct mbox_context; -struct vpnor_partition_table; - -struct vpnor_partition_paths -{ - char ro_loc[PATH_MAX]; - char rw_loc[PATH_MAX]; - char prsv_loc[PATH_MAX]; - char patch_loc[PATH_MAX]; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/** @brief Create a virtual PNOR partition table. - * - * @param[in] context - mbox context pointer - * - * This API should be called before calling any other APIs below. If a table - * already exists, this function will not do anything further. This function - * will not do anything if the context is NULL. - * - * Returns 0 if the call succeeds, else a negative error code. - */ -int init_vpnor(struct mbox_context *context); - -/** @brief Create a virtual PNOR partition table. - * - * @param[in] context - mbox context pointer - * - * This API is same as above one but requires context->path is initialised - * with all the necessary paths. - * - * Returns 0 if the call succeeds, else a negative error code. - */ - -int init_vpnor_from_paths(struct mbox_context *context); - -/** @brief Copy bootloader partition (alongwith TOC) to LPC memory - * - * @param[in] context - mbox context pointer - * - * @returns 0 on success, negative error code on failure - */ -int vpnor_copy_bootloader_partition(const struct mbox_context *context); - -/** @brief Destroy partition table, if it exists. - * - * @param[in] context - mbox context pointer - */ -void destroy_vpnor(struct mbox_context *context); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/pnor_partition.cpp b/pnor_partition.cpp deleted file mode 100644 index 84aeb3f..0000000 --- a/pnor_partition.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018 IBM Corp. -#include "pnor_partition.hpp" -#include "pnor_partition_table.hpp" -#include "config.h" -#include "mboxd_flash.h" -#include "mboxd_pnor_partition_table.h" -#include "xyz/openbmc_project/Common/error.hpp" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -#include -#include -#include -#include - -namespace openpower -{ -namespace virtual_pnor -{ - -namespace fs = std::experimental::filesystem; - -fs::path Request::getPartitionFilePath(int flags) -{ - // Check if partition exists in patch location - auto dst = fs::path(ctx->paths.patch_loc) / partition.data.name; - if (fs::is_regular_file(dst)) - { - return dst; - } - - switch (partition.data.user.data[1] & - (PARTITION_PRESERVED | PARTITION_READONLY)) - { - case PARTITION_PRESERVED: - dst = ctx->paths.prsv_loc; - break; - - case PARTITION_READONLY: - dst = ctx->paths.ro_loc; - break; - - default: - dst = ctx->paths.rw_loc; - } - dst /= partition.data.name; - - if (fs::exists(dst)) - { - return dst; - } - - if (flags == O_RDONLY) - { - dst = fs::path(ctx->paths.ro_loc) / partition.data.name; - assert(fs::exists(dst)); - return dst; - } - - assert(flags == O_RDWR); - auto src = fs::path(ctx->paths.ro_loc) / partition.data.name; - assert(fs::exists(src)); - - MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n", - partition.data.name, dst.c_str(), src.c_str()); - - dst = ctx->paths.rw_loc; - if (partition.data.user.data[1] & PARTITION_PRESERVED) - { - dst = ctx->paths.prsv_loc; - } - - dst /= partition.data.name; - fs::copy_file(src, dst); - - return dst; -} - -size_t Request::clamp(size_t len) -{ - size_t maxAccess = offset + len; - size_t partSize = partition.data.size << ctx->block_size_shift; - return std::min(maxAccess, partSize) - offset; -} - -void Request::resize(const fs::path &path, size_t len) -{ - size_t maxAccess = offset + len; - size_t fileSize = fs::file_size(path); - if (maxAccess < fileSize) - { - return; - } - MSG_DBG("Resizing %s to %zu bytes\n", path.c_str(), maxAccess); - int rc = truncate(path.c_str(), maxAccess); - if (rc == -1) - { - MSG_ERR("Failed to resize %s: %d\n", path.c_str(), errno); - throw std::system_error(errno, std::system_category()); - } -} - -size_t Request::fulfil(const fs::path &path, int flags, void *buf, size_t len) -{ - if (!(flags == O_RDONLY || flags == O_RDWR)) - { - std::stringstream err; - err << "Require O_RDONLY (0x" << std::hex << O_RDONLY << " or O_RDWR " - << std::hex << O_RDWR << " for flags, got: 0x" << std::hex << flags; - throw std::invalid_argument(err.str()); - } - - int fd = ::open(path.c_str(), flags); - if (fd == -1) - { - MSG_ERR("Failed to open backing file at '%s': %d\n", - path.c_str(), errno); - throw std::system_error(errno, std::system_category()); - } - - size_t fileSize = fs::file_size(path); - int mprot = PROT_READ | ((flags == O_RDWR) ? PROT_WRITE : 0); - auto map = mmap(NULL, fileSize, mprot, MAP_SHARED, fd, 0); - if (map == MAP_FAILED) - { - close(fd); - MSG_ERR("Failed to map backing file '%s' for %zd bytes: %d\n", - path.c_str(), fileSize, errno); - throw std::system_error(errno, std::system_category()); - } - - // copy to the reserved memory area - if (flags == O_RDONLY) - { - memset(buf, 0xff, len); - memcpy(buf, (char *)map + offset, std::min(len, fileSize)); - } - else - { - memcpy((char *)map + offset, buf, len); - set_flash_bytemap(ctx, base + offset, len, FLASH_DIRTY); - } - munmap(map, fileSize); - close(fd); - - return len; -} - -} // namespace virtual_pnor -} // namespace openpower diff --git a/pnor_partition.hpp b/pnor_partition.hpp deleted file mode 100644 index eb63a39..0000000 --- a/pnor_partition.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* Copyright (C) 2018 IBM Corp. */ -#pragma once - -extern "C" { -#include "mbox.h" -}; - -#include "mboxd_pnor_partition_table.h" -#include "pnor_partition_table.hpp" -#include -#include -#include -#include - -namespace openpower -{ -namespace virtual_pnor -{ - -namespace fs = std::experimental::filesystem; - -class Request -{ - public: - /** @brief Construct a flash access request - * - * @param[in] ctx - The mbox context used to process the request - * @param[in] offset - The absolute offset into the flash device as - * provided by the mbox message associated with the - * request - * - * The class does not take ownership of the ctx pointer. The lifetime of - * the ctx pointer must strictly exceed the lifetime of the class - * instance. - */ - Request(struct mbox_context* ctx, size_t offset) : - ctx(ctx), partition(ctx->vpnor->table->partition(offset)), - base(partition.data.base << ctx->block_size_shift), - offset(offset - base) - { - } - Request(const Request&) = delete; - Request& operator=(const Request&) = delete; - Request(Request&&) = default; - Request& operator=(Request&&) = default; - ~Request() = default; - - ssize_t read(void* dst, size_t len) - { - len = clamp(len); - constexpr auto flags = O_RDONLY; - fs::path path = getPartitionFilePath(flags); - return fulfil(path, flags, dst, len); - } - - ssize_t write(void* dst, size_t len) - { - if (len != clamp(len)) - { - std::stringstream err; - err << "Request size 0x" << std::hex << len << " from offset 0x" - << std::hex << offset << " exceeds the partition size 0x" - << std::hex << (partition.data.size << ctx->block_size_shift); - throw OutOfBoundsOffset(err.str()); - } - constexpr auto flags = O_RDWR; - /* Ensure file is at least the size of the maximum access */ - fs::path path = getPartitionFilePath(flags); - resize(path, len); - return fulfil(path, flags, dst, len); - } - - private: - /** @brief Clamp the access length to the maximum supported by the ToC */ - size_t clamp(size_t len); - - /** @brief Ensure the backing file is sized appropriately for the access - * - * We need to ensure the file is big enough to satisfy the request so that - * mmap() will succeed for the required size. - * - * @return The valid access length - * - * Throws: std::system_error - */ - void resize(const std::experimental::filesystem::path& path, size_t len); - - /** @brief Returns the partition file path associated with the offset. - * - * The search strategy for the partition file depends on the value of the - * flags parameter. - * - * For the O_RDONLY case: - * - * 1. Depending on the partition type,tries to open the file - * from the associated partition(RW/PRSV/RO). - * 1a. if file not found in the corresponding - * partition(RW/PRSV/RO) then tries to read the file from - * the read only partition. - * 1b. if the file not found in the read only partition then - * throw exception. - * - * For the O_RDWR case: - * - * 1. Depending on the partition type tries to open the file - * from the associated partition. - * 1a. if file not found in the corresponding partition(RW/PRSV) - * then copy the file from the read only partition to the (RW/PRSV) - * partition depending on the partition type. - * 1b. if the file not found in the read only partition then throw - * exception. - * - * @param[in] flags - The flags that will be used to open the file. Must - * be one of O_RDONLY or O_RDWR. - * - * Post-condition: The file described by the returned path exists - * - * Throws: std::filesystem_error, std::bad_alloc - */ - std::experimental::filesystem::path getPartitionFilePath(int flags); - - /** @brief Fill dst with the content of the partition relative to offset. - * - * @param[in] offset - The pnor offset(bytes). - * @param[out] dst - The buffer to fill with partition data - * @param[in] len - The length of the destination buffer - */ - size_t fulfil(const std::experimental::filesystem::path& path, int flags, - void* dst, size_t len); - - struct mbox_context* ctx; - const pnor_partition& partition; - size_t base; - size_t offset; -}; - -} // namespace virtual_pnor -} // namespace openpower diff --git a/pnor_partition_defs.h b/pnor_partition_defs.h deleted file mode 100644 index 447b11c..0000000 --- a/pnor_partition_defs.h +++ /dev/null @@ -1,136 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* Copyright (C) 2018 IBM Corp. */ -#pragma once - -#include -#include - -/* There are two structures outlined here - one that represents the PNOR - * partition table (or header) - this appears first in the PNOR image. - * The last field of the PNOR partition table structure is an array - * of another structure - which represents the partition. - * - * The flash structures used here have been borrowed from - * https://github.com/open-power/hostboot/blob/master/src/usr/pnor/ffs.h */ - - -/* The maximum length of a partition's name */ -#define PARTITION_NAME_MAX 15 - -/* The version of this partition implementation. This is an - * incrementing value */ -#define PARTITION_VERSION_1 1 - -/* Magic number for the partition partition_table (ASCII 'PART') */ -#define PARTITION_HEADER_MAGIC 0x50415254 - -/* Default parent partition id */ -#define PARENT_PATITION_ID 0xFFFFFFFF - -/* The partition structure has 16 'user data' words, which can be used to store - * miscellaneous information. This is typically used to store bits that state - * whether a partition is ECC protected, is read-only, is preserved across - * updates, etc. - * - * TODO: Replace with libflash (!) or at least refactor the data structures to - * better match hostboot's layout[1]. The latter would avoid the headache of - * verifying these flags match the expected functionality (taking into account - * changes in endianness). - * - * [1] https://github.com/open-power/hostboot/blob/9acfce99596f12dcc60952f8506a77e542609cbf/src/usr/pnor/common/ffs_hb.H#L81 - */ -#define PARTITION_USER_WORDS 16 -#define PARTITION_ECC_PROTECTED 0x8000 -#define PARTITION_PRESERVED 0x00800000 -#define PARTITION_READONLY 0x00400000 -#define PARTITION_REPROVISION 0x00100000 -#define PARTITION_VOLATILE 0x00080000 -#define PARTITION_CLEARECC 0x00040000 -#define PARTITION_VERSION_CHECK_SHA512 0x80000000 -#define PARTITION_VERSION_CHECK_SHA512_PER_EC 0x40000000 - -/* Partition flags */ -enum partition_flags { - PARTITION_FLAGS_PROTECTED = 0x0001, - PARTITION_FLAGS_U_BOOT_ENV = 0x0002 -}; - -/* Type of image contained within partition */ -enum partition_type { - PARTITION_TYPE_DATA = 1, - PARTITION_TYPE_LOGICAL = 2, - PARTITION_TYPE_PARTITION = 3 -}; - - -/** - * struct pnor_partition - * - * @name: Name of the partition - a null terminated string - * @base: The offset in the PNOR, in block-size (1 block = 4KB), - * where this partition is placed - * @size: Partition size in blocks. - * @pid: Parent partition id - * @id: Partition ID [1..65536] - * @type: Type of partition, see the 'type' enum - * @flags: Partition flags (optional), see the 'flags' enum - * @actual: Actual partition size (in bytes) - * @resvd: Reserved words for future use - * @user: User data (optional), see user data macros above - * @checksum: Partition checksum (includes all words above) - the - * checksum is obtained by a XOR operation on all of the - * words above. This is used for detecting a corruption - * in this structure - */ -struct pnor_partition { - struct { - char name[PARTITION_NAME_MAX + 1]; - uint32_t base; - uint32_t size; - uint32_t pid; - uint32_t id; - uint32_t type; - uint32_t flags; - uint32_t actual; - uint32_t resvd[4]; - struct - { - uint32_t data[PARTITION_USER_WORDS]; - } user; - } __attribute__ ((packed)) data; - uint32_t checksum; -} __attribute__ ((packed)); - -/** - * struct pnor_partition_table - * - * @magic: Eye catcher/corruption detector - set to - * PARTITION_HEADER_MAGIC - * @version: Version of the structure, set to - * PARTITION_VERSION_1 - * @size: Size of partition table (in blocks) - * @entry_size: Size of struct pnor_partition element (in bytes) - * @entry_count: Number of struct pnor_partition elements in partitions array - * @block_size: Size of an erase-block on the PNOR (in bytes) - * @block_count: Number of blocks on the PNOR - * @resvd: Reserved words for future use - * @checksum: Header checksum (includes all words above) - the - * checksum is obtained by a XOR operation on all of the - * words above. This is used for detecting a corruption - * in this structure - * @partitions: Array of struct pnor_partition - */ -struct pnor_partition_table { - struct { - uint32_t magic; - uint32_t version; - uint32_t size; - uint32_t entry_size; - uint32_t entry_count; - uint32_t block_size; - uint32_t block_count; - uint32_t resvd[4]; - } __attribute__ ((packed)) data; - uint32_t checksum; - struct pnor_partition partitions[]; -} __attribute__ ((packed)); diff --git a/pnor_partition_table.cpp b/pnor_partition_table.cpp deleted file mode 100644 index 4ad07bc..0000000 --- a/pnor_partition_table.cpp +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2018 IBM Corp. -#include "pnor_partition_table.hpp" -#include "common.h" -#include "config.h" -#include "xyz/openbmc_project/Common/error.hpp" -#include -#include -#include -#include -#include -#include - -namespace openpower -{ -namespace virtual_pnor -{ - -using namespace phosphor::logging; -using namespace sdbusplus::xyz::openbmc_project::Common::Error; - -namespace partition -{ - -Table::Table(const struct mbox_context* ctx) : - szBytes(sizeof(pnor_partition_table)), numParts(0), - blockSize(1 << ctx->erase_size_shift), pnorSize(ctx->flash_size) -{ - preparePartitions(ctx); - prepareHeader(); - hostTbl = endianFixup(tbl); -} - -void Table::prepareHeader() -{ - decltype(auto) table = getNativeTable(); - table.data.magic = PARTITION_HEADER_MAGIC; - table.data.version = PARTITION_VERSION_1; - table.data.size = blocks(); - table.data.entry_size = sizeof(pnor_partition); - table.data.entry_count = numParts; - table.data.block_size = blockSize; - table.data.block_count = pnorSize / blockSize; - table.checksum = details::checksum(table.data); -} - -inline void Table::allocateMemory(const fs::path& tocFile) -{ - size_t num = 0; - std::string line; - std::ifstream file(tocFile.c_str()); - - // Find number of lines in partition file - this will help - // determine the number of partitions and hence also how much - // memory to allocate for the partitions array. - // The actual number of partitions may turn out to be lesser than this, - // in case of errors. - while (std::getline(file, line)) - { - // Check if line starts with "partition" - if (std::string::npos != line.find("partition", 0)) - { - ++num; - } - } - - szBytes = sizeof(pnor_partition_table) + (num * sizeof(pnor_partition)); - tbl.resize(capacity()); -} - -void Table::preparePartitions(const struct mbox_context* ctx) -{ - const fs::path roDir = ctx->paths.ro_loc; - const fs::path patchDir = ctx->paths.patch_loc; - fs::path tocFile = roDir / PARTITION_TOC_FILE; - allocateMemory(tocFile); - - std::ifstream file(tocFile.c_str()); - std::string line; - decltype(auto) table = getNativeTable(); - - while (std::getline(file, line)) - { - pnor_partition& part = table.partitions[numParts]; - fs::path patch; - fs::path file; - - // The ToC file presented in the vpnor squashfs looks like: - // - // version=IBM-witherspoon-ibm-OP9_v1.19_1.135 - // extended_version=op-build-v1.19-571-g04f4690-dirty,buildroot-2017.11-5-g65679be,skiboot-v5.10-rc4,hostboot-4c46d66,linux-4.14.20-openpower1-p4a6b675,petitboot-v1.6.6-pe5aaec2,machine-xml-0fea226,occ-3286b6b,hostboot-binaries-3d1af8f,capp-ucode-p9-dd2-v3,sbe-99e2fe2 - // partition00=part,0x00000000,0x00002000,00,READWRITE - // partition01=HBEL,0x00008000,0x0002c000,00,ECC,REPROVISION,CLEARECC,READWRITE - // ... - // - // As such we want to skip any lines that don't begin with 'partition' - if (std::string::npos == line.find("partition", 0)) - { - continue; - } - - parseTocLine(line, blockSize, part); - - if (numParts > 0) - { - struct pnor_partition& prev = table.partitions[numParts - 1]; - uint32_t prev_end = prev.data.base + prev.data.size; - - if (part.data.id == prev.data.id) - { - MSG_ERR("ID for previous partition '%s' at block 0x%" PRIx32 - "matches current partition '%s' at block 0x%" PRIx32 - ": %" PRId32 "\n", - prev.data.name, prev.data.base, part.data.name, - part.data.base, part.data.id); - } - - if (part.data.base < prev_end) - { - std::stringstream err; - err << "Partition '" << part.data.name << "' start block 0x" - << std::hex << part.data.base << "is less than the end " - << "block 0x" << std::hex << prev_end << " of '" - << prev.data.name << "'"; - throw InvalidTocEntry(err.str()); - } - } - - file = roDir / part.data.name; - if (!fs::exists(file)) - { - std::stringstream err; - err << "Partition file " << file.native() << " does not exist"; - throw InvalidTocEntry(err.str()); - } - - patch = patchDir / part.data.name; - if (fs::is_regular_file(patch)) - { - const size_t size = part.data.size * blockSize; - part.data.actual = - std::min(size, static_cast(fs::file_size(patch))); - } - - ++numParts; - } -} - -const pnor_partition& Table::partition(size_t offset) const -{ - const decltype(auto) table = getNativeTable(); - size_t blockOffset = offset / blockSize; - - for (decltype(numParts) i{}; i < numParts; ++i) - { - const struct pnor_partition& part = table.partitions[i]; - size_t len = part.data.size; - - if ((blockOffset >= part.data.base) && - (blockOffset < (part.data.base + len))) - { - return part; - } - - /* Are we in a hole between partitions? */ - if (blockOffset < part.data.base) - { - throw UnmappedOffset(offset, part.data.base * blockSize); - } - } - - throw UnmappedOffset(offset, pnorSize); -} - -const pnor_partition& Table::partition(const std::string& name) const -{ - const decltype(auto) table = getNativeTable(); - - for (decltype(numParts) i{}; i < numParts; ++i) - { - if (name == table.partitions[i].data.name) - { - return table.partitions[i]; - } - } - - std::stringstream err; - err << "Partition " << name << " is not listed in the table of contents"; - throw UnknownPartition(err.str()); -} - -} // namespace partition - -PartitionTable endianFixup(const PartitionTable& in) -{ - PartitionTable out; - out.resize(in.size()); - auto src = reinterpret_cast(in.data()); - auto dst = reinterpret_cast(out.data()); - - dst->data.magic = htobe32(src->data.magic); - dst->data.version = htobe32(src->data.version); - dst->data.size = htobe32(src->data.size); - dst->data.entry_size = htobe32(src->data.entry_size); - dst->data.entry_count = htobe32(src->data.entry_count); - dst->data.block_size = htobe32(src->data.block_size); - dst->data.block_count = htobe32(src->data.block_count); - dst->checksum = details::checksum(dst->data); - - for (decltype(src->data.entry_count) i{}; i < src->data.entry_count; ++i) - { - auto psrc = &src->partitions[i]; - auto pdst = &dst->partitions[i]; - strncpy(pdst->data.name, psrc->data.name, PARTITION_NAME_MAX); - // Just to be safe - pdst->data.name[PARTITION_NAME_MAX] = '\0'; - pdst->data.base = htobe32(psrc->data.base); - pdst->data.size = htobe32(psrc->data.size); - pdst->data.pid = htobe32(psrc->data.pid); - pdst->data.id = htobe32(psrc->data.id); - pdst->data.type = htobe32(psrc->data.type); - pdst->data.flags = htobe32(psrc->data.flags); - pdst->data.actual = htobe32(psrc->data.actual); - for (size_t j = 0; j < PARTITION_USER_WORDS; ++j) - { - pdst->data.user.data[j] = htobe32(psrc->data.user.data[j]); - } - pdst->checksum = details::checksum(pdst->data); - } - - return out; -} - -static inline void writeSizes(pnor_partition& part, size_t start, size_t end, - size_t blockSize) -{ - size_t size = end - start; - part.data.base = align_up(start, blockSize) / blockSize; - size_t sizeInBlocks = align_up(size, blockSize) / blockSize; - part.data.size = sizeInBlocks; - part.data.actual = size; -} - -static inline void writeUserdata(pnor_partition& part, uint32_t version, - const std::string& data) -{ - std::istringstream stream(data); - std::string flag{}; - auto perms = 0; - auto state = 0; - - MSG_DBG("Parsing ToC flags '%s'\n", data.c_str()); - while (std::getline(stream, flag, ',')) - { - if (flag == "") - continue; - - if (flag == "ECC") - { - state |= PARTITION_ECC_PROTECTED; - } - else if (flag == "READONLY") - { - perms |= PARTITION_READONLY; - } - else if (flag == "READWRITE") - { - perms &= ~PARTITION_READONLY; - } - else if (flag == "PRESERVED") - { - perms |= PARTITION_PRESERVED; - } - else if (flag == "REPROVISION") - { - perms |= PARTITION_REPROVISION; - } - else if (flag == "VOLATILE") - { - perms |= PARTITION_VOLATILE; - } - else if (flag == "CLEARECC") - { - perms |= PARTITION_CLEARECC; - } - else - { - MSG_INFO("Found unimplemented partition property: %s\n", - flag.c_str()); - } - } - - part.data.user.data[0] = state; - part.data.user.data[1] = perms; - part.data.user.data[1] |= version; -} - -static inline void writeDefaults(pnor_partition& part) -{ - part.data.pid = PARENT_PATITION_ID; - part.data.type = PARTITION_TYPE_DATA; - part.data.flags = 0; // flags unused -} - -static inline void writeNameAndId(pnor_partition& part, std::string&& name, - const std::string& id) -{ - name.resize(PARTITION_NAME_MAX); - memcpy(part.data.name, name.c_str(), sizeof(part.data.name)); - part.data.id = std::stoul(id); -} - -void parseTocLine(const std::string& line, size_t blockSize, - pnor_partition& part) -{ - static constexpr auto ID_MATCH = 1; - static constexpr auto NAME_MATCH = 2; - static constexpr auto START_ADDR_MATCH = 4; - static constexpr auto END_ADDR_MATCH = 6; - static constexpr auto VERSION_MATCH = 8; - constexpr auto versionShift = 24; - - // Parse PNOR toc (table of contents) file, which has lines like : - // partition01=HBB,0x00010000,0x000a0000,0x80,ECC,PRESERVED, to indicate - // partition information - std::regex regex{ - "^partition([0-9]+)=([A-Za-z0-9_]+)," - "(0x)?([0-9a-fA-F]+),(0x)?([0-9a-fA-F]+),(0x)?([A-Fa-f0-9]{2})", - std::regex::extended}; - - std::smatch match; - if (!std::regex_search(line, match, regex)) - { - std::stringstream err; - err << "Malformed partition description: " << line.c_str() << "\n"; - throw MalformedTocEntry(err.str()); - } - - writeNameAndId(part, match[NAME_MATCH].str(), match[ID_MATCH].str()); - writeDefaults(part); - - unsigned long start = - std::stoul(match[START_ADDR_MATCH].str(), nullptr, 16); - if (start & (blockSize - 1)) - { - MSG_ERR("Start offset 0x%lx for partition '%s' is not aligned to block " - "size 0x%zx\n", - start, match[NAME_MATCH].str().c_str(), blockSize); - } - - unsigned long end = std::stoul(match[END_ADDR_MATCH].str(), nullptr, 16); - if ((end - start) & (blockSize - 1)) - { - MSG_ERR("Partition '%s' has a size 0x%lx that is not aligned to block " - "size 0x%zx\n", - match[NAME_MATCH].str().c_str(), (end - start), blockSize); - } - - if (start >= end) - { - std::stringstream err; - err << "Partition " << match[NAME_MATCH].str() - << " has an invalid range: start offset (0x" << std::hex << start - << " is beyond open end (0x" << std::hex << end << ")\n"; - throw InvalidTocEntry(err.str()); - } - writeSizes(part, start, end, blockSize); - - // Use the shift to convert "80" to 0x80000000 - unsigned long version = std::stoul(match[VERSION_MATCH].str(), nullptr, 16); - writeUserdata(part, version << versionShift, match.suffix().str()); - part.checksum = details::checksum(part.data); -} - -} // namespace virtual_pnor -} // namespace openpower diff --git a/pnor_partition_table.hpp b/pnor_partition_table.hpp deleted file mode 100644 index 10dccdd..0000000 --- a/pnor_partition_table.hpp +++ /dev/null @@ -1,345 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* Copyright (C) 2018 IBM Corp. */ -#pragma once - -#include -#include -#include -#include -#include "common.h" -#include "mbox.h" -#include "pnor_partition_defs.h" - -namespace openpower -{ -namespace virtual_pnor -{ - -namespace fs = std::experimental::filesystem; - -using PartitionTable = std::vector; -using checksum_t = uint32_t; - -/** @brief Convert the input partition table to big endian. - * - * @param[in] src - reference to the pnor partition table - * - * @returns converted partition table - */ -PartitionTable endianFixup(const PartitionTable& src); - -/** @brief Parse a ToC line (entry) into the corresponding FFS partition - * object. - * - * @param[in] line - The ToC line to parse - * @param[in] blockSize - The flash block size in bytes - * @param[out] part - The partition object to populate with the information - * parsed from the provided ToC line - * - * Throws: MalformedTocEntry, InvalidTocEntry - */ -void parseTocLine(const std::string& line, size_t blockSize, - pnor_partition& part); - -namespace details -{ - -/** @brief Compute XOR-based checksum, by XORing consecutive words - * in the input data. Input must be aligned to word boundary. - * - * @param[in] data - input data on which checksum is computed - * - * @returns computed checksum - */ -template checksum_t checksum(const T& data) -{ - static_assert(sizeof(decltype(data)) % sizeof(checksum_t) == 0, - "sizeof(data) is not aligned to sizeof(checksum_t) boundary"); - - auto begin = reinterpret_cast(&data); - auto end = begin + (sizeof(decltype(data)) / sizeof(checksum_t)); - - return std::accumulate(begin, end, 0, std::bit_xor()); -} - -} // namespace details - -namespace partition -{ - -/** @class Table - * @brief Generates virtual PNOR partition table. - * - * Generates virtual PNOR partition table upon construction. Reads - * the PNOR information generated by this tool : - * github.com/openbmc/openpower-pnor-code-mgmt/blob/master/generate-squashfs, - * which generates a minimalistic table-of-contents (toc) file and - * individual files to represent various partitions that are of interest - - * these help form the "virtual" PNOR, which is typically a subset of the full - * PNOR image. - * These files are stored in a well-known location on the PNOR. - * Based on this information, this class prepares the partition table whose - * structure is as outlined in pnor_partition.h. - * - * The virtual PNOR supports 4KB erase blocks - partitions must be aligned to - * this size. - */ -class Table -{ - public: - /** @brief Constructor accepting the path of the directory - * that houses the PNOR partition files. - * - * @param[in] ctx - Acquire sizes and paths relevant to the table - * - * Throws MalformedTocEntry, InvalidTocEntry - */ - Table(const struct mbox_context* ctx); - - Table(const Table&) = delete; - Table& operator=(const Table&) = delete; - Table(Table&&) = delete; - Table& operator=(Table&&) = delete; - ~Table() = default; - - /** @brief Return the exact size of partition table in bytes - * - * @returns size_t - size of partition table in bytes - */ - size_t size() const - { - return szBytes; - } - - /** @brief Return aligned size of partition table in bytes - * - * The value returned will be greater-than or equal to size(), and - * aligned to blockSize. - * - * @returns size_t - capacity of partition table in bytes - */ - size_t capacity() const - { - return align_up(szBytes, blockSize); - } - - /** @brief Return the size of partition table in blocks - * - * @returns size_t - size of partition table in blocks - */ - size_t blocks() const - { - return capacity() / blockSize; - } - - /** @brief Return a partition table having byte-ordering - * that the host expects. - * - * The host needs the partion table in big-endian. - * - * @returns const reference to host partition table. - */ - const pnor_partition_table& getHostTable() const - { - return *(reinterpret_cast(hostTbl.data())); - } - - /** @brief Return a little-endian partition table - * - * @returns const reference to native partition table - */ - const pnor_partition_table& getNativeTable() const - { - return *(reinterpret_cast(tbl.data())); - } - - /** @brief Return partition corresponding to PNOR offset, the offset - * is within returned partition. - * - * @param[in] offset - PNOR offset in bytes - * - * @returns const reference to pnor_partition, if found, else an - * exception will be thrown. - * - * Throws: UnmappedOffset - */ - const pnor_partition& partition(size_t offset) const; - - /** @brief Return partition corresponding to input partition name. - * - * @param[in] name - PNOR partition name - * - * @returns const reference to pnor_partition, if found, else an - * exception will be thrown. - * - * Throws: UnknownPartition - */ - const pnor_partition& partition(const std::string& name) const; - - private: - /** @brief Prepares a vector of PNOR partition structures. - * - * @param[in] ctx - An mbox context providing partition locations - * - * Throws: MalformedTocEntry, InvalidTocEntry - */ - void preparePartitions(const struct mbox_context* ctx); - - /** @brief Prepares the PNOR header. - */ - void prepareHeader(); - - /** @brief Allocate memory to hold the partion table. Determine the - * amount needed based on the partition files in the toc file. - * - * @param[in] tocFile - Table of contents file path. - */ - void allocateMemory(const fs::path& tocFile); - - /** @brief Return a little-endian partition table - * - * @returns reference to native partition table - */ - pnor_partition_table& getNativeTable() - { - return *(reinterpret_cast(tbl.data())); - } - - /** @brief Size of the PNOR partition table - - * sizeof(pnor_partition_table) + - * (no. of partitions * sizeof(pnor_partition)), - */ - size_t szBytes; - - /** @brief Partition table */ - PartitionTable tbl; - - /** @brief Partition table with host byte ordering */ - PartitionTable hostTbl; - - /** @brief Directory housing generated PNOR partition files */ - fs::path directory; - - /** @brief Number of partitions */ - size_t numParts; - - /** @brief PNOR block size, in bytes */ - size_t blockSize; - - /** @brief PNOR size, in bytes */ - size_t pnorSize; -}; -} // namespace partition - -/** @brief An exception type storing a reason string. - * - * This looks a lot like how std::runtime_error might be implemented however - * we want to avoid extending it, as exceptions extending ReasonedError have - * an expectation of being handled (can be predicted and are inside the scope - * of the program). - * - * From std::runtime_error documentation[1]: - * - * > Defines a type of object to be thrown as exception. It reports errors - * > that are due to events beyond the scope of the program and can not be - * > easily predicted. - * - * [1] http://en.cppreference.com/w/cpp/error/runtime_error - * - * We need to keep the inheritance hierarchy separate: This avoids the - * introduction of code that overzealously catches std::runtime_error to - * handle exceptions that would otherwise derive ReasonedError, and in the - * process swallows genuine runtime failures. - */ -class ReasonedError : public std::exception -{ - public: - ReasonedError(const std::string&& what) : _what(what) - { - } - const char* what() const noexcept - { - return _what.c_str(); - }; - - private: - const std::string _what; -}; - -/** @brief Base exception type for errors related to ToC entry parsing. - * - * Callers of parseTocEntry() may not be concerned with the specifics and - * rather just want to extract and log what(). - */ -class TocEntryError : public ReasonedError -{ - public: - TocEntryError(const std::string&& reason) : ReasonedError(std::move(reason)) - { - } -}; - -/** @brief The exception thrown on finding a syntax error in the ToC entry - * - * If the syntax is wrong, or expected values are missing, the ToC entry is - * malformed - */ -class MalformedTocEntry : public TocEntryError -{ - public: - MalformedTocEntry(const std::string&& reason) : - TocEntryError(std::move(reason)) - { - } -}; - -/** @brief The exception thrown on finding a semantic error in the ToC entry - * - * If the syntax of the ToC entry is correct but the semantics are broken, - * then we have an invalid ToC entry. - */ -class InvalidTocEntry : public TocEntryError -{ - public: - InvalidTocEntry(const std::string&& reason) : - TocEntryError(std::move(reason)) - { - } -}; - -class UnmappedOffset : public std::exception -{ - public: - UnmappedOffset(size_t base, size_t next) : base(base), next(next) - { - } - - const size_t base; - const size_t next; -}; - -class OutOfBoundsOffset : public ReasonedError -{ - public: - OutOfBoundsOffset(const std::string&& reason) : - ReasonedError(std::move(reason)) - { - } -}; - -class UnknownPartition : public ReasonedError -{ - public: - UnknownPartition(const std::string&& reason) : - ReasonedError(std::move(reason)) - { - } -}; - -} // namespace virtual_pnor -} // namespace openpower - -struct vpnor_partition_table -{ - openpower::virtual_pnor::partition::Table* table; -}; diff --git a/test/Makefile.am.include b/test/Makefile.am.include index 6e687ed..4251247 100644 --- a/test/Makefile.am.include +++ b/test/Makefile.am.include @@ -7,19 +7,19 @@ test_sanity_SOURCES = %reldir%/sanity.c test_copy_flash_SOURCES = \ %reldir%/copy_flash.c \ - mboxd_flash_physical.c \ + mboxd_flash.c \ common.c mtd.c \ %reldir%/tmpf.c test_erase_flash_SOURCES = \ %reldir%/erase_flash.c \ - mboxd_flash_physical.c \ + mboxd_flash.c \ common.c \ %reldir%/tmpf.c test_write_flash_SOURCES = \ %reldir%/write_flash.c \ - mboxd_flash_physical.c \ + mboxd_flash.c \ common.c \ %reldir%/tmpf.c @@ -27,9 +27,9 @@ TEST_MBOX_SRCS = \ mboxd_msg.c \ mboxd_windows.c \ mboxd_lpc.c \ - mboxd_lpc_physical.c \ + mboxd_lpc_reset.c \ common.c \ - mboxd_flash_physical.c + mboxd_flash.c TEST_MOCK_SRCS = %reldir%/tmpf.c %reldir%/mbox.c %reldir%/system.c diff --git a/test/vpnor/Makefile.am.include b/test/vpnor/Makefile.am.include index 0abb82c..0e6e030 100644 --- a/test/vpnor/Makefile.am.include +++ b/test/vpnor/Makefile.am.include @@ -1,5 +1,19 @@ -TEST_MBOX_VPNOR_SRCS = common.c pnor_partition_table.cpp \ - %reldir%/tmpd.cpp +TEST_MBOX_VPNOR_SRCS = \ + common.c \ + vpnor/pnor_partition_table.cpp \ + %reldir%/tmpd.cpp + +TEST_MBOX_VPNOR_INTEG_SRCS = \ + common.c \ + mboxd_msg.c \ + mboxd_windows.c \ + mboxd_lpc.c \ + vpnor/mboxd_lpc_reset.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ + vpnor/pnor_partition_table.cpp \ + %reldir%/tmpd.cpp VPNOR_LDADD = -lstdc++fs \ $(SDBUSPLUS_LIBS) \ @@ -7,323 +21,212 @@ VPNOR_LDADD = -lstdc++fs \ $(PHOSPHOR_DBUS_INTERFACES_LIBS) test_vpnor_create_pnor_partition_table_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) \ $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_pnor_partition_table.cpp test_vpnor_create_pnor_partition_table_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_pnor_partition_table_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_partition_exists_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_partition_exists.cpp test_vpnor_create_read_window_partition_exists_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_partition_exists_LDADD = $(VPNOR_LDADD) test_vpnor_write_patch_SOURCES = \ $(TEST_MBOX_VPNOR_SRCS) \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ mtd.c \ - pnor_partition.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ %reldir%/write_patch.cpp test_vpnor_write_patch_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_write_patch_LDADD = $(VPNOR_LDADD) test_vpnor_write_prsv_SOURCES = \ $(TEST_MBOX_VPNOR_SRCS) \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ mtd.c \ - pnor_partition.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ %reldir%/write_prsv.cpp test_vpnor_write_prsv_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_write_prsv_LDADD = $(VPNOR_LDADD) test_vpnor_write_ro_SOURCES = \ $(TEST_MBOX_VPNOR_SRCS) \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ mtd.c \ - pnor_partition.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ %reldir%/write_ro.cpp test_vpnor_write_ro_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_write_ro_LDADD = $(VPNOR_LDADD) test_vpnor_write_rw_SOURCES = \ $(TEST_MBOX_VPNOR_SRCS) \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ mtd.c \ - pnor_partition.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ %reldir%/write_rw.cpp test_vpnor_write_rw_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_write_rw_LDADD = $(VPNOR_LDADD) test_vpnor_toc_no_name_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_no_name.cpp test_vpnor_toc_no_name_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_no_name_LDADD = $(VPNOR_LDADD) test_vpnor_toc_start_gt_end_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_start_gt_end.cpp test_vpnor_toc_start_gt_end_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_start_gt_end_LDADD = $(VPNOR_LDADD) test_vpnor_toc_no_start_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_no_start.cpp test_vpnor_toc_no_start_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_no_start_LDADD = $(VPNOR_LDADD) test_vpnor_toc_no_end_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_no_end.cpp test_vpnor_toc_no_end_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_no_end_LDADD = $(VPNOR_LDADD) test_vpnor_toc_no_version_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_no_version.cpp test_vpnor_toc_no_version_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_no_version_LDADD = $(VPNOR_LDADD) test_vpnor_toc_flags_SOURCES = \ common.c \ - pnor_partition_table.cpp \ + vpnor/pnor_partition_table.cpp \ %reldir%/toc_flags.cpp test_vpnor_toc_flags_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_flags_LDADD = $(VPNOR_LDADD) test_vpnor_toc_overlap_SOURCES = \ $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ - common.c \ - pnor_partition_table.cpp \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/toc_overlap.cpp test_vpnor_toc_overlap_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_overlap_LDADD = $(VPNOR_LDADD) test_vpnor_toc_lookup_found_SOURCES = \ $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ - common.c \ - pnor_partition_table.cpp \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/toc_lookup_found.cpp test_vpnor_toc_lookup_found_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_lookup_found_LDADD = $(VPNOR_LDADD) test_vpnor_toc_lookup_failed_SOURCES = \ $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ - common.c \ - pnor_partition_table.cpp \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/toc_lookup_failed.cpp test_vpnor_toc_lookup_failed_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_lookup_failed_LDADD = $(VPNOR_LDADD) test_vpnor_toc_missing_file_SOURCES = \ $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ - common.c \ - pnor_partition_table.cpp \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/toc_missing_file.cpp test_vpnor_toc_missing_file_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_toc_missing_file_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_oob_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_oob.cpp test_vpnor_create_read_window_oob_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_oob_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_toc_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_toc.cpp test_vpnor_create_read_window_toc_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_toc_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_straddle_partitions_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_straddle_partitions.cpp test_vpnor_create_read_window_straddle_partitions_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_straddle_partitions_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_partition_invalid_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_partition_invalid.cpp test_vpnor_create_read_window_partition_invalid_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_partition_invalid_LDADD = $(VPNOR_LDADD) test_vpnor_read_patch_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/read_patch.cpp test_vpnor_read_patch_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_read_patch_LDADD = $(VPNOR_LDADD) test_vpnor_write_patch_resize_SOURCES = \ $(TEST_MBOX_VPNOR_SRCS) \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ mtd.c \ - pnor_partition.cpp \ + vpnor/mboxd_pnor_partition_table.cpp \ + vpnor/mboxd_flash.cpp \ + vpnor/pnor_partition.cpp \ %reldir%/write_patch_resize.cpp test_vpnor_write_patch_resize_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_write_patch_resize_LDADD = $(VPNOR_LDADD) test_vpnor_dump_flash_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/dump_flash.cpp test_vpnor_dump_flash_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_dump_flash_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_size_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_size.cpp test_vpnor_create_read_window_size_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_size_LDADD = $(VPNOR_LDADD) test_vpnor_create_read_window_remap_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_read_window_remap.cpp test_vpnor_create_read_window_remap_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_read_window_remap_LDADD = $(VPNOR_LDADD) test_vpnor_create_write_window_ro_partition_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_write_window_ro_partition.cpp test_vpnor_create_write_window_ro_partition_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_write_window_ro_partition_LDADD = $(VPNOR_LDADD) test_vpnor_create_write_window_rw_partition_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_write_window_rw_partition.cpp test_vpnor_create_write_window_rw_partition_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_write_window_rw_partition_LDADD = $(VPNOR_LDADD) test_vpnor_create_write_window_unmapped_SOURCES = \ - $(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \ - mboxd_msg.c \ - mboxd_windows.c \ - mboxd_lpc.c \ - mboxd_lpc_virtual.cpp \ - mboxd_pnor_partition_table.cpp \ - mboxd_flash_virtual.cpp \ - pnor_partition.cpp \ + $(TEST_MOCK_SRCS) \ + $(TEST_MBOX_VPNOR_INTEG_SRCS) \ %reldir%/create_write_window_unmapped.cpp test_vpnor_create_write_window_unmapped_LDFLAGS = $(OESDK_TESTCASE_FLAGS) test_vpnor_create_write_window_unmapped_LDADD = $(VPNOR_LDADD) diff --git a/test/vpnor/create_pnor_partition_table.cpp b/test/vpnor/create_pnor_partition_table.cpp index 761b5cd..1c664f9 100644 --- a/test/vpnor/create_pnor_partition_table.cpp +++ b/test/vpnor/create_pnor_partition_table.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_oob.cpp b/test/vpnor/create_read_window_oob.cpp index 8b2b0f4..e827b70 100644 --- a/test/vpnor/create_read_window_oob.cpp +++ b/test/vpnor/create_read_window_oob.cpp @@ -5,7 +5,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_partition_exists.cpp b/test/vpnor/create_read_window_partition_exists.cpp index f0466c9..983435f 100644 --- a/test/vpnor/create_read_window_partition_exists.cpp +++ b/test/vpnor/create_read_window_partition_exists.cpp @@ -8,7 +8,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_partition_invalid.cpp b/test/vpnor/create_read_window_partition_invalid.cpp index cdd895b..796f90b 100644 --- a/test/vpnor/create_read_window_partition_invalid.cpp +++ b/test/vpnor/create_read_window_partition_invalid.cpp @@ -5,7 +5,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_remap.cpp b/test/vpnor/create_read_window_remap.cpp index 97e0abc..dc3bb00 100644 --- a/test/vpnor/create_read_window_remap.cpp +++ b/test/vpnor/create_read_window_remap.cpp @@ -8,7 +8,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_size.cpp b/test/vpnor/create_read_window_size.cpp index cfbb881..eb19299 100644 --- a/test/vpnor/create_read_window_size.cpp +++ b/test/vpnor/create_read_window_size.cpp @@ -5,7 +5,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_straddle_partitions.cpp b/test/vpnor/create_read_window_straddle_partitions.cpp index 67149fa..48b6414 100644 --- a/test/vpnor/create_read_window_straddle_partitions.cpp +++ b/test/vpnor/create_read_window_straddle_partitions.cpp @@ -6,7 +6,7 @@ #include #include -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_read_window_toc.cpp b/test/vpnor/create_read_window_toc.cpp index 1f68371..b663032 100644 --- a/test/vpnor/create_read_window_toc.cpp +++ b/test/vpnor/create_read_window_toc.cpp @@ -5,7 +5,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_write_window_ro_partition.cpp b/test/vpnor/create_write_window_ro_partition.cpp index e09ac69..684cd39 100644 --- a/test/vpnor/create_write_window_ro_partition.cpp +++ b/test/vpnor/create_write_window_ro_partition.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_write_window_rw_partition.cpp b/test/vpnor/create_write_window_rw_partition.cpp index d205e22..38b0d7b 100644 --- a/test/vpnor/create_write_window_rw_partition.cpp +++ b/test/vpnor/create_write_window_rw_partition.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/create_write_window_unmapped.cpp b/test/vpnor/create_write_window_unmapped.cpp index b493fad..27dc931 100644 --- a/test/vpnor/create_write_window_unmapped.cpp +++ b/test/vpnor/create_write_window_unmapped.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/dump_flash.cpp b/test/vpnor/dump_flash.cpp index 8ee4b05..6757c90 100644 --- a/test/vpnor/dump_flash.cpp +++ b/test/vpnor/dump_flash.cpp @@ -6,7 +6,7 @@ #include "config.h" #include "mboxd_msg.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/read_patch.cpp b/test/vpnor/read_patch.cpp index 2a91b66..b1e6dbb 100644 --- a/test/vpnor/read_patch.cpp +++ b/test/vpnor/read_patch.cpp @@ -3,7 +3,7 @@ #include "config.h" #include "common.h" -#include "mboxd_pnor_partition_table.h" +#include "vpnor/mboxd_pnor_partition_table.h" #include diff --git a/test/vpnor/tmpd.hpp b/test/vpnor/tmpd.hpp index 2ebb6c6..45260ca 100644 --- a/test/vpnor/tmpd.hpp +++ b/test/vpnor/tmpd.hpp @@ -9,7 +9,7 @@ #include "config.h" #include "mbox.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" namespace openpower { diff --git a/test/vpnor/toc_flags.cpp b/test/vpnor/toc_flags.cpp index 6e134e5..c30f7ad 100644 --- a/test/vpnor/toc_flags.cpp +++ b/test/vpnor/toc_flags.cpp @@ -5,8 +5,8 @@ #include #include "common.h" -#include "pnor_partition_defs.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_defs.h" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; static constexpr auto DATA_MASK = ((1 << 24) - 1); diff --git a/test/vpnor/toc_lookup_failed.cpp b/test/vpnor/toc_lookup_failed.cpp index 5230ab1..eb9be4c 100644 --- a/test/vpnor/toc_lookup_failed.cpp +++ b/test/vpnor/toc_lookup_failed.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" #include "xyz/openbmc_project/Common/error.hpp" extern "C" { diff --git a/test/vpnor/toc_lookup_found.cpp b/test/vpnor/toc_lookup_found.cpp index fdc5a10..3d5dfd0 100644 --- a/test/vpnor/toc_lookup_found.cpp +++ b/test/vpnor/toc_lookup_found.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/toc_missing_file.cpp b/test/vpnor/toc_missing_file.cpp index dd602bb..2b2b423 100644 --- a/test/vpnor/toc_missing_file.cpp +++ b/test/vpnor/toc_missing_file.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/toc_no_end.cpp b/test/vpnor/toc_no_end.cpp index 0d90b91..f9c5187 100644 --- a/test/vpnor/toc_no_end.cpp +++ b/test/vpnor/toc_no_end.cpp @@ -4,7 +4,7 @@ #include "config.h" #include -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; diff --git a/test/vpnor/toc_no_name.cpp b/test/vpnor/toc_no_name.cpp index 06dea8c..d62056f 100644 --- a/test/vpnor/toc_no_name.cpp +++ b/test/vpnor/toc_no_name.cpp @@ -4,7 +4,7 @@ #include "config.h" #include -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; diff --git a/test/vpnor/toc_no_start.cpp b/test/vpnor/toc_no_start.cpp index 0ef47a8..1d20a46 100644 --- a/test/vpnor/toc_no_start.cpp +++ b/test/vpnor/toc_no_start.cpp @@ -4,7 +4,7 @@ #include "config.h" #include -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; diff --git a/test/vpnor/toc_no_version.cpp b/test/vpnor/toc_no_version.cpp index b607322..68cb057 100644 --- a/test/vpnor/toc_no_version.cpp +++ b/test/vpnor/toc_no_version.cpp @@ -4,7 +4,7 @@ #include "config.h" #include -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; diff --git a/test/vpnor/toc_overlap.cpp b/test/vpnor/toc_overlap.cpp index 97748e2..c87ae54 100644 --- a/test/vpnor/toc_overlap.cpp +++ b/test/vpnor/toc_overlap.cpp @@ -4,7 +4,7 @@ #include #include "config.h" -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" extern "C" { #include "test/mbox.h" diff --git a/test/vpnor/toc_start_gt_end.cpp b/test/vpnor/toc_start_gt_end.cpp index c5c83ef..69edc63 100644 --- a/test/vpnor/toc_start_gt_end.cpp +++ b/test/vpnor/toc_start_gt_end.cpp @@ -4,7 +4,7 @@ #include "config.h" #include -#include "pnor_partition_table.hpp" +#include "vpnor/pnor_partition_table.hpp" static constexpr auto BLOCK_SIZE = 4 * 1024; diff --git a/vpnor/Makefile.am.include b/vpnor/Makefile.am.include new file mode 100644 index 0000000..edb97b7 --- /dev/null +++ b/vpnor/Makefile.am.include @@ -0,0 +1,10 @@ +mboxd_SOURCES += %reldir%/pnor_partition_table.cpp \ + %reldir%/mboxd_pnor_partition_table.cpp \ + %reldir%/mboxd_flash.cpp \ + %reldir%/pnor_partition.cpp \ + %reldir%/mboxd_lpc_reset.cpp + +mboxd_LDFLAGS += -lstdc++fs \ + $(SDBUSPLUS_LIBS) \ + $(PHOSPHOR_LOGGING_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) diff --git a/vpnor/mboxd_flash.cpp b/vpnor/mboxd_flash.cpp new file mode 100644 index 0000000..ffe75c6 --- /dev/null +++ b/vpnor/mboxd_flash.cpp @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2018 IBM Corp. + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "common.h" +} + +#include "config.h" +#include "mboxd_flash.h" +#include "mboxd_pnor_partition_table.h" +#include "pnor_partition.hpp" +#include "pnor_partition_table.hpp" +#include "xyz/openbmc_project/Common/error.hpp" +#include +#include + +#include +#include +#include +#include + +namespace err = sdbusplus::xyz::openbmc_project::Common::Error; +namespace fs = std::experimental::filesystem; +namespace vpnor = openpower::virtual_pnor; + +/** @brief unique_ptr functor to release a char* reference. */ +struct StringDeleter +{ + void operator()(char* ptr) const + { + free(ptr); + } +}; +using StringPtr = std::unique_ptr; + +int init_flash_dev(struct mbox_context* context) +{ + StringPtr filename(get_dev_mtd()); + int fd = 0; + int rc = 0; + + if (!filename) + { + MSG_ERR("Couldn't find the flash /dev/mtd partition\n"); + return -1; + } + + MSG_DBG("Opening %s\n", filename.get()); + + fd = open(filename.get(), O_RDWR); + if (fd < 0) + { + MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename.get(), + strerror(errno)); + return -errno; + } + + // Read the Flash Info + if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) + { + MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno)); + close(fd); + return -errno; + } + + if (context->flash_size == 0) + { + // See comment in mboxd_flash_physical.c on why + // this is needed. + context->flash_size = context->mtd_info.size; + } + + // Hostboot requires a 4K block-size to be used in the FFS flash structure + context->mtd_info.erasesize = 4096; + context->erase_size_shift = log_2(context->mtd_info.erasesize); + context->flash_bmap = NULL; + context->fds[MTD_FD].fd = -1; + + close(fd); + return rc; +} + +void free_flash_dev(struct mbox_context* context) +{ + // No-op +} + +int set_flash_bytemap(struct mbox_context* context, uint32_t offset, + uint32_t count, uint8_t val) +{ + // No-op + return 0; +} + +int erase_flash(struct mbox_context* context, uint32_t offset, uint32_t count) +{ + // No-op + return 0; +} + +/* + * copy_flash() - Copy data from the virtual pnor into a provided buffer + * @context: The mbox context pointer + * @offset: The pnor offset to copy from (bytes) + * @mem: The buffer to copy into (must be of atleast 'size' bytes) + * @size: The number of bytes to copy + * Return: Number of bytes copied on success, otherwise negative error + * code. copy_flash will copy at most 'size' bytes, but it may + * copy less. + */ +int64_t copy_flash(struct mbox_context* context, uint32_t offset, void* mem, + uint32_t size) +{ + vpnor::partition::Table* table; + int rc = size; + + if (!(context && context->vpnor && context->vpnor->table)) + { + MSG_ERR("Trying to copy data with uninitialised context!\n"); + return -MBOX_R_SYSTEM_ERROR; + } + + table = context->vpnor->table; + + MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem, + size, offset); + + /* The virtual PNOR partition table starts at offset 0 in the virtual + * pnor image. Check if host asked for an offset that lies within the + * partition table. + */ + size_t sz = table->size(); + if (offset < sz) + { + const pnor_partition_table& toc = table->getHostTable(); + rc = std::min(sz - offset, static_cast(size)); + memcpy(mem, ((uint8_t*)&toc) + offset, rc); + return rc; + } + + try + { + vpnor::Request req(context, offset); + rc = req.read(mem, size); + } + catch (vpnor::UnmappedOffset& e) + { + /* + * Hooo boy. Pretend that this is valid flash so we don't have + * discontiguous regions presented to the host. Instead, fill a window + * with 0xff so the 'flash' looks erased. Writes to such regions are + * dropped on the floor, see the implementation of write_flash() below. + */ + MSG_INFO("Host requested unmapped region of %" PRId32 + " bytes at offset 0x%" PRIx32 "\n", + size, offset); + uint32_t span = e.next - e.base; + rc = std::min(size, span); + memset(mem, 0xff, rc); + } + catch (std::exception& e) + { + MSG_ERR("%s\n", e.what()); + phosphor::logging::commit(); + rc = -MBOX_R_SYSTEM_ERROR; + } + return rc; +} + +/* + * write_flash() - Write to the virtual pnor from a provided buffer + * @context: The mbox context pointer + * @offset: The flash offset to write to (bytes) + * @buf: The buffer to write from (must be of atleast size) + * @size: The number of bytes to write + * + * Return: 0 on success otherwise negative error code + */ + +int write_flash(struct mbox_context* context, uint32_t offset, void* buf, + uint32_t count) +{ + + if (!(context && context->vpnor && context->vpnor->table)) + { + MSG_ERR("Trying to write data with uninitialised context!\n"); + return -MBOX_R_SYSTEM_ERROR; + } + + vpnor::partition::Table* table = context->vpnor->table; + + try + { + const struct pnor_partition& part = table->partition(offset); + if (part.data.user.data[1] & PARTITION_READONLY) + { + /* FIXME: This should be done on CREATE_WRITE_WINDOW, not here */ + return -MBOX_R_WRITE_ERROR; + } + + MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, + buf); + vpnor::Request req(context, offset); + req.write(buf, count); + } + catch (vpnor::UnmappedOffset& e) + { + /* Paper over the fact that the write isn't persistent */ + MSG_INFO("Dropping %d bytes host wrote to unmapped offset 0x%" PRIx32 + "\n", + count, offset); + return 0; + } + catch (const vpnor::OutOfBoundsOffset& e) + { + MSG_ERR("%s\n", e.what()); + return -MBOX_R_PARAM_ERROR; + } + catch (const std::exception& e) + { + MSG_ERR("%s\n", e.what()); + phosphor::logging::commit(); + return -MBOX_R_SYSTEM_ERROR; + } + return 0; +} diff --git a/vpnor/mboxd_lpc_reset.cpp b/vpnor/mboxd_lpc_reset.cpp new file mode 100644 index 0000000..60ce0d3 --- /dev/null +++ b/vpnor/mboxd_lpc_reset.cpp @@ -0,0 +1,36 @@ +/* + * Mailbox Daemon LPC Helpers + * + * Copyright 2017 IBM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "mbox.h" +#include "mboxd_lpc.h" +#include "mboxd_pnor_partition_table.h" + +/* + * reset_lpc() - Reset the lpc bus mapping + * @context: The mbox context pointer + * + * Return 0 on success otherwise negative error code + */ +int reset_lpc(struct mbox_context *context) +{ + destroy_vpnor(context); + init_vpnor(context); + vpnor_copy_bootloader_partition(context); + return point_to_memory(context); +} diff --git a/vpnor/mboxd_pnor_partition_table.cpp b/vpnor/mboxd_pnor_partition_table.cpp new file mode 100644 index 0000000..db53dc8 --- /dev/null +++ b/vpnor/mboxd_pnor_partition_table.cpp @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2018 IBM Corp. +#include "mboxd_pnor_partition_table.h" +#include "pnor_partition_table.hpp" +#include "common.h" +#include "mbox.h" +#include "mboxd_flash.h" +#include "pnor_partition_table.hpp" +#include "config.h" +#include "xyz/openbmc_project/Common/error.hpp" +#include +#include + +int init_vpnor(struct mbox_context *context) +{ + if (context && !context->vpnor) + { + int rc; + + strncpy(context->paths.ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX); + context->paths.ro_loc[PATH_MAX - 1] = '\0'; + strncpy(context->paths.rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX); + context->paths.rw_loc[PATH_MAX - 1] = '\0'; + strncpy(context->paths.prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX); + context->paths.prsv_loc[PATH_MAX - 1] = '\0'; + strncpy(context->paths.patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX); + context->paths.prsv_loc[PATH_MAX - 1] = '\0'; + + rc = init_vpnor_from_paths(context); + if (rc < 0) + { + return rc; + } + } + + return 0; +} + +int init_vpnor_from_paths(struct mbox_context *context) +{ + namespace err = sdbusplus::xyz::openbmc_project::Common::Error; + namespace fs = std::experimental::filesystem; + namespace vpnor = openpower::virtual_pnor; + + if (context && !context->vpnor) + { + try + { + context->vpnor = new vpnor_partition_table; + context->vpnor->table = + new openpower::virtual_pnor::partition::Table(context); + } + catch (vpnor::TocEntryError &e) + { + MSG_ERR("%s\n", e.what()); + phosphor::logging::commit(); + return -MBOX_R_SYSTEM_ERROR; + } + } + + return 0; +} + +int vpnor_copy_bootloader_partition(const struct mbox_context *context) +{ + // The hostboot bootloader has certain size/offset assumptions, so + // we need a special partition table here. + // It assumes the PNOR is 64M, the TOC size is 32K, the erase block is + // 4K, the page size is 4K. + // It also assumes the TOC is at the 'end of pnor - toc size - 1 page size' + // offset, and first looks for the TOC here, before proceeding to move up + // page by page looking for the TOC. So it is optimal to place the TOC at + // this offset. + constexpr size_t eraseSize = 0x1000; + constexpr size_t pageSize = 0x1000; + constexpr size_t pnorSize = 0x4000000; + constexpr size_t tocMaxSize = 0x8000; + constexpr size_t tocStart = pnorSize - tocMaxSize - pageSize; + constexpr auto blPartitionName = "HBB"; + + namespace err = sdbusplus::xyz::openbmc_project::Common::Error; + namespace fs = std::experimental::filesystem; + namespace vpnor = openpower::virtual_pnor; + + try + { + vpnor_partition_table vtbl{}; + struct mbox_context local = *context; + local.vpnor = &vtbl; + local.block_size_shift = log_2(eraseSize); + + openpower::virtual_pnor::partition::Table blTable(&local); + + vtbl.table = &blTable; + + size_t tocOffset = 0; + + // Copy TOC + copy_flash(&local, tocOffset, + static_cast(context->mem) + tocStart, + blTable.capacity()); + const pnor_partition &partition = blTable.partition(blPartitionName); + size_t hbbOffset = partition.data.base * eraseSize; + uint32_t hbbSize = partition.data.actual; + // Copy HBB + copy_flash(&local, hbbOffset, + static_cast(context->mem) + hbbOffset, hbbSize); + } + catch (err::InternalFailure &e) + { + phosphor::logging::commit(); + return -MBOX_R_SYSTEM_ERROR; + } + catch (vpnor::TocEntryError &e) + { + MSG_ERR("%s\n", e.what()); + phosphor::logging::commit(); + return -MBOX_R_SYSTEM_ERROR; + } + + return 0; +} + +void destroy_vpnor(struct mbox_context *context) +{ + if (context && context->vpnor) + { + delete context->vpnor->table; + delete context->vpnor; + context->vpnor = nullptr; + } +} diff --git a/vpnor/mboxd_pnor_partition_table.h b/vpnor/mboxd_pnor_partition_table.h new file mode 100644 index 0000000..d13a2d2 --- /dev/null +++ b/vpnor/mboxd_pnor_partition_table.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2018 IBM Corp. */ +#pragma once + +#ifdef VIRTUAL_PNOR_ENABLED + +#include +#include "pnor_partition_defs.h" + +struct mbox_context; +struct vpnor_partition_table; + +struct vpnor_partition_paths +{ + char ro_loc[PATH_MAX]; + char rw_loc[PATH_MAX]; + char prsv_loc[PATH_MAX]; + char patch_loc[PATH_MAX]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Create a virtual PNOR partition table. + * + * @param[in] context - mbox context pointer + * + * This API should be called before calling any other APIs below. If a table + * already exists, this function will not do anything further. This function + * will not do anything if the context is NULL. + * + * Returns 0 if the call succeeds, else a negative error code. + */ +int init_vpnor(struct mbox_context *context); + +/** @brief Create a virtual PNOR partition table. + * + * @param[in] context - mbox context pointer + * + * This API is same as above one but requires context->path is initialised + * with all the necessary paths. + * + * Returns 0 if the call succeeds, else a negative error code. + */ + +int init_vpnor_from_paths(struct mbox_context *context); + +/** @brief Copy bootloader partition (alongwith TOC) to LPC memory + * + * @param[in] context - mbox context pointer + * + * @returns 0 on success, negative error code on failure + */ +int vpnor_copy_bootloader_partition(const struct mbox_context *context); + +/** @brief Destroy partition table, if it exists. + * + * @param[in] context - mbox context pointer + */ +void destroy_vpnor(struct mbox_context *context); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vpnor/pnor_partition.cpp b/vpnor/pnor_partition.cpp new file mode 100644 index 0000000..84aeb3f --- /dev/null +++ b/vpnor/pnor_partition.cpp @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2018 IBM Corp. +#include "pnor_partition.hpp" +#include "pnor_partition_table.hpp" +#include "config.h" +#include "mboxd_flash.h" +#include "mboxd_pnor_partition_table.h" +#include "xyz/openbmc_project/Common/error.hpp" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#include +#include +#include +#include + +namespace openpower +{ +namespace virtual_pnor +{ + +namespace fs = std::experimental::filesystem; + +fs::path Request::getPartitionFilePath(int flags) +{ + // Check if partition exists in patch location + auto dst = fs::path(ctx->paths.patch_loc) / partition.data.name; + if (fs::is_regular_file(dst)) + { + return dst; + } + + switch (partition.data.user.data[1] & + (PARTITION_PRESERVED | PARTITION_READONLY)) + { + case PARTITION_PRESERVED: + dst = ctx->paths.prsv_loc; + break; + + case PARTITION_READONLY: + dst = ctx->paths.ro_loc; + break; + + default: + dst = ctx->paths.rw_loc; + } + dst /= partition.data.name; + + if (fs::exists(dst)) + { + return dst; + } + + if (flags == O_RDONLY) + { + dst = fs::path(ctx->paths.ro_loc) / partition.data.name; + assert(fs::exists(dst)); + return dst; + } + + assert(flags == O_RDWR); + auto src = fs::path(ctx->paths.ro_loc) / partition.data.name; + assert(fs::exists(src)); + + MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n", + partition.data.name, dst.c_str(), src.c_str()); + + dst = ctx->paths.rw_loc; + if (partition.data.user.data[1] & PARTITION_PRESERVED) + { + dst = ctx->paths.prsv_loc; + } + + dst /= partition.data.name; + fs::copy_file(src, dst); + + return dst; +} + +size_t Request::clamp(size_t len) +{ + size_t maxAccess = offset + len; + size_t partSize = partition.data.size << ctx->block_size_shift; + return std::min(maxAccess, partSize) - offset; +} + +void Request::resize(const fs::path &path, size_t len) +{ + size_t maxAccess = offset + len; + size_t fileSize = fs::file_size(path); + if (maxAccess < fileSize) + { + return; + } + MSG_DBG("Resizing %s to %zu bytes\n", path.c_str(), maxAccess); + int rc = truncate(path.c_str(), maxAccess); + if (rc == -1) + { + MSG_ERR("Failed to resize %s: %d\n", path.c_str(), errno); + throw std::system_error(errno, std::system_category()); + } +} + +size_t Request::fulfil(const fs::path &path, int flags, void *buf, size_t len) +{ + if (!(flags == O_RDONLY || flags == O_RDWR)) + { + std::stringstream err; + err << "Require O_RDONLY (0x" << std::hex << O_RDONLY << " or O_RDWR " + << std::hex << O_RDWR << " for flags, got: 0x" << std::hex << flags; + throw std::invalid_argument(err.str()); + } + + int fd = ::open(path.c_str(), flags); + if (fd == -1) + { + MSG_ERR("Failed to open backing file at '%s': %d\n", + path.c_str(), errno); + throw std::system_error(errno, std::system_category()); + } + + size_t fileSize = fs::file_size(path); + int mprot = PROT_READ | ((flags == O_RDWR) ? PROT_WRITE : 0); + auto map = mmap(NULL, fileSize, mprot, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) + { + close(fd); + MSG_ERR("Failed to map backing file '%s' for %zd bytes: %d\n", + path.c_str(), fileSize, errno); + throw std::system_error(errno, std::system_category()); + } + + // copy to the reserved memory area + if (flags == O_RDONLY) + { + memset(buf, 0xff, len); + memcpy(buf, (char *)map + offset, std::min(len, fileSize)); + } + else + { + memcpy((char *)map + offset, buf, len); + set_flash_bytemap(ctx, base + offset, len, FLASH_DIRTY); + } + munmap(map, fileSize); + close(fd); + + return len; +} + +} // namespace virtual_pnor +} // namespace openpower diff --git a/vpnor/pnor_partition.hpp b/vpnor/pnor_partition.hpp new file mode 100644 index 0000000..eb63a39 --- /dev/null +++ b/vpnor/pnor_partition.hpp @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2018 IBM Corp. */ +#pragma once + +extern "C" { +#include "mbox.h" +}; + +#include "mboxd_pnor_partition_table.h" +#include "pnor_partition_table.hpp" +#include +#include +#include +#include + +namespace openpower +{ +namespace virtual_pnor +{ + +namespace fs = std::experimental::filesystem; + +class Request +{ + public: + /** @brief Construct a flash access request + * + * @param[in] ctx - The mbox context used to process the request + * @param[in] offset - The absolute offset into the flash device as + * provided by the mbox message associated with the + * request + * + * The class does not take ownership of the ctx pointer. The lifetime of + * the ctx pointer must strictly exceed the lifetime of the class + * instance. + */ + Request(struct mbox_context* ctx, size_t offset) : + ctx(ctx), partition(ctx->vpnor->table->partition(offset)), + base(partition.data.base << ctx->block_size_shift), + offset(offset - base) + { + } + Request(const Request&) = delete; + Request& operator=(const Request&) = delete; + Request(Request&&) = default; + Request& operator=(Request&&) = default; + ~Request() = default; + + ssize_t read(void* dst, size_t len) + { + len = clamp(len); + constexpr auto flags = O_RDONLY; + fs::path path = getPartitionFilePath(flags); + return fulfil(path, flags, dst, len); + } + + ssize_t write(void* dst, size_t len) + { + if (len != clamp(len)) + { + std::stringstream err; + err << "Request size 0x" << std::hex << len << " from offset 0x" + << std::hex << offset << " exceeds the partition size 0x" + << std::hex << (partition.data.size << ctx->block_size_shift); + throw OutOfBoundsOffset(err.str()); + } + constexpr auto flags = O_RDWR; + /* Ensure file is at least the size of the maximum access */ + fs::path path = getPartitionFilePath(flags); + resize(path, len); + return fulfil(path, flags, dst, len); + } + + private: + /** @brief Clamp the access length to the maximum supported by the ToC */ + size_t clamp(size_t len); + + /** @brief Ensure the backing file is sized appropriately for the access + * + * We need to ensure the file is big enough to satisfy the request so that + * mmap() will succeed for the required size. + * + * @return The valid access length + * + * Throws: std::system_error + */ + void resize(const std::experimental::filesystem::path& path, size_t len); + + /** @brief Returns the partition file path associated with the offset. + * + * The search strategy for the partition file depends on the value of the + * flags parameter. + * + * For the O_RDONLY case: + * + * 1. Depending on the partition type,tries to open the file + * from the associated partition(RW/PRSV/RO). + * 1a. if file not found in the corresponding + * partition(RW/PRSV/RO) then tries to read the file from + * the read only partition. + * 1b. if the file not found in the read only partition then + * throw exception. + * + * For the O_RDWR case: + * + * 1. Depending on the partition type tries to open the file + * from the associated partition. + * 1a. if file not found in the corresponding partition(RW/PRSV) + * then copy the file from the read only partition to the (RW/PRSV) + * partition depending on the partition type. + * 1b. if the file not found in the read only partition then throw + * exception. + * + * @param[in] flags - The flags that will be used to open the file. Must + * be one of O_RDONLY or O_RDWR. + * + * Post-condition: The file described by the returned path exists + * + * Throws: std::filesystem_error, std::bad_alloc + */ + std::experimental::filesystem::path getPartitionFilePath(int flags); + + /** @brief Fill dst with the content of the partition relative to offset. + * + * @param[in] offset - The pnor offset(bytes). + * @param[out] dst - The buffer to fill with partition data + * @param[in] len - The length of the destination buffer + */ + size_t fulfil(const std::experimental::filesystem::path& path, int flags, + void* dst, size_t len); + + struct mbox_context* ctx; + const pnor_partition& partition; + size_t base; + size_t offset; +}; + +} // namespace virtual_pnor +} // namespace openpower diff --git a/vpnor/pnor_partition_defs.h b/vpnor/pnor_partition_defs.h new file mode 100644 index 0000000..447b11c --- /dev/null +++ b/vpnor/pnor_partition_defs.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2018 IBM Corp. */ +#pragma once + +#include +#include + +/* There are two structures outlined here - one that represents the PNOR + * partition table (or header) - this appears first in the PNOR image. + * The last field of the PNOR partition table structure is an array + * of another structure - which represents the partition. + * + * The flash structures used here have been borrowed from + * https://github.com/open-power/hostboot/blob/master/src/usr/pnor/ffs.h */ + + +/* The maximum length of a partition's name */ +#define PARTITION_NAME_MAX 15 + +/* The version of this partition implementation. This is an + * incrementing value */ +#define PARTITION_VERSION_1 1 + +/* Magic number for the partition partition_table (ASCII 'PART') */ +#define PARTITION_HEADER_MAGIC 0x50415254 + +/* Default parent partition id */ +#define PARENT_PATITION_ID 0xFFFFFFFF + +/* The partition structure has 16 'user data' words, which can be used to store + * miscellaneous information. This is typically used to store bits that state + * whether a partition is ECC protected, is read-only, is preserved across + * updates, etc. + * + * TODO: Replace with libflash (!) or at least refactor the data structures to + * better match hostboot's layout[1]. The latter would avoid the headache of + * verifying these flags match the expected functionality (taking into account + * changes in endianness). + * + * [1] https://github.com/open-power/hostboot/blob/9acfce99596f12dcc60952f8506a77e542609cbf/src/usr/pnor/common/ffs_hb.H#L81 + */ +#define PARTITION_USER_WORDS 16 +#define PARTITION_ECC_PROTECTED 0x8000 +#define PARTITION_PRESERVED 0x00800000 +#define PARTITION_READONLY 0x00400000 +#define PARTITION_REPROVISION 0x00100000 +#define PARTITION_VOLATILE 0x00080000 +#define PARTITION_CLEARECC 0x00040000 +#define PARTITION_VERSION_CHECK_SHA512 0x80000000 +#define PARTITION_VERSION_CHECK_SHA512_PER_EC 0x40000000 + +/* Partition flags */ +enum partition_flags { + PARTITION_FLAGS_PROTECTED = 0x0001, + PARTITION_FLAGS_U_BOOT_ENV = 0x0002 +}; + +/* Type of image contained within partition */ +enum partition_type { + PARTITION_TYPE_DATA = 1, + PARTITION_TYPE_LOGICAL = 2, + PARTITION_TYPE_PARTITION = 3 +}; + + +/** + * struct pnor_partition + * + * @name: Name of the partition - a null terminated string + * @base: The offset in the PNOR, in block-size (1 block = 4KB), + * where this partition is placed + * @size: Partition size in blocks. + * @pid: Parent partition id + * @id: Partition ID [1..65536] + * @type: Type of partition, see the 'type' enum + * @flags: Partition flags (optional), see the 'flags' enum + * @actual: Actual partition size (in bytes) + * @resvd: Reserved words for future use + * @user: User data (optional), see user data macros above + * @checksum: Partition checksum (includes all words above) - the + * checksum is obtained by a XOR operation on all of the + * words above. This is used for detecting a corruption + * in this structure + */ +struct pnor_partition { + struct { + char name[PARTITION_NAME_MAX + 1]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct + { + uint32_t data[PARTITION_USER_WORDS]; + } user; + } __attribute__ ((packed)) data; + uint32_t checksum; +} __attribute__ ((packed)); + +/** + * struct pnor_partition_table + * + * @magic: Eye catcher/corruption detector - set to + * PARTITION_HEADER_MAGIC + * @version: Version of the structure, set to + * PARTITION_VERSION_1 + * @size: Size of partition table (in blocks) + * @entry_size: Size of struct pnor_partition element (in bytes) + * @entry_count: Number of struct pnor_partition elements in partitions array + * @block_size: Size of an erase-block on the PNOR (in bytes) + * @block_count: Number of blocks on the PNOR + * @resvd: Reserved words for future use + * @checksum: Header checksum (includes all words above) - the + * checksum is obtained by a XOR operation on all of the + * words above. This is used for detecting a corruption + * in this structure + * @partitions: Array of struct pnor_partition + */ +struct pnor_partition_table { + struct { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + } __attribute__ ((packed)) data; + uint32_t checksum; + struct pnor_partition partitions[]; +} __attribute__ ((packed)); diff --git a/vpnor/pnor_partition_table.cpp b/vpnor/pnor_partition_table.cpp new file mode 100644 index 0000000..4ad07bc --- /dev/null +++ b/vpnor/pnor_partition_table.cpp @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2018 IBM Corp. +#include "pnor_partition_table.hpp" +#include "common.h" +#include "config.h" +#include "xyz/openbmc_project/Common/error.hpp" +#include +#include +#include +#include +#include +#include + +namespace openpower +{ +namespace virtual_pnor +{ + +using namespace phosphor::logging; +using namespace sdbusplus::xyz::openbmc_project::Common::Error; + +namespace partition +{ + +Table::Table(const struct mbox_context* ctx) : + szBytes(sizeof(pnor_partition_table)), numParts(0), + blockSize(1 << ctx->erase_size_shift), pnorSize(ctx->flash_size) +{ + preparePartitions(ctx); + prepareHeader(); + hostTbl = endianFixup(tbl); +} + +void Table::prepareHeader() +{ + decltype(auto) table = getNativeTable(); + table.data.magic = PARTITION_HEADER_MAGIC; + table.data.version = PARTITION_VERSION_1; + table.data.size = blocks(); + table.data.entry_size = sizeof(pnor_partition); + table.data.entry_count = numParts; + table.data.block_size = blockSize; + table.data.block_count = pnorSize / blockSize; + table.checksum = details::checksum(table.data); +} + +inline void Table::allocateMemory(const fs::path& tocFile) +{ + size_t num = 0; + std::string line; + std::ifstream file(tocFile.c_str()); + + // Find number of lines in partition file - this will help + // determine the number of partitions and hence also how much + // memory to allocate for the partitions array. + // The actual number of partitions may turn out to be lesser than this, + // in case of errors. + while (std::getline(file, line)) + { + // Check if line starts with "partition" + if (std::string::npos != line.find("partition", 0)) + { + ++num; + } + } + + szBytes = sizeof(pnor_partition_table) + (num * sizeof(pnor_partition)); + tbl.resize(capacity()); +} + +void Table::preparePartitions(const struct mbox_context* ctx) +{ + const fs::path roDir = ctx->paths.ro_loc; + const fs::path patchDir = ctx->paths.patch_loc; + fs::path tocFile = roDir / PARTITION_TOC_FILE; + allocateMemory(tocFile); + + std::ifstream file(tocFile.c_str()); + std::string line; + decltype(auto) table = getNativeTable(); + + while (std::getline(file, line)) + { + pnor_partition& part = table.partitions[numParts]; + fs::path patch; + fs::path file; + + // The ToC file presented in the vpnor squashfs looks like: + // + // version=IBM-witherspoon-ibm-OP9_v1.19_1.135 + // extended_version=op-build-v1.19-571-g04f4690-dirty,buildroot-2017.11-5-g65679be,skiboot-v5.10-rc4,hostboot-4c46d66,linux-4.14.20-openpower1-p4a6b675,petitboot-v1.6.6-pe5aaec2,machine-xml-0fea226,occ-3286b6b,hostboot-binaries-3d1af8f,capp-ucode-p9-dd2-v3,sbe-99e2fe2 + // partition00=part,0x00000000,0x00002000,00,READWRITE + // partition01=HBEL,0x00008000,0x0002c000,00,ECC,REPROVISION,CLEARECC,READWRITE + // ... + // + // As such we want to skip any lines that don't begin with 'partition' + if (std::string::npos == line.find("partition", 0)) + { + continue; + } + + parseTocLine(line, blockSize, part); + + if (numParts > 0) + { + struct pnor_partition& prev = table.partitions[numParts - 1]; + uint32_t prev_end = prev.data.base + prev.data.size; + + if (part.data.id == prev.data.id) + { + MSG_ERR("ID for previous partition '%s' at block 0x%" PRIx32 + "matches current partition '%s' at block 0x%" PRIx32 + ": %" PRId32 "\n", + prev.data.name, prev.data.base, part.data.name, + part.data.base, part.data.id); + } + + if (part.data.base < prev_end) + { + std::stringstream err; + err << "Partition '" << part.data.name << "' start block 0x" + << std::hex << part.data.base << "is less than the end " + << "block 0x" << std::hex << prev_end << " of '" + << prev.data.name << "'"; + throw InvalidTocEntry(err.str()); + } + } + + file = roDir / part.data.name; + if (!fs::exists(file)) + { + std::stringstream err; + err << "Partition file " << file.native() << " does not exist"; + throw InvalidTocEntry(err.str()); + } + + patch = patchDir / part.data.name; + if (fs::is_regular_file(patch)) + { + const size_t size = part.data.size * blockSize; + part.data.actual = + std::min(size, static_cast(fs::file_size(patch))); + } + + ++numParts; + } +} + +const pnor_partition& Table::partition(size_t offset) const +{ + const decltype(auto) table = getNativeTable(); + size_t blockOffset = offset / blockSize; + + for (decltype(numParts) i{}; i < numParts; ++i) + { + const struct pnor_partition& part = table.partitions[i]; + size_t len = part.data.size; + + if ((blockOffset >= part.data.base) && + (blockOffset < (part.data.base + len))) + { + return part; + } + + /* Are we in a hole between partitions? */ + if (blockOffset < part.data.base) + { + throw UnmappedOffset(offset, part.data.base * blockSize); + } + } + + throw UnmappedOffset(offset, pnorSize); +} + +const pnor_partition& Table::partition(const std::string& name) const +{ + const decltype(auto) table = getNativeTable(); + + for (decltype(numParts) i{}; i < numParts; ++i) + { + if (name == table.partitions[i].data.name) + { + return table.partitions[i]; + } + } + + std::stringstream err; + err << "Partition " << name << " is not listed in the table of contents"; + throw UnknownPartition(err.str()); +} + +} // namespace partition + +PartitionTable endianFixup(const PartitionTable& in) +{ + PartitionTable out; + out.resize(in.size()); + auto src = reinterpret_cast(in.data()); + auto dst = reinterpret_cast(out.data()); + + dst->data.magic = htobe32(src->data.magic); + dst->data.version = htobe32(src->data.version); + dst->data.size = htobe32(src->data.size); + dst->data.entry_size = htobe32(src->data.entry_size); + dst->data.entry_count = htobe32(src->data.entry_count); + dst->data.block_size = htobe32(src->data.block_size); + dst->data.block_count = htobe32(src->data.block_count); + dst->checksum = details::checksum(dst->data); + + for (decltype(src->data.entry_count) i{}; i < src->data.entry_count; ++i) + { + auto psrc = &src->partitions[i]; + auto pdst = &dst->partitions[i]; + strncpy(pdst->data.name, psrc->data.name, PARTITION_NAME_MAX); + // Just to be safe + pdst->data.name[PARTITION_NAME_MAX] = '\0'; + pdst->data.base = htobe32(psrc->data.base); + pdst->data.size = htobe32(psrc->data.size); + pdst->data.pid = htobe32(psrc->data.pid); + pdst->data.id = htobe32(psrc->data.id); + pdst->data.type = htobe32(psrc->data.type); + pdst->data.flags = htobe32(psrc->data.flags); + pdst->data.actual = htobe32(psrc->data.actual); + for (size_t j = 0; j < PARTITION_USER_WORDS; ++j) + { + pdst->data.user.data[j] = htobe32(psrc->data.user.data[j]); + } + pdst->checksum = details::checksum(pdst->data); + } + + return out; +} + +static inline void writeSizes(pnor_partition& part, size_t start, size_t end, + size_t blockSize) +{ + size_t size = end - start; + part.data.base = align_up(start, blockSize) / blockSize; + size_t sizeInBlocks = align_up(size, blockSize) / blockSize; + part.data.size = sizeInBlocks; + part.data.actual = size; +} + +static inline void writeUserdata(pnor_partition& part, uint32_t version, + const std::string& data) +{ + std::istringstream stream(data); + std::string flag{}; + auto perms = 0; + auto state = 0; + + MSG_DBG("Parsing ToC flags '%s'\n", data.c_str()); + while (std::getline(stream, flag, ',')) + { + if (flag == "") + continue; + + if (flag == "ECC") + { + state |= PARTITION_ECC_PROTECTED; + } + else if (flag == "READONLY") + { + perms |= PARTITION_READONLY; + } + else if (flag == "READWRITE") + { + perms &= ~PARTITION_READONLY; + } + else if (flag == "PRESERVED") + { + perms |= PARTITION_PRESERVED; + } + else if (flag == "REPROVISION") + { + perms |= PARTITION_REPROVISION; + } + else if (flag == "VOLATILE") + { + perms |= PARTITION_VOLATILE; + } + else if (flag == "CLEARECC") + { + perms |= PARTITION_CLEARECC; + } + else + { + MSG_INFO("Found unimplemented partition property: %s\n", + flag.c_str()); + } + } + + part.data.user.data[0] = state; + part.data.user.data[1] = perms; + part.data.user.data[1] |= version; +} + +static inline void writeDefaults(pnor_partition& part) +{ + part.data.pid = PARENT_PATITION_ID; + part.data.type = PARTITION_TYPE_DATA; + part.data.flags = 0; // flags unused +} + +static inline void writeNameAndId(pnor_partition& part, std::string&& name, + const std::string& id) +{ + name.resize(PARTITION_NAME_MAX); + memcpy(part.data.name, name.c_str(), sizeof(part.data.name)); + part.data.id = std::stoul(id); +} + +void parseTocLine(const std::string& line, size_t blockSize, + pnor_partition& part) +{ + static constexpr auto ID_MATCH = 1; + static constexpr auto NAME_MATCH = 2; + static constexpr auto START_ADDR_MATCH = 4; + static constexpr auto END_ADDR_MATCH = 6; + static constexpr auto VERSION_MATCH = 8; + constexpr auto versionShift = 24; + + // Parse PNOR toc (table of contents) file, which has lines like : + // partition01=HBB,0x00010000,0x000a0000,0x80,ECC,PRESERVED, to indicate + // partition information + std::regex regex{ + "^partition([0-9]+)=([A-Za-z0-9_]+)," + "(0x)?([0-9a-fA-F]+),(0x)?([0-9a-fA-F]+),(0x)?([A-Fa-f0-9]{2})", + std::regex::extended}; + + std::smatch match; + if (!std::regex_search(line, match, regex)) + { + std::stringstream err; + err << "Malformed partition description: " << line.c_str() << "\n"; + throw MalformedTocEntry(err.str()); + } + + writeNameAndId(part, match[NAME_MATCH].str(), match[ID_MATCH].str()); + writeDefaults(part); + + unsigned long start = + std::stoul(match[START_ADDR_MATCH].str(), nullptr, 16); + if (start & (blockSize - 1)) + { + MSG_ERR("Start offset 0x%lx for partition '%s' is not aligned to block " + "size 0x%zx\n", + start, match[NAME_MATCH].str().c_str(), blockSize); + } + + unsigned long end = std::stoul(match[END_ADDR_MATCH].str(), nullptr, 16); + if ((end - start) & (blockSize - 1)) + { + MSG_ERR("Partition '%s' has a size 0x%lx that is not aligned to block " + "size 0x%zx\n", + match[NAME_MATCH].str().c_str(), (end - start), blockSize); + } + + if (start >= end) + { + std::stringstream err; + err << "Partition " << match[NAME_MATCH].str() + << " has an invalid range: start offset (0x" << std::hex << start + << " is beyond open end (0x" << std::hex << end << ")\n"; + throw InvalidTocEntry(err.str()); + } + writeSizes(part, start, end, blockSize); + + // Use the shift to convert "80" to 0x80000000 + unsigned long version = std::stoul(match[VERSION_MATCH].str(), nullptr, 16); + writeUserdata(part, version << versionShift, match.suffix().str()); + part.checksum = details::checksum(part.data); +} + +} // namespace virtual_pnor +} // namespace openpower diff --git a/vpnor/pnor_partition_table.hpp b/vpnor/pnor_partition_table.hpp new file mode 100644 index 0000000..10dccdd --- /dev/null +++ b/vpnor/pnor_partition_table.hpp @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2018 IBM Corp. */ +#pragma once + +#include +#include +#include +#include +#include "common.h" +#include "mbox.h" +#include "pnor_partition_defs.h" + +namespace openpower +{ +namespace virtual_pnor +{ + +namespace fs = std::experimental::filesystem; + +using PartitionTable = std::vector; +using checksum_t = uint32_t; + +/** @brief Convert the input partition table to big endian. + * + * @param[in] src - reference to the pnor partition table + * + * @returns converted partition table + */ +PartitionTable endianFixup(const PartitionTable& src); + +/** @brief Parse a ToC line (entry) into the corresponding FFS partition + * object. + * + * @param[in] line - The ToC line to parse + * @param[in] blockSize - The flash block size in bytes + * @param[out] part - The partition object to populate with the information + * parsed from the provided ToC line + * + * Throws: MalformedTocEntry, InvalidTocEntry + */ +void parseTocLine(const std::string& line, size_t blockSize, + pnor_partition& part); + +namespace details +{ + +/** @brief Compute XOR-based checksum, by XORing consecutive words + * in the input data. Input must be aligned to word boundary. + * + * @param[in] data - input data on which checksum is computed + * + * @returns computed checksum + */ +template checksum_t checksum(const T& data) +{ + static_assert(sizeof(decltype(data)) % sizeof(checksum_t) == 0, + "sizeof(data) is not aligned to sizeof(checksum_t) boundary"); + + auto begin = reinterpret_cast(&data); + auto end = begin + (sizeof(decltype(data)) / sizeof(checksum_t)); + + return std::accumulate(begin, end, 0, std::bit_xor()); +} + +} // namespace details + +namespace partition +{ + +/** @class Table + * @brief Generates virtual PNOR partition table. + * + * Generates virtual PNOR partition table upon construction. Reads + * the PNOR information generated by this tool : + * github.com/openbmc/openpower-pnor-code-mgmt/blob/master/generate-squashfs, + * which generates a minimalistic table-of-contents (toc) file and + * individual files to represent various partitions that are of interest - + * these help form the "virtual" PNOR, which is typically a subset of the full + * PNOR image. + * These files are stored in a well-known location on the PNOR. + * Based on this information, this class prepares the partition table whose + * structure is as outlined in pnor_partition.h. + * + * The virtual PNOR supports 4KB erase blocks - partitions must be aligned to + * this size. + */ +class Table +{ + public: + /** @brief Constructor accepting the path of the directory + * that houses the PNOR partition files. + * + * @param[in] ctx - Acquire sizes and paths relevant to the table + * + * Throws MalformedTocEntry, InvalidTocEntry + */ + Table(const struct mbox_context* ctx); + + Table(const Table&) = delete; + Table& operator=(const Table&) = delete; + Table(Table&&) = delete; + Table& operator=(Table&&) = delete; + ~Table() = default; + + /** @brief Return the exact size of partition table in bytes + * + * @returns size_t - size of partition table in bytes + */ + size_t size() const + { + return szBytes; + } + + /** @brief Return aligned size of partition table in bytes + * + * The value returned will be greater-than or equal to size(), and + * aligned to blockSize. + * + * @returns size_t - capacity of partition table in bytes + */ + size_t capacity() const + { + return align_up(szBytes, blockSize); + } + + /** @brief Return the size of partition table in blocks + * + * @returns size_t - size of partition table in blocks + */ + size_t blocks() const + { + return capacity() / blockSize; + } + + /** @brief Return a partition table having byte-ordering + * that the host expects. + * + * The host needs the partion table in big-endian. + * + * @returns const reference to host partition table. + */ + const pnor_partition_table& getHostTable() const + { + return *(reinterpret_cast(hostTbl.data())); + } + + /** @brief Return a little-endian partition table + * + * @returns const reference to native partition table + */ + const pnor_partition_table& getNativeTable() const + { + return *(reinterpret_cast(tbl.data())); + } + + /** @brief Return partition corresponding to PNOR offset, the offset + * is within returned partition. + * + * @param[in] offset - PNOR offset in bytes + * + * @returns const reference to pnor_partition, if found, else an + * exception will be thrown. + * + * Throws: UnmappedOffset + */ + const pnor_partition& partition(size_t offset) const; + + /** @brief Return partition corresponding to input partition name. + * + * @param[in] name - PNOR partition name + * + * @returns const reference to pnor_partition, if found, else an + * exception will be thrown. + * + * Throws: UnknownPartition + */ + const pnor_partition& partition(const std::string& name) const; + + private: + /** @brief Prepares a vector of PNOR partition structures. + * + * @param[in] ctx - An mbox context providing partition locations + * + * Throws: MalformedTocEntry, InvalidTocEntry + */ + void preparePartitions(const struct mbox_context* ctx); + + /** @brief Prepares the PNOR header. + */ + void prepareHeader(); + + /** @brief Allocate memory to hold the partion table. Determine the + * amount needed based on the partition files in the toc file. + * + * @param[in] tocFile - Table of contents file path. + */ + void allocateMemory(const fs::path& tocFile); + + /** @brief Return a little-endian partition table + * + * @returns reference to native partition table + */ + pnor_partition_table& getNativeTable() + { + return *(reinterpret_cast(tbl.data())); + } + + /** @brief Size of the PNOR partition table - + * sizeof(pnor_partition_table) + + * (no. of partitions * sizeof(pnor_partition)), + */ + size_t szBytes; + + /** @brief Partition table */ + PartitionTable tbl; + + /** @brief Partition table with host byte ordering */ + PartitionTable hostTbl; + + /** @brief Directory housing generated PNOR partition files */ + fs::path directory; + + /** @brief Number of partitions */ + size_t numParts; + + /** @brief PNOR block size, in bytes */ + size_t blockSize; + + /** @brief PNOR size, in bytes */ + size_t pnorSize; +}; +} // namespace partition + +/** @brief An exception type storing a reason string. + * + * This looks a lot like how std::runtime_error might be implemented however + * we want to avoid extending it, as exceptions extending ReasonedError have + * an expectation of being handled (can be predicted and are inside the scope + * of the program). + * + * From std::runtime_error documentation[1]: + * + * > Defines a type of object to be thrown as exception. It reports errors + * > that are due to events beyond the scope of the program and can not be + * > easily predicted. + * + * [1] http://en.cppreference.com/w/cpp/error/runtime_error + * + * We need to keep the inheritance hierarchy separate: This avoids the + * introduction of code that overzealously catches std::runtime_error to + * handle exceptions that would otherwise derive ReasonedError, and in the + * process swallows genuine runtime failures. + */ +class ReasonedError : public std::exception +{ + public: + ReasonedError(const std::string&& what) : _what(what) + { + } + const char* what() const noexcept + { + return _what.c_str(); + }; + + private: + const std::string _what; +}; + +/** @brief Base exception type for errors related to ToC entry parsing. + * + * Callers of parseTocEntry() may not be concerned with the specifics and + * rather just want to extract and log what(). + */ +class TocEntryError : public ReasonedError +{ + public: + TocEntryError(const std::string&& reason) : ReasonedError(std::move(reason)) + { + } +}; + +/** @brief The exception thrown on finding a syntax error in the ToC entry + * + * If the syntax is wrong, or expected values are missing, the ToC entry is + * malformed + */ +class MalformedTocEntry : public TocEntryError +{ + public: + MalformedTocEntry(const std::string&& reason) : + TocEntryError(std::move(reason)) + { + } +}; + +/** @brief The exception thrown on finding a semantic error in the ToC entry + * + * If the syntax of the ToC entry is correct but the semantics are broken, + * then we have an invalid ToC entry. + */ +class InvalidTocEntry : public TocEntryError +{ + public: + InvalidTocEntry(const std::string&& reason) : + TocEntryError(std::move(reason)) + { + } +}; + +class UnmappedOffset : public std::exception +{ + public: + UnmappedOffset(size_t base, size_t next) : base(base), next(next) + { + } + + const size_t base; + const size_t next; +}; + +class OutOfBoundsOffset : public ReasonedError +{ + public: + OutOfBoundsOffset(const std::string&& reason) : + ReasonedError(std::move(reason)) + { + } +}; + +class UnknownPartition : public ReasonedError +{ + public: + UnknownPartition(const std::string&& reason) : + ReasonedError(std::move(reason)) + { + } +}; + +} // namespace virtual_pnor +} // namespace openpower + +struct vpnor_partition_table +{ + openpower::virtual_pnor::partition::Table* table; +}; -- cgit v1.2.1