| /* |
| * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "NvidiaGpuMemoryDevice.hpp" |
| |
| #include "NvidiaGpuMctpVdm.hpp" |
| #include "NvidiaUtils.hpp" |
| #include "OcpMctpVdm.hpp" |
| |
| #include <MctpRequester.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| |
| #include <cstdint> |
| #include <format> |
| #include <functional> |
| #include <memory> |
| #include <span> |
| #include <string> |
| #include <system_error> |
| |
| static constexpr const char* embeddedIfaceName = |
| "xyz.openbmc_project.Inventory.Connector.Embedded"; |
| |
| NvidiaGpuMemoryDevice::NvidiaGpuMemoryDevice( |
| std::shared_ptr<sdbusplus::asio::connection>& conn, |
| mctp::MctpRequester& mctpRequester, const std::string& gpuName, uint8_t eid, |
| sdbusplus::asio::object_server& objectServer) : |
| eid(eid), gpuName(gpuName), conn(conn), mctpRequester(mctpRequester), |
| objectServer(objectServer) |
| { |
| const int rc = gpu::encodeGetEccErrorCountsRequest(0, requestBuffer); |
| if (rc == 0) |
| { |
| requestEncoded = true; |
| } |
| else |
| { |
| lg2::error( |
| "Failed to encode ECC request for {NAME}, eid={EID}, rc={RC}", |
| "NAME", gpuName, "EID", eid, "RC", rc); |
| } |
| |
| const sdbusplus::object_path inventoryPath = inventoryPrefix / gpuName; |
| const std::string dramName = |
| std::format("{}_{}", gpuName, dramInventorySuffix); |
| const sdbusplus::object_path dramPath = inventoryPrefix / dramName; |
| |
| sramEccInterface = objectServer.add_interface( |
| inventoryPath, "xyz.openbmc_project.Memory.MemoryECC"); |
| |
| sramEccInterface->register_property("ceCount", int64_t{0}); |
| sramEccInterface->register_property("ueCount", int64_t{0}); |
| |
| if (!sramEccInterface->initialize()) |
| { |
| lg2::error( |
| "Error initializing SRAM ECC interface for {NAME}, eid={EID}", |
| "NAME", gpuName, "EID", eid); |
| } |
| |
| lg2::info("Created SRAM ECC interface for {NAME} at {PATH}", "NAME", |
| gpuName, "PATH", inventoryPath); |
| |
| dramEmbeddedInterface = |
| objectServer.add_interface(dramPath, embeddedIfaceName); |
| |
| if (!dramEmbeddedInterface->initialize()) |
| { |
| lg2::error( |
| "Error initializing Embedded interface for {NAME}, eid={EID}", |
| "NAME", dramName, "EID", eid); |
| } |
| |
| dramEccInterface = objectServer.add_interface( |
| dramPath, "xyz.openbmc_project.Memory.MemoryECC"); |
| |
| dramEccInterface->register_property("ceCount", int64_t{0}); |
| dramEccInterface->register_property("ueCount", int64_t{0}); |
| |
| if (!dramEccInterface->initialize()) |
| { |
| lg2::error( |
| "Error initializing DRAM ECC interface for {NAME}, eid={EID}", |
| "NAME", dramName, "EID", eid); |
| } |
| } |
| |
| NvidiaGpuMemoryDevice::~NvidiaGpuMemoryDevice() |
| { |
| objectServer.remove_interface(sramEccInterface); |
| objectServer.remove_interface(dramEmbeddedInterface); |
| objectServer.remove_interface(dramEccInterface); |
| } |
| |
| void NvidiaGpuMemoryDevice::update() |
| { |
| if (!requestEncoded) |
| { |
| return; |
| } |
| |
| mctpRequester.sendRecvMsg( |
| eid, requestBuffer, |
| [weak{weak_from_this()}](const std::error_code& ec, |
| std::span<const uint8_t> buffer) { |
| std::shared_ptr<NvidiaGpuMemoryDevice> self = weak.lock(); |
| if (!self) |
| { |
| lg2::error("Invalid reference to NvidiaGpuMemoryDevice"); |
| return; |
| } |
| self->processResponse(ec, buffer); |
| }); |
| } |
| |
| void NvidiaGpuMemoryDevice::processResponse(const std::error_code& ec, |
| std::span<const uint8_t> buffer) |
| { |
| if (ec) |
| { |
| lg2::error("MCTP error for {NAME}: {EC}", "NAME", gpuName, "EC", |
| ec.message()); |
| return; |
| } |
| |
| ocp::accelerator_management::CompletionCode cc{}; |
| uint16_t reasonCode = 0; |
| uint16_t flags = 0; |
| uint32_t sramCorrected = 0; |
| uint32_t sramUncorrectedSecded = 0; |
| uint32_t sramUncorrectedParity = 0; |
| uint32_t dramCorrected = 0; |
| uint32_t dramUncorrected = 0; |
| |
| auto rc = gpu::decodeGetEccErrorCountsResponse( |
| buffer, cc, reasonCode, flags, sramCorrected, sramUncorrectedSecded, |
| sramUncorrectedParity, dramCorrected, dramUncorrected); |
| |
| if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS) |
| { |
| lg2::error( |
| "Error decoding ECC response for {NAME}: rc={RC}, cc={CC}, reason={REASON}", |
| "NAME", gpuName, "RC", rc, "CC", static_cast<int>(cc), "REASON", |
| reasonCode); |
| return; |
| } |
| |
| int64_t sramCeCount = sramCorrected; |
| int64_t sramUeCount = static_cast<int64_t>(sramUncorrectedSecded) + |
| static_cast<int64_t>(sramUncorrectedParity); |
| |
| sramEccInterface->set_property("ceCount", sramCeCount); |
| sramEccInterface->set_property("ueCount", sramUeCount); |
| |
| int64_t dramCeCount = dramCorrected; |
| int64_t dramUeCount = dramUncorrected; |
| |
| dramEccInterface->set_property("ceCount", dramCeCount); |
| dramEccInterface->set_property("ueCount", dramUeCount); |
| } |