1 /* SPDX-License-Identifier: Apache-2.0 */ 2 /* Copyright (C) 2018 IBM Corp. */ 3 #pragma once 4 5 #include <vector> 6 #include <memory> 7 #include <numeric> 8 #include <experimental/filesystem> 9 #include "common.h" 10 #include "mbox.h" 11 #include "pnor_partition_defs.h" 12 13 namespace openpower 14 { 15 namespace virtual_pnor 16 { 17 18 namespace fs = std::experimental::filesystem; 19 20 using PartitionTable = std::vector<uint8_t>; 21 using checksum_t = uint32_t; 22 23 /** @brief Convert the input partition table to big endian. 24 * 25 * @param[in] src - reference to the pnor partition table 26 * 27 * @returns converted partition table 28 */ 29 PartitionTable endianFixup(const PartitionTable& src); 30 31 /** @brief Parse a ToC line (entry) into the corresponding FFS partition 32 * object. 33 * 34 * @param[in] line - The ToC line to parse 35 * @param[in] blockSize - The flash block size in bytes 36 * @param[out] part - The partition object to populate with the information 37 * parsed from the provided ToC line 38 * 39 * Throws: MalformedTocEntry, InvalidTocEntry 40 */ 41 void parseTocLine(const std::string& line, size_t blockSize, 42 pnor_partition& part); 43 44 namespace details 45 { 46 47 /** @brief Compute XOR-based checksum, by XORing consecutive words 48 * in the input data. Input must be aligned to word boundary. 49 * 50 * @param[in] data - input data on which checksum is computed 51 * 52 * @returns computed checksum 53 */ 54 template <class T> checksum_t checksum(const T& data) 55 { 56 static_assert(sizeof(decltype(data)) % sizeof(checksum_t) == 0, 57 "sizeof(data) is not aligned to sizeof(checksum_t) boundary"); 58 59 auto begin = reinterpret_cast<const checksum_t*>(&data); 60 auto end = begin + (sizeof(decltype(data)) / sizeof(checksum_t)); 61 62 return std::accumulate(begin, end, 0, std::bit_xor<checksum_t>()); 63 } 64 65 } // namespace details 66 67 namespace partition 68 { 69 70 /** @class Table 71 * @brief Generates virtual PNOR partition table. 72 * 73 * Generates virtual PNOR partition table upon construction. Reads 74 * the PNOR information generated by this tool : 75 * github.com/openbmc/openpower-pnor-code-mgmt/blob/master/generate-squashfs, 76 * which generates a minimalistic table-of-contents (toc) file and 77 * individual files to represent various partitions that are of interest - 78 * these help form the "virtual" PNOR, which is typically a subset of the full 79 * PNOR image. 80 * These files are stored in a well-known location on the PNOR. 81 * Based on this information, this class prepares the partition table whose 82 * structure is as outlined in pnor_partition.h. 83 * 84 * The virtual PNOR supports 4KB erase blocks - partitions must be aligned to 85 * this size. 86 */ 87 class Table 88 { 89 public: 90 /** @brief Constructor accepting the path of the directory 91 * that houses the PNOR partition files. 92 * 93 * @param[in] ctx - Acquire sizes and paths relevant to the table 94 * 95 * Throws MalformedTocEntry, InvalidTocEntry 96 */ 97 Table(const struct mbox_context* ctx); 98 99 Table(const Table&) = delete; 100 Table& operator=(const Table&) = delete; 101 Table(Table&&) = delete; 102 Table& operator=(Table&&) = delete; 103 ~Table() = default; 104 105 /** @brief Return the exact size of partition table in bytes 106 * 107 * @returns size_t - size of partition table in bytes 108 */ 109 size_t size() const 110 { 111 return szBytes; 112 } 113 114 /** @brief Return aligned size of partition table in bytes 115 * 116 * The value returned will be greater-than or equal to size(), and 117 * aligned to blockSize. 118 * 119 * @returns size_t - capacity of partition table in bytes 120 */ 121 size_t capacity() const 122 { 123 return align_up(szBytes, blockSize); 124 } 125 126 /** @brief Return the size of partition table in blocks 127 * 128 * @returns size_t - size of partition table in blocks 129 */ 130 size_t blocks() const 131 { 132 return capacity() / blockSize; 133 } 134 135 /** @brief Return a partition table having byte-ordering 136 * that the host expects. 137 * 138 * The host needs the partion table in big-endian. 139 * 140 * @returns const reference to host partition table. 141 */ 142 const pnor_partition_table& getHostTable() const 143 { 144 return *(reinterpret_cast<const pnor_partition_table*>(hostTbl.data())); 145 } 146 147 /** @brief Return a little-endian partition table 148 * 149 * @returns const reference to native partition table 150 */ 151 const pnor_partition_table& getNativeTable() const 152 { 153 return *(reinterpret_cast<const pnor_partition_table*>(tbl.data())); 154 } 155 156 /** @brief Return partition corresponding to PNOR offset, the offset 157 * is within returned partition. 158 * 159 * @param[in] offset - PNOR offset in bytes 160 * 161 * @returns const reference to pnor_partition, if found, else an 162 * exception will be thrown. 163 * 164 * Throws: UnmappedOffset 165 */ 166 const pnor_partition& partition(size_t offset) const; 167 168 /** @brief Return partition corresponding to input partition name. 169 * 170 * @param[in] name - PNOR partition name 171 * 172 * @returns const reference to pnor_partition, if found, else an 173 * exception will be thrown. 174 * 175 * Throws: UnknownPartition 176 */ 177 const pnor_partition& partition(const std::string& name) const; 178 179 private: 180 /** @brief Prepares a vector of PNOR partition structures. 181 * 182 * @param[in] ctx - An mbox context providing partition locations 183 * 184 * Throws: MalformedTocEntry, InvalidTocEntry 185 */ 186 void preparePartitions(const struct mbox_context* ctx); 187 188 /** @brief Prepares the PNOR header. 189 */ 190 void prepareHeader(); 191 192 /** @brief Allocate memory to hold the partion table. Determine the 193 * amount needed based on the partition files in the toc file. 194 * 195 * @param[in] tocFile - Table of contents file path. 196 */ 197 void allocateMemory(const fs::path& tocFile); 198 199 /** @brief Return a little-endian partition table 200 * 201 * @returns reference to native partition table 202 */ 203 pnor_partition_table& getNativeTable() 204 { 205 return *(reinterpret_cast<pnor_partition_table*>(tbl.data())); 206 } 207 208 /** @brief Size of the PNOR partition table - 209 * sizeof(pnor_partition_table) + 210 * (no. of partitions * sizeof(pnor_partition)), 211 */ 212 size_t szBytes; 213 214 /** @brief Partition table */ 215 PartitionTable tbl; 216 217 /** @brief Partition table with host byte ordering */ 218 PartitionTable hostTbl; 219 220 /** @brief Directory housing generated PNOR partition files */ 221 fs::path directory; 222 223 /** @brief Number of partitions */ 224 size_t numParts; 225 226 /** @brief PNOR block size, in bytes */ 227 size_t blockSize; 228 229 /** @brief PNOR size, in bytes */ 230 size_t pnorSize; 231 }; 232 } // namespace partition 233 234 /** @brief An exception type storing a reason string. 235 * 236 * This looks a lot like how std::runtime_error might be implemented however 237 * we want to avoid extending it, as exceptions extending ReasonedError have 238 * an expectation of being handled (can be predicted and are inside the scope 239 * of the program). 240 * 241 * From std::runtime_error documentation[1]: 242 * 243 * > Defines a type of object to be thrown as exception. It reports errors 244 * > that are due to events beyond the scope of the program and can not be 245 * > easily predicted. 246 * 247 * [1] http://en.cppreference.com/w/cpp/error/runtime_error 248 * 249 * We need to keep the inheritance hierarchy separate: This avoids the 250 * introduction of code that overzealously catches std::runtime_error to 251 * handle exceptions that would otherwise derive ReasonedError, and in the 252 * process swallows genuine runtime failures. 253 */ 254 class ReasonedError : public std::exception 255 { 256 public: 257 ReasonedError(const std::string&& what) : _what(what) 258 { 259 } 260 const char* what() const noexcept 261 { 262 return _what.c_str(); 263 }; 264 265 private: 266 const std::string _what; 267 }; 268 269 /** @brief Base exception type for errors related to ToC entry parsing. 270 * 271 * Callers of parseTocEntry() may not be concerned with the specifics and 272 * rather just want to extract and log what(). 273 */ 274 class TocEntryError : public ReasonedError 275 { 276 public: 277 TocEntryError(const std::string&& reason) : ReasonedError(std::move(reason)) 278 { 279 } 280 }; 281 282 /** @brief The exception thrown on finding a syntax error in the ToC entry 283 * 284 * If the syntax is wrong, or expected values are missing, the ToC entry is 285 * malformed 286 */ 287 class MalformedTocEntry : public TocEntryError 288 { 289 public: 290 MalformedTocEntry(const std::string&& reason) : 291 TocEntryError(std::move(reason)) 292 { 293 } 294 }; 295 296 /** @brief The exception thrown on finding a semantic error in the ToC entry 297 * 298 * If the syntax of the ToC entry is correct but the semantics are broken, 299 * then we have an invalid ToC entry. 300 */ 301 class InvalidTocEntry : public TocEntryError 302 { 303 public: 304 InvalidTocEntry(const std::string&& reason) : 305 TocEntryError(std::move(reason)) 306 { 307 } 308 }; 309 310 class UnmappedOffset : public std::exception 311 { 312 public: 313 UnmappedOffset(size_t base, size_t next) : base(base), next(next) 314 { 315 } 316 317 const size_t base; 318 const size_t next; 319 }; 320 321 class OutOfBoundsOffset : public ReasonedError 322 { 323 public: 324 OutOfBoundsOffset(const std::string&& reason) : 325 ReasonedError(std::move(reason)) 326 { 327 } 328 }; 329 330 class UnknownPartition : public ReasonedError 331 { 332 public: 333 UnknownPartition(const std::string&& reason) : 334 ReasonedError(std::move(reason)) 335 { 336 } 337 }; 338 339 } // namespace virtual_pnor 340 } // namespace openpower 341 342 struct vpnor_partition_table 343 { 344 openpower::virtual_pnor::partition::Table* table; 345 }; 346