blob: c9df51f72d870efed7687d3c906a483e90ac9ecc [file] [log] [blame]
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -05001#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
20using namespace phosphor::logging;
21using InternalFailure =
22 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
23namespace fs = std::filesystem;
24
25namespace pldm
26{
27
28namespace responder
29{
30
31namespace pdr
32{
33
34using Type = uint8_t;
35using Json = nlohmann::json;
36using RecordHandle = uint32_t;
37using Entry = std::vector<uint8_t>;
38using 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 */
47class Repo
48{
49 public:
Deepak Kodihalli3c275e12019-09-21 06:39:39 -050050 virtual ~Repo() = default;
51
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -050052 /** @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
98namespace 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 */
107inline 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 */
127class 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 */
186template <typename T>
187void 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
Deepak Kodihallid0782962019-05-07 16:36:47 +0530243 using namespace effecter::dbus_mapping;
244 Paths paths{};
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500245 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;
Deepak Kodihallid0782962019-05-07 16:36:47 +0530271
272 auto dbus = effecter.value("dbus", empty);
273 paths.emplace_back(std::move(dbus));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500274 }
Deepak Kodihallid0782962019-05-07 16:36:47 +0530275 add(pdr->effecter_id, std::move(paths));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500276 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 */
316Repo& get(const std::string& dir);
317
318} // namespace pdr
319} // namespace responder
320} // namespace pldm