1 #pragma once 2 3 #include "effecters.hpp" 4 #include "utils.hpp" 5 6 #include <stdint.h> 7 8 #include <filesystem> 9 #include <fstream> 10 #include <functional> 11 #include <iostream> 12 #include <map> 13 #include <nlohmann/json.hpp> 14 #include <string> 15 #include <vector> 16 #include <xyz/openbmc_project/Common/error.hpp> 17 18 #include "libpldm/platform.h" 19 20 using InternalFailure = 21 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 22 namespace fs = std::filesystem; 23 24 namespace pldm 25 { 26 27 namespace responder 28 { 29 30 namespace pdr 31 { 32 33 using Type = uint8_t; 34 using Json = nlohmann::json; 35 using RecordHandle = uint32_t; 36 using Entry = std::vector<uint8_t>; 37 using 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 */ 46 class Repo 47 { 48 public: 49 virtual ~Repo() = default; 50 51 /** @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 97 namespace 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 */ 106 inline Json readJson(const std::string& path) 107 { 108 std::ifstream jsonFile(path); 109 if (!jsonFile.is_open()) 110 { 111 std::cout << "Error opening PDR JSON file, PATH=" << path.c_str() 112 << std::endl; 113 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 */ 126 class 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 */ 185 template <typename T> 186 void 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 { 209 std::cerr 210 << "Malformed PDR JSON - no state set info, TYPE=" 211 << PLDM_STATE_EFFECTER_PDR << "\n"; 212 throw InternalFailure(); 213 } 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 242 using namespace effecter::dbus_mapping; 243 Paths paths{}; 244 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; 270 271 auto dbus = effecter.value("dbus", empty); 272 paths.emplace_back(std::move(dbus)); 273 } 274 add(pdr->effecter_id, std::move(paths)); 275 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 } 297 catch (const Json::exception& e) 298 { 299 std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType 300 << " ERROR=" << e.what() << "\n"; 301 pldm::utils::reportError( 302 "xyz.openbmc_project.bmc.pldm.InternalFailure"); 303 } 304 catch (const std::exception& e) 305 { 306 std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType 307 << " ERROR=" << e.what() << "\n"; 308 pldm::utils::reportError( 309 "xyz.openbmc_project.bmc.pldm.InternalFailure"); 310 } 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 */ 322 Repo& get(const std::string& dir); 323 324 } // namespace pdr 325 } // namespace responder 326 } // namespace pldm 327