diff options
Diffstat (limited to 'vpnor/pnor_partition_table.hpp')
-rw-r--r-- | vpnor/pnor_partition_table.hpp | 345 |
1 files changed, 345 insertions, 0 deletions
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; +}; |