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