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