xref: /openbmc/hiomapd/vpnor/table.hpp (revision 150be912)
1 /* SPDX-License-Identifier: Apache-2.0 */
2 /* Copyright (C) 2018 IBM Corp. */
3 #pragma once
4 
5 #include <cstring>
6 #include <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::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>
checksum(const T & data)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      */
size() const119     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      */
capacity() const131     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      */
blocks() const140     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      */
getHostTable() const152     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      */
getNativeTable() const161     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      */
getNativeTable()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:
ReasonedError(const std::string && what)267     ReasonedError(const std::string&& what) : _what(what)
268     {
269     }
what() const270     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:
TocEntryError(const std::string && reason)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:
MalformedTocEntry(const std::string && reason)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:
InvalidTocEntry(const std::string && reason)314     InvalidTocEntry(const std::string&& reason) :
315         TocEntryError(std::move(reason))
316     {
317     }
318 };
319 
320 class UnmappedOffset : public std::exception
321 {
322   public:
UnmappedOffset(size_t base,size_t next)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:
OutOfBoundsOffset(const std::string && reason)334     OutOfBoundsOffset(const std::string&& reason) :
335         ReasonedError(std::move(reason))
336     {
337     }
338 };
339 
340 class UnknownPartition : public ReasonedError
341 {
342   public:
UnknownPartition(const std::string && reason)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