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