blob: c9df51f72d870efed7687d3c906a483e90ac9ecc [file] [log] [blame]
#pragma once
#include "effecters.hpp"
#include <stdint.h>
#include <filesystem>
#include <fstream>
#include <functional>
#include <map>
#include <nlohmann/json.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <string>
#include <vector>
#include <xyz/openbmc_project/Common/error.hpp>
#include "libpldm/platform.h"
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
namespace fs = std::filesystem;
namespace pldm
{
namespace responder
{
namespace pdr
{
using Type = uint8_t;
using Json = nlohmann::json;
using RecordHandle = uint32_t;
using Entry = std::vector<uint8_t>;
using Pdr = std::vector<Entry>;
/** @class Repo
*
* @brief Abstract class describing the interface API to the PDR repository
*
* Concrete implementations of this must handle storing and addressing the
* PDR entries by a "record handle", which can be indices, offsets, etc.
*/
class Repo
{
public:
virtual ~Repo() = default;
/** @brief Add a new entry to the PDR
*
* @param[in] entry - new PDR entry
*/
virtual void add(Entry&& entry) = 0;
/** @brief Access PDR entry at inout record handle
*
* @param[in] handle - record handle
*
* @return Entry - PDR entry
*/
virtual Entry at(RecordHandle handle) const = 0;
/** @brief Get next available record handle for assignment
*
* @return RecordHandle - PDR record handle
*/
virtual RecordHandle getNextRecordHandle() const = 0;
/** @brief Get record handle immediately suceeding the input record
* handle
*
* @param[in] current - input record handle
*
* @return RecordHandle - PDR record handle
*/
virtual RecordHandle getNextRecordHandle(RecordHandle current) const = 0;
/** @brief Get number of entries in the PDR
*
* @return size_t - number of entries
*/
virtual size_t numEntries() const = 0;
/** @brief Check if PDR is empty
*
* @return bool - true if PDR is empty, false otherwise
*/
virtual bool empty() const = 0;
/** @brief Empty the PDR
*/
virtual void makeEmpty() = 0;
};
namespace internal
{
/** @brief Parse PDR JSON file and output Json object
*
* @param[in] path - path of PDR JSON file
*
* @return Json - Json object
*/
inline Json readJson(const std::string& path)
{
std::ifstream jsonFile(path);
if (!jsonFile.is_open())
{
log<level::INFO>("Error opening PDR JSON file",
entry("PATH=%s", path.c_str()));
return {};
}
return Json::parse(jsonFile);
}
/** @class IndexedRepo
*
* @brief Inherits and implements Repo
*
* Stores the PDR as a vector of entries, and addresses PDR entries based on an
* incrementing record handle, starting at 1.
*/
class IndexedRepo : public Repo
{
public:
void add(Entry&& entry)
{
repo.emplace_back(std::move(entry));
}
Entry at(RecordHandle handle) const
{
if (!handle)
{
handle = 1;
}
return repo.at(handle - 1);
}
RecordHandle getNextRecordHandle() const
{
return repo.size() + 1;
}
RecordHandle getNextRecordHandle(RecordHandle current) const
{
if (current >= repo.size())
{
return 0;
}
if (!current)
{
current = 1;
}
return current + 1;
}
size_t numEntries() const
{
return repo.size();
}
bool empty() const
{
return repo.empty();
}
void makeEmpty()
{
repo.clear();
}
private:
Pdr repo{};
};
/** @brief Parse PDR JSONs and build PDR repository
*
* @param[in] dir - directory housing platform specific PDR JSON files
* @tparam[in] repo - instance of concrete implementation of Repo
*/
template <typename T>
void generate(const std::string& dir, T& repo)
{
using namespace internal;
// A map of PDR type to a lambda that handles creation of that PDR type.
// The lambda essentially would parse the platform specific PDR JSONs to
// generate the PDR structures. This function iterates through the map to
// invoke all lambdas, so that all PDR types can be created.
std::map<Type, std::function<void(const Json& json, T& repo)>> generators =
{{PLDM_STATE_EFFECTER_PDR, [](const auto& json, T& repo) {
static const std::vector<Json> emptyList{};
static const Json empty{};
auto entries = json.value("entries", emptyList);
for (const auto& e : entries)
{
size_t pdrSize = 0;
auto effecters = e.value("effecters", emptyList);
static const Json empty{};
for (const auto& effecter : effecters)
{
auto set = effecter.value("set", empty);
auto statesSize = set.value("size", 0);
if (!statesSize)
{
log<level::ERR>(
"Malformed PDR JSON - no state set info",
entry("TYPE=%d", PLDM_STATE_EFFECTER_PDR));
elog<InternalFailure>();
}
pdrSize += sizeof(state_effecter_possible_states) -
sizeof(bitfield8_t) +
(sizeof(bitfield8_t) * statesSize);
}
pdrSize += sizeof(pldm_state_effecter_pdr) - sizeof(uint8_t);
Entry pdrEntry{};
pdrEntry.resize(pdrSize);
pldm_state_effecter_pdr* pdr =
reinterpret_cast<pldm_state_effecter_pdr*>(
pdrEntry.data());
pdr->hdr.record_handle = repo.getNextRecordHandle();
pdr->hdr.version = 1;
pdr->hdr.type = PLDM_STATE_EFFECTER_PDR;
pdr->hdr.record_change_num = 0;
pdr->hdr.length = pdrSize - sizeof(pldm_pdr_hdr);
pdr->terminus_handle = 0;
pdr->effecter_id = effecter::nextId();
pdr->entity_type = e.value("type", 0);
pdr->entity_instance = e.value("instance", 0);
pdr->container_id = e.value("container", 0);
pdr->effecter_semantic_id = 0;
pdr->effecter_init = PLDM_NO_INIT;
pdr->has_description_pdr = false;
pdr->composite_effecter_count = effecters.size();
using namespace effecter::dbus_mapping;
Paths paths{};
uint8_t* start = pdrEntry.data() +
sizeof(pldm_state_effecter_pdr) -
sizeof(uint8_t);
for (const auto& effecter : effecters)
{
auto set = effecter.value("set", empty);
state_effecter_possible_states* possibleStates =
reinterpret_cast<state_effecter_possible_states*>(
start);
possibleStates->state_set_id = set.value("id", 0);
possibleStates->possible_states_size =
set.value("size", 0);
start += sizeof(possibleStates->state_set_id) +
sizeof(possibleStates->possible_states_size);
static const std::vector<uint8_t> emptyStates{};
auto states = set.value("states", emptyStates);
for (const auto& state : states)
{
auto index = state / 8;
auto bit = state - (index * 8);
bitfield8_t* bf =
reinterpret_cast<bitfield8_t*>(start + index);
bf->byte |= 1 << bit;
}
start += possibleStates->possible_states_size;
auto dbus = effecter.value("dbus", empty);
paths.emplace_back(std::move(dbus));
}
add(pdr->effecter_id, std::move(paths));
repo.add(std::move(pdrEntry));
}
}}};
auto eraseLen = strlen(".json");
Type pdrType{};
for (const auto& dirEntry : fs::directory_iterator(dir))
{
try
{
auto json = readJson(dirEntry.path().string());
if (!json.empty())
{
auto fileName = dirEntry.path().filename().string();
fileName.erase(fileName.end() - eraseLen);
pdrType = stoi(fileName);
generators.at(pdrType)(json, repo);
}
}
catch (const InternalFailure& e)
{
}
catch (const std::exception& e)
{
log<level::ERR>("Failed parsing PDR JSON file",
entry("TYPE=%d", pdrType),
entry("ERROR=%s", e.what()));
report<InternalFailure>();
}
}
}
} // namespace internal
/** @brief Build (if not built already) and retrieve PDR
*
* @param[in] dir - directory housing platform specific PDR JSON files
*
* @return Repo& - Reference to instance of pdr::Repo
*/
Repo& get(const std::string& dir);
} // namespace pdr
} // namespace responder
} // namespace pldm