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