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