blob: b32f878b75a9b916c01a8f36aa66ffd76f144ba7 [file] [log] [blame]
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -05001#pragma once
2
3#include "effecters.hpp"
Sampa Misraaa8ae722019-12-12 03:20:40 -06004#include "utils.hpp"
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -05005
6#include <stdint.h>
7
8#include <filesystem>
9#include <fstream>
10#include <functional>
Sampa Misraaa8ae722019-12-12 03:20:40 -060011#include <iostream>
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -050012#include <map>
13#include <nlohmann/json.hpp>
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -050014#include <string>
15#include <vector>
16#include <xyz/openbmc_project/Common/error.hpp>
17
18#include "libpldm/platform.h"
19
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -050020using InternalFailure =
21 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
22namespace fs = std::filesystem;
23
24namespace pldm
25{
26
27namespace responder
28{
29
30namespace pdr
31{
32
33using Type = uint8_t;
34using Json = nlohmann::json;
35using RecordHandle = uint32_t;
36using Entry = std::vector<uint8_t>;
37using 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 */
46class Repo
47{
48 public:
Deepak Kodihalli3c275e12019-09-21 06:39:39 -050049 virtual ~Repo() = default;
50
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -050051 /** @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
97namespace 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 */
106inline Json readJson(const std::string& path)
107{
108 std::ifstream jsonFile(path);
109 if (!jsonFile.is_open())
110 {
Sampa Misraaa8ae722019-12-12 03:20:40 -0600111 std::cout << "Error opening PDR JSON file, PATH=" << path.c_str()
112 << std::endl;
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500113 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 */
126class 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 */
185template <typename T>
186void 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 {
Sampa Misraaa8ae722019-12-12 03:20:40 -0600209 std::cerr
210 << "Malformed PDR JSON - no state set info, TYPE="
211 << PLDM_STATE_EFFECTER_PDR << "\n";
212 throw InternalFailure();
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500213 }
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
Deepak Kodihallid0782962019-05-07 16:36:47 +0530242 using namespace effecter::dbus_mapping;
243 Paths paths{};
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500244 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;
Deepak Kodihallid0782962019-05-07 16:36:47 +0530270
271 auto dbus = effecter.value("dbus", empty);
272 paths.emplace_back(std::move(dbus));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500273 }
Deepak Kodihallid0782962019-05-07 16:36:47 +0530274 add(pdr->effecter_id, std::move(paths));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500275 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 }
Sampa Misraaa8ae722019-12-12 03:20:40 -0600297 catch (const Json::exception& e)
298 {
299 std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
300 << " ERROR=" << e.what() << "\n";
George Liu83409572019-12-24 18:42:54 +0800301 pldm::utils::reportError(
302 "xyz.openbmc_project.bmc.pldm.InternalFailure");
Sampa Misraaa8ae722019-12-12 03:20:40 -0600303 }
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500304 catch (const std::exception& e)
305 {
Sampa Misraaa8ae722019-12-12 03:20:40 -0600306 std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
307 << " ERROR=" << e.what() << "\n";
George Liu83409572019-12-24 18:42:54 +0800308 pldm::utils::reportError(
309 "xyz.openbmc_project.bmc.pldm.InternalFailure");
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500310 }
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 */
322Repo& get(const std::string& dir);
323
324} // namespace pdr
325} // namespace responder
326} // namespace pldm