blob: 40d371877123b9947e8633ff6d43163d871a705b [file]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PCIE_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PCIE_H_
/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include <array>
#include <cstddef>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include "absl/strings/str_cat.h"
#include "boost/system/linux_error.hpp" // NOLINT
#include "app.hpp"
#include "http_request.hpp"
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "generated/enums/pcie_device.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "dbus_utils.hpp"
#include "system_utils.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message/native_types.hpp"
#include "sdbusplus/unpack_properties.hpp"
namespace redfish {
static constexpr char const* pciePath = "/xyz/openbmc_project/inventory/pcie";
static constexpr char const* pcieDeviceInterface =
"xyz.openbmc_project.Inventory.Item.PCIeDevice";
static constexpr std::array<std::string_view, 1> pcieInterfaces = {
pcieDeviceInterface};
static inline void getPCIeDeviceList(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& name) {
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
pciePath, 0, pcieInterfaces, requestContext,
[asyncResp, systemName, name](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths) {
if (ec) {
BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: " << ec.message();
// Not an error, system just doesn't have PCIe info
return;
}
nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name];
pcieDeviceList = nlohmann::json::array();
for (const std::string& pcieDevicePath : pcieDevicePaths) {
size_t devStart = pcieDevicePath.rfind('/');
if (devStart == std::string::npos) {
continue;
}
std::string devName = pcieDevicePath.substr(devStart + 1);
if (devName.empty()) {
continue;
}
nlohmann::json::object_t pcieDevice;
pcieDevice["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName, "PCIeDevices", devName);
pcieDeviceList.push_back(std::move(pcieDevice));
}
asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
});
}
static inline void handlePCIeDeviceCollectionGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::string& systemName) {
if (!redfish::setUpRedfishRoute(app, req, aResp)) {
return;
}
system_utils::getSystemInformation(
aResp, systemName,
[](const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::string& systemPath) {
std::string systemName = redfish::system_utils::getSystemName(systemPath);
aResp->res.addHeader(boost::beast::http::field::link,
"</redfish/v1/JsonSchemas/PCIeDeviceCollection/"
"PCIeDeviceCollection.json>; rel=describedby");
aResp->res.jsonValue["@odata.type"] =
"#PCIeDeviceCollection.PCIeDeviceCollection";
aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName, "PCIeDevices");
aResp->res.jsonValue["Name"] = "PCIe Device Collection";
aResp->res.jsonValue["Description"] = "Collection of PCIe Devices";
aResp->res.jsonValue["Members"] = nlohmann::json::array();
aResp->res.jsonValue["Members@odata.count"] = 0;
getPCIeDeviceList(aResp, systemName, "Members");
});
}
inline void requestRoutesSystemPCIeDeviceCollection(App& app) {
/**
* Functions triggers appropriate requests on DBus
*/
BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/")
.privileges(redfish::privileges::getPCIeDeviceCollection)
.methods(boost::beast::http::verb::get)(
std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app)));
}
inline std::optional<pcie_device::PCIeTypes>
redfishPcieGenerationFromDbus(const std::string& generationInUse) {
if (generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1") {
return pcie_device::PCIeTypes::Gen1;
}
if (generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2") {
return pcie_device::PCIeTypes::Gen2;
}
if (generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3") {
return pcie_device::PCIeTypes::Gen3;
}
if (generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4") {
return pcie_device::PCIeTypes::Gen4;
}
if (generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5") {
return pcie_device::PCIeTypes::Gen5;
}
if (generationInUse.empty() ||
generationInUse ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown") {
return pcie_device::PCIeTypes::Invalid;
}
// The value is not unknown or Gen1-5, need return an internal error.
return std::nullopt;
}
inline void getPCIeDevicePropertiesCallback(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device, const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
if (ec) {
BMCWEB_LOG_DEBUG << "failed to get PCIe Device properties ec: "
<< ec.value() << ": " << ec.message();
if (ec.value() == boost::system::linux_error::bad_request_descriptor) {
messages::resourceNotFound(asyncResp->res, "PCIeDevice", device);
} else {
messages::internalError(asyncResp->res);
}
return;
}
const std::string* manufacturer = nullptr;
const std::string* deviceType = nullptr;
const std::string* generationInUse = nullptr;
const size_t* lanesInUse = nullptr;
const std::string* oemString = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "Manufacturer",
manufacturer, "DeviceType", deviceType, "LanesInUse", lanesInUse,
"GenerationInUse", generationInUse, "OEM", oemString);
if (!success) {
messages::internalError(asyncResp->res);
return;
}
if (lanesInUse != nullptr) {
asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = *lanesInUse;
}
if (generationInUse != nullptr) {
std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
redfishPcieGenerationFromDbus(*generationInUse);
if (!redfishGenerationInUse) {
messages::internalError(asyncResp->res);
return;
}
if (*redfishGenerationInUse != pcie_device::PCIeTypes::Invalid) {
asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
*redfishGenerationInUse;
}
}
if (oemString != nullptr) {
asyncResp->res.jsonValue["PCIeInterface"]["OEM"]["Google"]["LaneStatus"] =
*oemString;
}
if (manufacturer != nullptr) {
asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
}
if (deviceType != nullptr) {
asyncResp->res.jsonValue["DeviceType"] = *deviceType;
}
asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_4_0.PCIeDevice";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName, "PCIeDevices", device);
asyncResp->res.jsonValue["Name"] = "PCIe Device";
asyncResp->res.jsonValue["Id"] = device;
asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName,
"PCIeDevices", device, "PCIeFunctions");
}
inline void getPCIeChassisCallback(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& device,
const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& chassisPaths) {
if (ec) {
if (ec.value() != EBADR) {
BMCWEB_LOG_ERROR << "DBUS response error " << ec;
messages::internalError(asyncResp->res);
}
return;
}
if (chassisPaths.empty()) {
return;
}
if (chassisPaths.size() > 1) {
BMCWEB_LOG_ERROR << device << " is contained by mutliple chassis";
messages::internalError(asyncResp->res);
return;
}
sdbusplus::message::object_path chassisPath(chassisPaths[0]);
std::string chassis = chassisPath.filename();
if (chassis.empty()) {
BMCWEB_LOG_WARNING << "Malformed Chassis path " << chassisPath.str
<< " on " << device;
return;
}
asyncResp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
"/redfish/v1/Chassis/" + chassis;
}
using GetPCIeDeviceCallback = std::function<void(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device, const boost::system::error_code&,
const ::dbus::utility::DBusPropertiesMap&)>;
inline void getPCIeDevice(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device,
GetPCIeDeviceCallback&& cb) {
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
std::string(pciePath), 0, pcieInterfaces, context,
[asyncResp, context, device, systemName, cb = std::move(cb)](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec) {
BMCWEB_LOG_ERROR << "getPCIeDevice getSubTree error: "
<< ec.message();
messages::internalError(asyncResp->res);
return;
}
bool found = false;
// Iterate over all retrieved ObjectPaths.
for (const auto& object : subtree) {
const std::string& path = object.first;
const dbus::utility::MapperServiceMap& connectionNames =
object.second;
sdbusplus::message::object_path objPath(path);
if (objPath.filename() != device) {
continue;
}
if (connectionNames.empty()) {
BMCWEB_LOG_ERROR << "Got 0 Connection names";
continue;
}
found = true;
managedStore::GetManagedObjectStore()->getAllProperties(
connectionNames[0].first, objPath, pcieDeviceInterface, context,
std::bind_front(cb, asyncResp, systemName, device));
}
if (!found) {
// Pass error code for missing object
cb(asyncResp, systemName, device,
boost::system::linux_error::bad_request_descriptor,
::dbus::utility::DBusPropertiesMap());
}
});
}
inline void handleSystemPCIeDeviceGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& device) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
redfish::system_utils::getSystemInformation(asyncResp, systemName,
[device](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath) {
std::string systemName =
redfish::system_utils::getSystemName(systemPath);
getPCIeDevice(asyncResp, systemName, device,
getPCIeDevicePropertiesCallback);
std::string escapedPath = absl::StrCat(std::string(pciePath),
"/", device);
dbus::utility::escapePathForDbus(escapedPath);
constexpr std::array<std::string_view, 2> chassisInterfaces = {
"xyz.openbmc_project.Inventory.Item.Board",
"xyz.openbmc_project.Inventory.Item.Chassis"};
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths(
escapedPath + "/contained_by", {"/xyz/openbmc_project/inventory"},
0, chassisInterfaces, context,
std::bind_front(getPCIeChassisCallback, asyncResp, device));
});
}
inline void requestRoutesSystemPCIeDevice(App& app) {
BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
.privileges(redfish::privileges::getPCIeDevice)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleSystemPCIeDeviceGet, std::ref(app)));
}
inline void handleSystemPCIeFunctionCollectionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
auto getPCIeDeviceCallback =
[](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& device,
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
if (ec) {
BMCWEB_LOG_DEBUG << "failed to get PCIe Device properties ec: " <<
ec.value() << ": " << ec.message();
if (ec.value() == boost::system::linux_error::bad_request_descriptor) {
messages::resourceNotFound(asyncResp->res, "PCIeDevice", device);
} else {
messages::internalError(asyncResp->res);
}
return;
}
nlohmann::json& pcieFunctionList = asyncResp->res.jsonValue["Members"];
pcieFunctionList = nlohmann::json::array();
static constexpr const int maxPciFunctionNum = 8;
for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) {
// Check if this function exists by looking for a device ID
std::string devIDProperty = "Function" + std::to_string(functionNum) +
"DeviceId";
const std::string* property = nullptr;
for (const auto& propEntry : pcieDevProperties) {
if (propEntry.first == devIDProperty) {
property = std::get_if<std::string>(&propEntry.second);
}
}
if (property == nullptr || property->empty()) {
continue;
}
nlohmann::json::object_t pcieFunction;
pcieFunction["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName, "PCIeDevices", device,
"PCIeFunctions", std::to_string(functionNum));
pcieFunctionList.push_back(std::move(pcieFunction));
}
asyncResp->res.jsonValue["Members@odata.count"] = pcieFunctionList.size();
};
redfish::system_utils::getSystemInformation(asyncResp, systemName,
[device, getPCIeDeviceCallback](
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath) {
std::string systemName =
redfish::system_utils::getSystemName(systemPath);
asyncResp->res.jsonValue["@odata.type"] =
"#PCIeFunctionCollection.PCIeFunctionCollection";
asyncResp->res.jsonValue["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName,
"PCIeDevices", device,
"PCIeFunctions");
asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
asyncResp->res.jsonValue["Description"] =
"Collection of PCIe Functions for PCIe Device " + device;
getPCIeDevice(asyncResp, systemName, device, getPCIeDeviceCallback);
});
}
inline void requestRoutesSystemPCIeFunctionCollection(App& app) {
/**
* Functions triggers appropriate requests on DBus
*/
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/")
.privileges(redfish::privileges::getPCIeFunctionCollection)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleSystemPCIeFunctionCollectionGet,
std::ref(app)));
}
inline void handleSystemPCIeFunctionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device, const std::string& function) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
auto getPCIeDeviceCallback =
[function](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const std::string& device,
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
if (ec) {
BMCWEB_LOG_DEBUG << "failed to get PCIe Device properties ec: "
<< ec.value() << ": " << ec.message();
if (ec.value() == boost::system::linux_error::bad_request_descriptor) {
messages::resourceNotFound(asyncResp->res, "PCIeDevice", device);
} else {
messages::internalError(asyncResp->res);
}
return;
}
// Check if this function exists by looking for a device ID
std::string functionName = "Function" + function;
std::string devIDProperty = functionName + "DeviceId";
const std::string* devIdProperty = nullptr;
for (const auto& property : pcieDevProperties) {
if (property.first == devIDProperty) {
devIdProperty = std::get_if<std::string>(&property.second);
continue;
}
}
if (devIdProperty == nullptr || devIdProperty->empty()) {
messages::resourceNotFound(asyncResp->res, "PCIeFunction", function);
return;
}
asyncResp->res.jsonValue["@odata.type"] =
"#PCIeFunction.v1_2_0.PCIeFunction";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName, "PCIeDevices", device,
"PCIeFunctions", function);
asyncResp->res.jsonValue["Name"] = "PCIe Function";
asyncResp->res.jsonValue["Id"] = function;
asyncResp->res.jsonValue["FunctionId"] = std::stoi(function);
asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName,
"PCIeDevices", device);
for (const auto& property : pcieDevProperties) {
const std::string* strProperty =
std::get_if<std::string>(&property.second);
if (property.first == functionName + "DeviceId") {
asyncResp->res.jsonValue["DeviceId"] = *strProperty;
}
if (property.first == functionName + "VendorId") {
asyncResp->res.jsonValue["VendorId"] = *strProperty;
}
if (property.first == functionName + "FunctionType") {
asyncResp->res.jsonValue["FunctionType"] = *strProperty;
}
if (property.first == functionName + "DeviceClass") {
asyncResp->res.jsonValue["DeviceClass"] = *strProperty;
}
if (property.first == functionName + "ClassCode") {
asyncResp->res.jsonValue["ClassCode"] = *strProperty;
}
if (property.first == functionName + "RevisionId") {
asyncResp->res.jsonValue["RevisionId"] = *strProperty;
}
if (property.first == functionName + "SubsystemId") {
asyncResp->res.jsonValue["SubsystemId"] = *strProperty;
}
if (property.first == functionName + "SubsystemVendorId") {
asyncResp->res.jsonValue["SubsystemVendorId"] = *strProperty;
}
}
};
redfish::system_utils::getSystemInformation(asyncResp, systemName,
[device, getPCIeDeviceCallback](
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath) {
std::string systemName =
redfish::system_utils::getSystemName(systemPath);
getPCIeDevice(asyncResp, systemName, device, getPCIeDeviceCallback);
});
}
inline void requestRoutesSystemPCIeFunction(App& app) {
BMCWEB_ROUTE(
app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/")
.privileges(redfish::privileges::getPCIeFunction)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleSystemPCIeFunctionGet, std::ref(app)));
}
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PCIE_H_