1 #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 20 using namespace phosphor::logging; 21 using InternalFailure = 22 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 23 namespace fs = std::filesystem; 24 25 namespace pldm 26 { 27 28 namespace responder 29 { 30 31 namespace pdr 32 { 33 34 using Type = uint8_t; 35 using Json = nlohmann::json; 36 using RecordHandle = uint32_t; 37 using Entry = std::vector<uint8_t>; 38 using 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 */ 47 class 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 96 namespace 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 */ 105 inline 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 */ 125 class 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 */ 184 template <typename T> 185 void 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 using namespace effecter::dbus_mapping; 242 Paths paths{}; 243 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; 269 270 auto dbus = effecter.value("dbus", empty); 271 paths.emplace_back(std::move(dbus)); 272 } 273 add(pdr->effecter_id, std::move(paths)); 274 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 */ 314 Repo& get(const std::string& dir); 315 316 } // namespace pdr 317 } // namespace responder 318 } // namespace pldm 319