xref: /openbmc/pldm/libpldmresponder/pdr.hpp (revision 1f4df219)
1 #pragma once
2 
3 #include "effecters.hpp"
4 #include "utils.hpp"
5 
6 #include <stdint.h>
7 
8 #include <filesystem>
9 #include <fstream>
10 #include <functional>
11 #include <iostream>
12 #include <map>
13 #include <nlohmann/json.hpp>
14 #include <string>
15 #include <vector>
16 #include <xyz/openbmc_project/Common/error.hpp>
17 
18 #include "libpldm/platform.h"
19 
20 using InternalFailure =
21     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
22 namespace fs = std::filesystem;
23 
24 namespace pldm
25 {
26 
27 namespace responder
28 {
29 
30 namespace pdr
31 {
32 
33 using Type = uint8_t;
34 using Json = nlohmann::json;
35 using RecordHandle = uint32_t;
36 using Entry = std::vector<uint8_t>;
37 using Pdr = std::vector<Entry>;
38 
39 /** @class Repo
40  *
41  *  @brief Abstract class describing the interface API to the PDR repository
42  *
43  *  Concrete implementations of this must handle storing and addressing the
44  *  PDR entries by a "record handle", which can be indices, offsets, etc.
45  */
46 class Repo
47 {
48   public:
49     virtual ~Repo() = default;
50 
51     /** @brief Add a new entry to the PDR
52      *
53      *  @param[in] entry - new PDR entry
54      */
55     virtual void add(Entry&& entry) = 0;
56 
57     /** @brief Access PDR entry at inout record handle
58      *
59      *  @param[in] handle - record handle
60      *
61      *  @return Entry - PDR entry
62      */
63     virtual Entry at(RecordHandle handle) const = 0;
64 
65     /** @brief Get next available record handle for assignment
66      *
67      *  @return RecordHandle - PDR record handle
68      */
69     virtual RecordHandle getNextRecordHandle() const = 0;
70 
71     /** @brief Get record handle immediately suceeding the input record
72      *         handle
73      *
74      *  @param[in] current - input record handle
75      *
76      *  @return RecordHandle - PDR record handle
77      */
78     virtual RecordHandle getNextRecordHandle(RecordHandle current) const = 0;
79 
80     /** @brief Get number of entries in the PDR
81      *
82      *  @return size_t - number of entries
83      */
84     virtual size_t numEntries() const = 0;
85 
86     /** @brief Check if PDR is empty
87      *
88      *  @return bool - true if PDR is empty, false otherwise
89      */
90     virtual bool empty() const = 0;
91 
92     /** @brief Empty the PDR
93      */
94     virtual void makeEmpty() = 0;
95 };
96 
97 namespace internal
98 {
99 
100 /** @brief Parse PDR JSON file and output Json object
101  *
102  *  @param[in] path - path of PDR JSON file
103  *
104  *  @return Json - Json object
105  */
106 inline Json readJson(const std::string& path)
107 {
108     std::ifstream jsonFile(path);
109     if (!jsonFile.is_open())
110     {
111         std::cout << "Error opening PDR JSON file, PATH=" << path.c_str()
112                   << std::endl;
113         return {};
114     }
115 
116     return Json::parse(jsonFile);
117 }
118 
119 /** @class IndexedRepo
120  *
121  *  @brief Inherits and implements Repo
122  *
123  *  Stores the PDR as a vector of entries, and addresses PDR entries based on an
124  *  incrementing record handle, starting at 1.
125  */
126 class IndexedRepo : public Repo
127 {
128   public:
129     void add(Entry&& entry)
130     {
131         repo.emplace_back(std::move(entry));
132     }
133 
134     Entry at(RecordHandle handle) const
135     {
136         if (!handle)
137         {
138             handle = 1;
139         }
140         return repo.at(handle - 1);
141     }
142 
143     RecordHandle getNextRecordHandle() const
144     {
145         return repo.size() + 1;
146     }
147 
148     RecordHandle getNextRecordHandle(RecordHandle current) const
149     {
150         if (current >= repo.size())
151         {
152             return 0;
153         }
154         if (!current)
155         {
156             current = 1;
157         }
158         return current + 1;
159     }
160 
161     size_t numEntries() const
162     {
163         return repo.size();
164     }
165 
166     bool empty() const
167     {
168         return repo.empty();
169     }
170 
171     void makeEmpty()
172     {
173         repo.clear();
174     }
175 
176   private:
177     Pdr repo{};
178 };
179 
180 /** @brief Parse PDR JSONs and build PDR repository
181  *
182  *  @param[in] dir - directory housing platform specific PDR JSON files
183  *  @tparam[in] repo - instance of concrete implementation of Repo
184  */
185 template <typename T>
186 void generate(const std::string& dir, T& repo)
187 {
188     using namespace internal;
189     // A map of PDR type to a lambda that handles creation of that PDR type.
190     // The lambda essentially would parse the platform specific PDR JSONs to
191     // generate the PDR structures. This function iterates through the map to
192     // invoke all lambdas, so that all PDR types can be created.
193     std::map<Type, std::function<void(const Json& json, T& repo)>> generators =
194         {{PLDM_STATE_EFFECTER_PDR, [](const auto& json, T& repo) {
195               static const std::vector<Json> emptyList{};
196               static const Json empty{};
197               auto entries = json.value("entries", emptyList);
198               for (const auto& e : entries)
199               {
200                   size_t pdrSize = 0;
201                   auto effecters = e.value("effecters", emptyList);
202                   static const Json empty{};
203                   for (const auto& effecter : effecters)
204                   {
205                       auto set = effecter.value("set", empty);
206                       auto statesSize = set.value("size", 0);
207                       if (!statesSize)
208                       {
209                           std::cerr
210                               << "Malformed PDR JSON - no state set info, TYPE="
211                               << PLDM_STATE_EFFECTER_PDR << "\n";
212                           throw InternalFailure();
213                       }
214                       pdrSize += sizeof(state_effecter_possible_states) -
215                                  sizeof(bitfield8_t) +
216                                  (sizeof(bitfield8_t) * statesSize);
217                   }
218                   pdrSize += sizeof(pldm_state_effecter_pdr) - sizeof(uint8_t);
219 
220                   Entry pdrEntry{};
221                   pdrEntry.resize(pdrSize);
222 
223                   pldm_state_effecter_pdr* pdr =
224                       reinterpret_cast<pldm_state_effecter_pdr*>(
225                           pdrEntry.data());
226                   pdr->hdr.record_handle = repo.getNextRecordHandle();
227                   pdr->hdr.version = 1;
228                   pdr->hdr.type = PLDM_STATE_EFFECTER_PDR;
229                   pdr->hdr.record_change_num = 0;
230                   pdr->hdr.length = pdrSize - sizeof(pldm_pdr_hdr);
231 
232                   pdr->terminus_handle = 0;
233                   pdr->effecter_id = effecter::nextId();
234                   pdr->entity_type = e.value("type", 0);
235                   pdr->entity_instance = e.value("instance", 0);
236                   pdr->container_id = e.value("container", 0);
237                   pdr->effecter_semantic_id = 0;
238                   pdr->effecter_init = PLDM_NO_INIT;
239                   pdr->has_description_pdr = false;
240                   pdr->composite_effecter_count = effecters.size();
241 
242                   using namespace effecter::dbus_mapping;
243                   Paths paths{};
244                   uint8_t* start = pdrEntry.data() +
245                                    sizeof(pldm_state_effecter_pdr) -
246                                    sizeof(uint8_t);
247                   for (const auto& effecter : effecters)
248                   {
249                       auto set = effecter.value("set", empty);
250                       state_effecter_possible_states* possibleStates =
251                           reinterpret_cast<state_effecter_possible_states*>(
252                               start);
253                       possibleStates->state_set_id = set.value("id", 0);
254                       possibleStates->possible_states_size =
255                           set.value("size", 0);
256 
257                       start += sizeof(possibleStates->state_set_id) +
258                                sizeof(possibleStates->possible_states_size);
259                       static const std::vector<uint8_t> emptyStates{};
260                       auto states = set.value("states", emptyStates);
261                       for (const auto& state : states)
262                       {
263                           auto index = state / 8;
264                           auto bit = state - (index * 8);
265                           bitfield8_t* bf =
266                               reinterpret_cast<bitfield8_t*>(start + index);
267                           bf->byte |= 1 << bit;
268                       }
269                       start += possibleStates->possible_states_size;
270 
271                       auto dbus = effecter.value("dbus", empty);
272                       paths.emplace_back(std::move(dbus));
273                   }
274                   add(pdr->effecter_id, std::move(paths));
275                   repo.add(std::move(pdrEntry));
276               }
277           }}};
278 
279     auto eraseLen = strlen(".json");
280     Type pdrType{};
281     for (const auto& dirEntry : fs::directory_iterator(dir))
282     {
283         try
284         {
285             auto json = readJson(dirEntry.path().string());
286             if (!json.empty())
287             {
288                 auto fileName = dirEntry.path().filename().string();
289                 fileName.erase(fileName.end() - eraseLen);
290                 pdrType = stoi(fileName);
291                 generators.at(pdrType)(json, repo);
292             }
293         }
294         catch (const InternalFailure& e)
295         {
296         }
297         catch (const Json::exception& e)
298         {
299             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
300                       << " ERROR=" << e.what() << "\n";
301             pldm::utils::reportError(
302                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
303         }
304         catch (const std::exception& e)
305         {
306             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
307                       << " ERROR=" << e.what() << "\n";
308             pldm::utils::reportError(
309                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
310         }
311     }
312 }
313 
314 } // namespace internal
315 
316 /** @brief Build (if not built already) and retrieve PDR
317  *
318  *  @param[in] dir - directory housing platform specific PDR JSON files
319  *
320  *  @return Repo& - Reference to instance of pdr::Repo
321  */
322 Repo& get(const std::string& dir);
323 
324 } // namespace pdr
325 } // namespace responder
326 } // namespace pldm
327