xref: /openbmc/hiomapd/vpnor/table.hpp (revision 81d27b04)
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