blob: 88c8fb9e463377f58839c8bf2a4159a2dc59f1ff [file] [log] [blame]
/* 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 "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] directory - path of the directory housing PNOR partitions
* @param[in] blockSize - PNOR block size, in bytes. See
* open-power/hostboot/blob/master/src/usr/pnor/ffs.h for
* the PNOR FFS structure.
* @param[in] pnorSize - PNOR size, in bytes
*
* Throws MalformedTocEntry, InvalidTocEntry
*/
Table(fs::path&& directory, size_t blockSize, size_t pnorSize);
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.
*
* Throws: MalformedTocEntry, InvalidTocEntry
*/
void preparePartitions();
/** @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;
};