blob: 9cbfbac946ef636340e6ddf523e2a5442b4c65a2 [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
241 uint8_t* start = pdrEntry.data() +
242 sizeof(pldm_state_effecter_pdr) -
243 sizeof(uint8_t);
244 for (const auto& effecter : effecters)
245 {
246 auto set = effecter.value("set", empty);
247 state_effecter_possible_states* possibleStates =
248 reinterpret_cast<state_effecter_possible_states*>(
249 start);
250 possibleStates->state_set_id = set.value("id", 0);
251 possibleStates->possible_states_size =
252 set.value("size", 0);
253
254 start += sizeof(possibleStates->state_set_id) +
255 sizeof(possibleStates->possible_states_size);
256 static const std::vector<uint8_t> emptyStates{};
257 auto states = set.value("states", emptyStates);
258 for (const auto& state : states)
259 {
260 auto index = state / 8;
261 auto bit = state - (index * 8);
262 bitfield8_t* bf =
263 reinterpret_cast<bitfield8_t*>(start + index);
264 bf->byte |= 1 << bit;
265 }
266 start += possibleStates->possible_states_size;
267 }
268 repo.add(std::move(pdrEntry));
269 }
270 }}};
271
272 auto eraseLen = strlen(".json");
273 Type pdrType{};
274 for (const auto& dirEntry : fs::directory_iterator(dir))
275 {
276 try
277 {
278 auto json = readJson(dirEntry.path().string());
279 if (!json.empty())
280 {
281 auto fileName = dirEntry.path().filename().string();
282 fileName.erase(fileName.end() - eraseLen);
283 pdrType = stoi(fileName);
284 generators.at(pdrType)(json, repo);
285 }
286 }
287 catch (const InternalFailure& e)
288 {
289 }
290 catch (const std::exception& e)
291 {
292 log<level::ERR>("Failed parsing PDR JSON file",
293 entry("TYPE=%d", pdrType),
294 entry("ERROR=%s", e.what()));
295 report<InternalFailure>();
296 }
297 }
298}
299
300} // namespace internal
301
302/** @brief Build (if not built already) and retrieve PDR
303 *
304 * @param[in] dir - directory housing platform specific PDR JSON files
305 *
306 * @return Repo& - Reference to instance of pdr::Repo
307 */
308Repo& get(const std::string& dir);
309
310} // namespace pdr
311} // namespace responder
312} // namespace pldm