#include "Inventory.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr const char* inventoryPrefix = "/xyz/openbmc_project/inventory/"; constexpr const char* acceleratorIfaceName = "xyz.openbmc_project.Inventory.Item.Accelerator"; static constexpr const char* assetIfaceName = "xyz.openbmc_project.Inventory.Decorator.Asset"; static constexpr const char* uuidIfaceName = "xyz.openbmc_project.Common.UUID"; static constexpr const char* revisionIfaceName = "xyz.openbmc_project.Inventory.Decorator.Revision"; Inventory::Inventory( const std::shared_ptr& /*conn*/, sdbusplus::asio::object_server& objectServer, const std::string& inventoryName, mctp::MctpRequester& mctpRequester, const gpu::DeviceIdentification deviceTypeIn, const uint8_t eid, boost::asio::io_context& io) : name(escapeName(inventoryName)), mctpRequester(mctpRequester), deviceType(deviceTypeIn), eid(eid), retryTimer(io) { requestBuffer = std::make_shared(); responseBuffer = std::make_shared(); std::string path = inventoryPrefix + name; assetIface = objectServer.add_interface(path, assetIfaceName); assetIface->register_property("Manufacturer", std::string("NVIDIA")); // Register properties which need to be fetched from the device registerProperty(gpu::InventoryPropertyId::SERIAL_NUMBER, assetIface, "SerialNumber"); registerProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER, assetIface, "PartNumber"); registerProperty(gpu::InventoryPropertyId::MARKETING_NAME, assetIface, "Model"); assetIface->initialize(); uuidInterface = objectServer.add_interface(path, uuidIfaceName); registerProperty(gpu::InventoryPropertyId::DEVICE_GUID, uuidInterface, "UUID"); uuidInterface->initialize(); revisionIface = objectServer.add_interface(path, revisionIfaceName); registerProperty(gpu::InventoryPropertyId::DEVICE_PART_NUMBER, revisionIface, "Version"); revisionIface->initialize(); // Static properties if (deviceType == gpu::DeviceIdentification::DEVICE_GPU) { acceleratorInterface = objectServer.add_interface(path, acceleratorIfaceName); acceleratorInterface->register_property("Type", std::string("GPU")); acceleratorInterface->initialize(); } processNextProperty(); } void Inventory::registerProperty( gpu::InventoryPropertyId propertyId, const std::shared_ptr& interface, const std::string& propertyName) { if (interface) { interface->register_property(propertyName, std::string{}); properties[propertyId] = {interface, propertyName, 0, true}; } } void Inventory::processInventoryProperty(gpu::InventoryPropertyId propertyId) { auto it = properties.find(propertyId); if (it != properties.end()) { markPropertyPending(it); std::optional nextProperty = getNextPendingProperty(); if (nextProperty && *nextProperty == propertyId) { processNextProperty(); } } } void Inventory::markPropertyPending( std::unordered_map::iterator it) { it->second.isPending = true; it->second.retryCount = 0; } void Inventory::markPropertyProcessed( std::unordered_map::iterator it) { it->second.isPending = false; } std::optional Inventory::getNextPendingProperty() const { for (const auto& [propertyId, info] : properties) { if (info.isPending) { return propertyId; } } return std::nullopt; } void Inventory::sendInventoryPropertyRequest( gpu::InventoryPropertyId propertyId) { int rc = gpu::encodeGetInventoryInformationRequest( 0, static_cast(propertyId), *requestBuffer); if (rc != 0) { lg2::error( "Failed to encode property ID {PROP_ID} request for {NAME}: rc={RC}", "PROP_ID", static_cast(propertyId), "NAME", name, "RC", rc); return; } lg2::info( "Sending inventory request for property ID {PROP_ID} to EID {EID} for {NAME}", "PROP_ID", static_cast(propertyId), "EID", eid, "NAME", name); mctpRequester.sendRecvMsg(eid, *requestBuffer, *responseBuffer, [this, propertyId](int sendRecvMsgResult) { this->handleInventoryPropertyResponse( propertyId, sendRecvMsgResult); }); } void Inventory::handleInventoryPropertyResponse( gpu::InventoryPropertyId propertyId, int sendRecvMsgResult) { auto it = properties.find(propertyId); if (it == properties.end()) { lg2::error("Property ID {PROP_ID} for {NAME} not found", "PROP_ID", static_cast(propertyId), "NAME", name); processNextProperty(); return; } bool success = false; if (sendRecvMsgResult == 0) { ocp::accelerator_management::CompletionCode cc{}; uint16_t reasonCode = 0; gpu::InventoryValue info; int rc = gpu::decodeGetInventoryInformationResponse( *responseBuffer, cc, reasonCode, propertyId, info); lg2::info( "Response for property ID {PROP_ID} from {NAME}, sendRecvMsgResult: {RESULT}, decode_rc: {RC}, completion_code: {CC}, reason_code: {REASON}", "PROP_ID", static_cast(propertyId), "NAME", name, "RESULT", sendRecvMsgResult, "RC", rc, "CC", static_cast(cc), "REASON", reasonCode); if (rc == 0 && cc == ocp::accelerator_management::CompletionCode::SUCCESS) { std::string value; // Handle different property types based on property ID switch (propertyId) { case gpu::InventoryPropertyId::BOARD_PART_NUMBER: case gpu::InventoryPropertyId::SERIAL_NUMBER: case gpu::InventoryPropertyId::MARKETING_NAME: case gpu::InventoryPropertyId::DEVICE_PART_NUMBER: if (std::holds_alternative(info)) { value = std::get(info); } else { lg2::error( "Property ID {PROP_ID} for {NAME} expected string but got different type", "PROP_ID", static_cast(propertyId), "NAME", name); break; } break; case gpu::InventoryPropertyId::DEVICE_GUID: if (std::holds_alternative>(info)) { const auto& guidBytes = std::get>(info); if (guidBytes.size() >= 16) { boost::uuids::uuid uuid; std::copy(guidBytes.begin(), guidBytes.begin() + 16, uuid.begin()); value = boost::uuids::to_string(uuid); } else { lg2::error( "Property ID {PROP_ID} for {NAME} GUID size {SIZE} is less than 16 bytes", "PROP_ID", static_cast(propertyId), "NAME", name, "SIZE", guidBytes.size()); break; } } else { lg2::error( "Property ID {PROP_ID} for {NAME} expected vector but got different type", "PROP_ID", static_cast(propertyId), "NAME", name); break; } break; default: lg2::error("Unsupported property ID {PROP_ID} for {NAME}", "PROP_ID", static_cast(propertyId), "NAME", name); break; } if (!value.empty()) { it->second.interface->set_property(it->second.propertyName, value); lg2::info( "Successfully received property ID {PROP_ID} for {NAME} with value: {VALUE}", "PROP_ID", static_cast(propertyId), "NAME", name, "VALUE", value); success = true; } } } if (!success) { it->second.retryCount++; if (it->second.retryCount >= maxRetryAttempts) { lg2::error( "Property ID {PROP_ID} for {NAME} failed after {ATTEMPTS} attempts", "PROP_ID", static_cast(propertyId), "NAME", name, "ATTEMPTS", maxRetryAttempts); markPropertyProcessed(it); } else { retryTimer.expires_after(retryDelay); retryTimer.async_wait([this](const boost::system::error_code& ec) { if (ec) { lg2::error("Retry timer error for {NAME}: {ERROR}", "NAME", name, "ERROR", ec.message()); return; } this->processNextProperty(); }); return; } } else { markPropertyProcessed(it); } processNextProperty(); } void Inventory::processNextProperty() { std::optional nextProperty = getNextPendingProperty(); if (nextProperty) { sendInventoryPropertyRequest(*nextProperty); } else { lg2::info("No pending properties found to process for {NAME}", "NAME", name); } }