/* 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; };