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