blob: c95ab7ba7d2b8bbf37175a121910e1c80c009ef6 [file] [log] [blame]
#include "fru_parser.hpp"
#include <nlohmann/json.hpp>
#include <phosphor-logging/lg2.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Inventory/Decorator/Asset/common.hpp>
#include <xyz/openbmc_project/Inventory/Decorator/AssetTag/common.hpp>
#include <xyz/openbmc_project/Inventory/Decorator/Revision/common.hpp>
#include <xyz/openbmc_project/Inventory/Item/common.hpp>
#include <filesystem>
#include <fstream>
PHOSPHOR_LOG2_USING;
using namespace pldm::responder::dbus;
namespace pldm
{
namespace responder
{
namespace fru_parser
{
using Json = nlohmann::json;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
using InventoryDecoratorAsset =
sdbusplus::common::xyz::openbmc_project::inventory::decorator::Asset;
using InventoryDecoratorAssetTag =
sdbusplus::common::xyz::openbmc_project::inventory::decorator::AssetTag;
using InventoryDecoratorRevision =
sdbusplus::common::xyz::openbmc_project::inventory::decorator::Revision;
using InventoryItem = sdbusplus::common::xyz::openbmc_project::inventory::Item;
const Json emptyJson{};
const std::vector<Json> emptyJsonList{};
FruParser::FruParser(const std::string& dirPath,
const fs::path& fruMasterJsonPath)
{
if (fs::exists(fruMasterJsonPath))
{
setupDefaultDBusLookup(fruMasterJsonPath);
}
setupDefaultFruRecordMap();
fs::path dir(dirPath);
if (fs::exists(dir) && !fs::is_empty(dir))
{
setupFruRecordMap(dirPath);
}
}
void FruParser::setupDefaultDBusLookup(const fs::path& masterJsonPath)
{
constexpr auto service = "xyz.openbmc_project.Inventory.Manager";
constexpr auto rootPath = "/xyz/openbmc_project/inventory";
std::ifstream jsonFile(masterJsonPath);
auto data = Json::parse(jsonFile, nullptr, false);
if (data.is_discarded())
{
error("Failed to parse FRU Dbus Lookup Map config file '{PATH}'",
"PATH", masterJsonPath);
std::abort();
}
std::map<Interface, EntityType> defIntfToEntityType;
auto dbusMap = data.value("FruDBusLookupMap", emptyJson);
for (const auto& element : dbusMap.items())
{
try
{
defIntfToEntityType[static_cast<Interface>(element.key())] =
static_cast<EntityType>(element.value());
}
catch (const std::exception& e)
{
error("Failure in FRU dbus lookup map format, error - {ERROR}",
"ERROR", e);
throw InternalFailure();
}
}
Interfaces interfaces{};
for (auto [intf, entityType] : defIntfToEntityType)
{
intfToEntityType[intf] = entityType;
interfaces.emplace(intf);
}
lookupInfo.emplace(service, rootPath, std::move(interfaces));
}
void FruParser::setupDefaultFruRecordMap()
{
const FruRecordInfo generalRecordInfo = {
1, // generalRecordType
1, // encodingTypeASCII
{
// DSP0257 Table 5 General FRU Record Field Type Definitions
{InventoryDecoratorAsset::interface,
InventoryDecoratorAsset::property_names::model, "string", 2},
{InventoryDecoratorAsset::interface,
InventoryDecoratorAsset::property_names::part_number, "string", 3},
{InventoryDecoratorAsset::interface,
InventoryDecoratorAsset::property_names::serial_number, "string",
4},
{InventoryDecoratorAsset::interface,
InventoryDecoratorAsset::property_names::manufacturer, "string",
5},
{InventoryItem::interface,
InventoryItem::property_names::pretty_name, "string", 8},
{InventoryDecoratorAssetTag::interface,
InventoryDecoratorAssetTag::property_names::asset_tag, "string",
11},
{InventoryDecoratorRevision::interface,
InventoryDecoratorRevision::property_names::version, "string", 10},
}};
for (auto [intf, entityType] : intfToEntityType)
{
recordMap[intf] = {generalRecordInfo};
}
}
void FruParser::setupFruRecordMap(const std::string& dirPath)
{
for (auto& file : fs::directory_iterator(dirPath))
{
auto fileName = file.path().filename().string();
std::ifstream jsonFile(file.path());
auto data = Json::parse(jsonFile, nullptr, false);
if (data.is_discarded())
{
error("Failed to parse FRU config file at '{PATH}'", "PATH",
file.path());
throw InternalFailure();
}
try
{
auto record = data.value("record_details", emptyJson);
auto recordType =
static_cast<uint8_t>(record.value("fru_record_type", 0));
auto encType =
static_cast<uint8_t>(record.value("fru_encoding_type", 0));
auto dbusIntfName = record.value("dbus_interface_name", "");
auto entries = data.value("fru_fields", emptyJsonList);
std::vector<FieldInfo> fieldInfo;
for (const auto& entry : entries)
{
auto fieldType =
static_cast<uint8_t>(entry.value("fru_field_type", 0));
auto dbus = entry.value("dbus", emptyJson);
auto interface = dbus.value("interface", "");
auto property = dbus.value("property_name", "");
auto propType = dbus.value("property_type", "");
fieldInfo.emplace_back(
std::make_tuple(std::move(interface), std::move(property),
std::move(propType), std::move(fieldType)));
}
FruRecordInfo fruInfo;
fruInfo =
std::make_tuple(recordType, encType, std::move(fieldInfo));
auto search = recordMap.find(dbusIntfName);
// PLDM FRU can have multiple records for the same FRU like General
// FRU record and multiple OEM FRU records. If the FRU item
// interface name is already in the map, that indicates a record
// info is already added for the FRU, so append the new record info
// to the same data.
if (search != recordMap.end())
{
search->second.emplace_back(std::move(fruInfo));
}
else
{
FruRecordInfos recordInfos{fruInfo};
recordMap.emplace(dbusIntfName, recordInfos);
}
}
catch (const std::exception&)
{
continue;
}
}
}
} // namespace fru_parser
} // namespace responder
} // namespace pldm