diff options
Diffstat (limited to 'vpnor')
-rw-r--r-- | vpnor/Makefile.am.include | 10 | ||||
-rw-r--r-- | vpnor/mboxd_flash.cpp | 235 | ||||
-rw-r--r-- | vpnor/mboxd_lpc_reset.cpp | 36 | ||||
-rw-r--r-- | vpnor/mboxd_pnor_partition_table.cpp | 132 | ||||
-rw-r--r-- | vpnor/mboxd_pnor_partition_table.h | 67 | ||||
-rw-r--r-- | vpnor/pnor_partition.cpp | 163 | ||||
-rw-r--r-- | vpnor/pnor_partition.hpp | 139 | ||||
-rw-r--r-- | vpnor/pnor_partition_defs.h | 136 | ||||
-rw-r--r-- | vpnor/pnor_partition_table.cpp | 376 | ||||
-rw-r--r-- | vpnor/pnor_partition_table.hpp | 345 |
10 files changed, 1639 insertions, 0 deletions
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 <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <syslog.h> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <algorithm> + +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 <phosphor-logging/log.hpp> +#include <phosphor-logging/elog-errors.hpp> + +#include <memory> +#include <string> +#include <exception> +#include <stdexcept> + +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<char, StringDeleter>; + +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_t>(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<err::InternalFailure>(); + 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<err::InternalFailure>(); + 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 <phosphor-logging/elog-errors.hpp> +#include <experimental/filesystem> + +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<err::InternalFailure>(); + 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<uint8_t *>(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<uint8_t *>(context->mem) + hbbOffset, hbbSize); + } + catch (err::InternalFailure &e) + { + phosphor::logging::commit<err::InternalFailure>(); + return -MBOX_R_SYSTEM_ERROR; + } + catch (vpnor::TocEntryError &e) + { + MSG_ERR("%s\n", e.what()); + phosphor::logging::commit<err::InternalFailure>(); + 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 <limits.h> +#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 <phosphor-logging/log.hpp> +#include <phosphor-logging/elog-errors.hpp> + +#include <assert.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "common.h" + +#include <string> +#include <exception> +#include <stdexcept> +#include <iostream> + +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 <fcntl.h> +#include <string> +#include <unistd.h> +#include <experimental/filesystem> + +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 <stdint.h> +#include <sys/types.h> + +/* 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 <phosphor-logging/elog-errors.hpp> +#include <syslog.h> +#include <endian.h> +#include <regex> +#include <fstream> +#include <algorithm> + +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<size_t>(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<const pnor_partition_table*>(in.data()); + auto dst = reinterpret_cast<pnor_partition_table*>(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 <vector> +#include <memory> +#include <numeric> +#include <experimental/filesystem> +#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<uint8_t>; +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 <class T> 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<const checksum_t*>(&data); + auto end = begin + (sizeof(decltype(data)) / sizeof(checksum_t)); + + return std::accumulate(begin, end, 0, std::bit_xor<checksum_t>()); +} + +} // 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<const pnor_partition_table*>(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<const pnor_partition_table*>(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<pnor_partition_table*>(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; +}; |