blob: 17c8926a10f8e32ad9e88a48c82ccb6dbdf40fca [file] [log] [blame]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaPcieInterface.hpp"
#include "Utils.hpp"
#include <bits/basic_string.h>
#include <MctpRequester.hpp>
#include <NvidiaGpuMctpVdm.hpp>
#include <NvidiaPcieDevice.hpp>
#include <OcpMctpVdm.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <vector>
using std::string;
using namespace std::literals;
NvidiaPcieInterface::NvidiaPcieInterface(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& path, uint8_t eid,
sdbusplus::asio::object_server& objectServer) :
eid(eid), path(path), conn(conn), mctpRequester(mctpRequester)
{
const std::string dbusPath = pcieDevicePathPrefix + escapeName(name);
pcieDeviceInterface = objectServer.add_interface(
dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeDevice");
switchInterface = objectServer.add_interface(
dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeSwitch");
pcieDeviceInterface->register_property(
"GenerationInUse",
std::string(
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
pcieDeviceInterface->register_property("LanesInUse",
std::numeric_limits<size_t>::max());
pcieDeviceInterface->register_property(
"GenerationSupported",
std::string(
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
pcieDeviceInterface->register_property("MaxLanes", static_cast<size_t>(0));
if (!pcieDeviceInterface->initialize())
{
lg2::error("Error initializing PCIe Device Interface for EID={EID}",
"EID", eid);
}
if (!switchInterface->initialize())
{
lg2::error("Error initializing Switch Interface for EID={EID}", "EID",
eid);
}
}
string NvidiaPcieInterface::mapPcieGeneration(uint32_t value)
{
switch (value)
{
case 1:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1";
case 2:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2";
case 3:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3";
case 4:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4";
case 5:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5";
case 6:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen6";
default:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown";
}
}
size_t NvidiaPcieInterface::decodeLinkWidth(uint32_t value)
{
return (value > 0) ? pow(2, value - 1) : 0;
}
void NvidiaPcieInterface::processResponse(const std::error_code& ec,
std::span<const uint8_t> response)
{
if (ec)
{
lg2::error(
"Error updating PCIe Interface: sending message over MCTP failed, "
"rc={RC}, EID={EID}",
"RC", ec.value(), "EID", eid);
return;
}
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
size_t numTelemetryValue = 0;
auto rc = gpu::decodeQueryScalarGroupTelemetryV2Response(
response, cc, reasonCode, numTelemetryValue, telemetryValues);
if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
{
lg2::error("Error updating PCIe Interface: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode,
"EID", eid);
return;
}
if (!telemetryValues.empty())
{
pcieDeviceInterface->set_property(
"GenerationInUse", mapPcieGeneration(telemetryValues[0]));
}
if (telemetryValues.size() > 1)
{
pcieDeviceInterface->set_property(
"LanesInUse",
decodeLinkWidth(static_cast<size_t>(telemetryValues[1])));
}
if (telemetryValues.size() > 3)
{
pcieDeviceInterface->set_property(
"GenerationSupported", mapPcieGeneration(telemetryValues[3]));
}
if (telemetryValues.size() > 4)
{
pcieDeviceInterface->set_property(
"MaxLanes",
decodeLinkWidth(static_cast<size_t>(telemetryValues[4])));
}
}
void NvidiaPcieInterface::update()
{
auto rc =
gpu::encodeQueryScalarGroupTelemetryV2Request(0, {}, 0, 0, 1, request);
if (rc != 0)
{
lg2::error("Error updating PCIe Interface: failed, rc={RC}, EID={EID}",
"RC", rc, "EID", eid);
return;
}
mctpRequester.sendRecvMsg(
eid, request,
[weak{weak_from_this()}](const std::error_code& ec,
std::span<const uint8_t> buffer) {
std::shared_ptr<NvidiaPcieInterface> self = weak.lock();
if (!self)
{
lg2::error(
"Invalid reference to NvidiaPcieInterface for EID {EID}",
"EID", self->eid);
return;
}
self->processResponse(ec, buffer);
});
}