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