blob: 71d8aeec66d684d856171ff23ba6b338369c146a [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:
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
96namespace 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 */
105inline 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 */
125class 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 */
184template <typename T>
185void 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
Deepak Kodihallid0782962019-05-07 16:36:47 +0530241 using namespace effecter::dbus_mapping;
242 Paths paths{};
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500243 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;
Deepak Kodihallid0782962019-05-07 16:36:47 +0530269
270 auto dbus = effecter.value("dbus", empty);
271 paths.emplace_back(std::move(dbus));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500272 }
Deepak Kodihallid0782962019-05-07 16:36:47 +0530273 add(pdr->effecter_id, std::move(paths));
Deepak Kodihallic6e8fb52019-05-02 08:35:31 -0500274 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 */
314Repo& get(const std::string& dir);
315
316} // namespace pdr
317} // namespace responder
318} // namespace pldm