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 virtual ~Repo() = default; 51 52 /** @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 98 namespace 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 */ 107 inline 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 */ 127 class 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 */ 186 template <typename T> 187 void 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 243 using namespace effecter::dbus_mapping; 244 Paths paths{}; 245 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; 271 272 auto dbus = effecter.value("dbus", empty); 273 paths.emplace_back(std::move(dbus)); 274 } 275 add(pdr->effecter_id, std::move(paths)); 276 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 */ 316 Repo& get(const std::string& dir); 317 318 } // namespace pdr 319 } // namespace responder 320 } // namespace pldm 321