| /* |
| * SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "MctpMockTestBase.hpp" |
| #include "MessagePackUnpackUtils.hpp" |
| #include "MockMctpRequester.hpp" |
| #include "NvidiaEventReporting.hpp" |
| #include "NvidiaGpuMctpVdm.hpp" |
| #include "OcpMctpVdm.hpp" |
| |
| #include <cstdint> |
| #include <initializer_list> |
| #include <memory> |
| #include <optional> |
| #include <span> |
| #include <system_error> |
| #include <utility> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace |
| { |
| |
| // Build a DEVICE_CAPABILITY_DISCOVERY response: header + command + cc + |
| // reasonCode (no payload), matching decodeResponseCommonHeader expectations. |
| std::vector<uint8_t> buildCapabilityResponse( |
| gpu::DeviceCapabilityDiscoveryCommands command, |
| ocp::accelerator_management::CompletionCode cc) |
| { |
| std::vector<uint8_t> buf(ocp::accelerator_management::messageHeaderSize + 4, |
| 0); |
| PackBuffer pack(buf); |
| ocp::accelerator_management::packHeader( |
| pack, gpu::nvidiaPciVendorId, |
| ocp::accelerator_management::MessageType::RESPONSE, 0, |
| static_cast<uint8_t>(gpu::MessageType::DEVICE_CAPABILITY_DISCOVERY)); |
| pack.pack(static_cast<uint8_t>(command)); |
| pack.pack(static_cast<uint8_t>(cc)); |
| pack.pack(static_cast<uint16_t>(0)); // reasonCode |
| EXPECT_EQ(pack.getError(), 0); |
| return buf; |
| } |
| |
| std::vector<uint8_t> subscriptionSuccess() |
| { |
| return buildCapabilityResponse( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_EVENT_SUBSCRIPTION, |
| ocp::accelerator_management::CompletionCode::SUCCESS); |
| } |
| |
| std::vector<uint8_t> sourcesSuccess() |
| { |
| return buildCapabilityResponse( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_CURRENT_EVENT_SOURCES, |
| ocp::accelerator_management::CompletionCode::SUCCESS); |
| } |
| |
| // Build a full event message (header + eventData) for NvidiaEventHandler. |
| std::vector<uint8_t> buildEventBuffer(uint8_t messageType, uint8_t eventId, |
| uint8_t eventClass, uint16_t eventState, |
| const std::vector<uint8_t>& eventData) |
| { |
| std::vector<uint8_t> buf( |
| ocp::accelerator_management::eventHeaderSize + eventData.size(), 0); |
| PackBuffer pack(buf); |
| ocp::accelerator_management::packHeader( |
| pack, gpu::nvidiaPciVendorId, |
| ocp::accelerator_management::MessageType::RESPONSE, 0, messageType); |
| pack.pack(static_cast<uint8_t>(0)); // versionByte |
| pack.pack(eventId); |
| pack.pack(eventClass); |
| pack.pack(eventState); |
| pack.pack(static_cast<uint8_t>(eventData.size())); |
| for (const uint8_t byte : eventData) |
| { |
| pack.pack(byte); |
| } |
| EXPECT_EQ(pack.getError(), 0); |
| return buf; |
| } |
| |
| // Action that records a copy of the request bytes (the reqMsg span is a view |
| // into caller-owned memory) and completes the call with the owned response. |
| auto captureAndRespond(std::vector<std::vector<uint8_t>>& history, |
| std::vector<uint8_t> response) |
| { |
| return |
| [&history, response = std::move(response)]( |
| uint8_t /*eid*/, std::span<const uint8_t> reqMsg, auto callback) { |
| history.emplace_back(reqMsg.begin(), reqMsg.end()); |
| callback(std::error_code{}, response); |
| }; |
| } |
| |
| // NvidiaEventHandler keeps registered handlers in a static map for the life of |
| // the binary, so anything a registered handler writes to has to outlive the |
| // test that installed it. |
| std::optional<std::vector<uint8_t>>& dispatchedEventData() |
| { |
| static std::optional<std::vector<uint8_t>> data; |
| return data; |
| } |
| |
| class NvidiaEventReportingTest : public MctpMockTestBase |
| { |
| protected: |
| static std::shared_ptr<NvidiaEventReportingConfig> createConfig( |
| uint8_t eid, std::initializer_list<EventDescriptor> events) |
| { |
| return std::make_shared<NvidiaEventReportingConfig>(eid, requester(), |
| events); |
| } |
| }; |
| |
| TEST_F(NvidiaEventReportingTest, InitSubscriptionFailureStops) |
| { |
| // Exactly one request: a failed subscription must not drive event sources. |
| EXPECT_CALL(mctpMock, sendRecvMsg) |
| .WillOnce(mock_mctp::respondWith( |
| {}, |
| buildCapabilityResponse( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_EVENT_SUBSCRIPTION, |
| ocp::accelerator_management::CompletionCode::ERROR))); |
| |
| auto cfg = createConfig( |
| 110, |
| {EventDescriptor{gpu::MessageType::PLATFORM_ENVIRONMENTAL, 5, |
| [](const EventInfo&, std::span<const uint8_t>) {}}}); |
| cfg->init(); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, InitSubscriptionTransportErrorStops) |
| { |
| EXPECT_CALL(mctpMock, sendRecvMsg) |
| .WillOnce(mock_mctp::respondWith( |
| std::make_error_code(std::errc::timed_out), {})); |
| |
| auto cfg = createConfig( |
| 111, |
| {EventDescriptor{gpu::MessageType::PLATFORM_ENVIRONMENTAL, 5, |
| [](const EventInfo&, std::span<const uint8_t>) {}}}); |
| EXPECT_NO_THROW(cfg->init()); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, InitDrivesEventSourcesForRegisteredTypes) |
| { |
| // subscription request + one event-sources request for the single |
| // non-empty message type. |
| std::vector<std::vector<uint8_t>> history; |
| EXPECT_CALL(mctpMock, sendRecvMsg) |
| .WillOnce(captureAndRespond(history, subscriptionSuccess())) |
| .WillOnce(captureAndRespond(history, sourcesSuccess())); |
| |
| auto cfg = createConfig( |
| 112, |
| {EventDescriptor{gpu::MessageType::PLATFORM_ENVIRONMENTAL, 5, |
| [](const EventInfo&, std::span<const uint8_t>) {}}}); |
| cfg->init(); |
| |
| ASSERT_EQ(history.size(), 2U); |
| |
| UnpackBuffer sub(history[0]); |
| ocp::accelerator_management::MessageType ocpMsgType{}; |
| uint8_t instanceId = 0; |
| uint8_t msgType = 0; |
| EXPECT_EQ(ocp::accelerator_management::unpackHeader( |
| sub, gpu::nvidiaPciVendorId, ocpMsgType, instanceId, msgType), |
| 0); |
| uint8_t subCommand = 0; |
| sub.unpack(subCommand); |
| EXPECT_EQ( |
| subCommand, |
| static_cast<uint8_t>( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_EVENT_SUBSCRIPTION)); |
| |
| UnpackBuffer src(history[1]); |
| EXPECT_EQ(ocp::accelerator_management::unpackHeader( |
| src, gpu::nvidiaPciVendorId, ocpMsgType, instanceId, msgType), |
| 0); |
| uint8_t srcCommand = 0; |
| src.unpack(srcCommand); |
| EXPECT_EQ( |
| srcCommand, |
| static_cast<uint8_t>( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_CURRENT_EVENT_SOURCES)); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, InitVerifiesSubscriptionRequestEncoding) |
| { |
| // Empty response fails decoding, so init stops after the one request. |
| std::vector<std::vector<uint8_t>> history; |
| EXPECT_CALL(mctpMock, sendRecvMsg).WillOnce(captureAndRespond(history, {})); |
| |
| auto cfg = createConfig( |
| 113, |
| {EventDescriptor{gpu::MessageType::PLATFORM_ENVIRONMENTAL, 5, |
| [](const EventInfo&, std::span<const uint8_t>) {}}}); |
| cfg->init(); |
| |
| ASSERT_EQ(history.size(), 1U); |
| const std::vector<uint8_t>& req = history[0]; |
| ASSERT_FALSE(req.empty()); |
| |
| UnpackBuffer unpack(req); |
| ocp::accelerator_management::MessageType ocpMsgType{}; |
| uint8_t instanceId = 0; |
| uint8_t msgType = 0; |
| EXPECT_EQ(ocp::accelerator_management::unpackHeader( |
| unpack, gpu::nvidiaPciVendorId, ocpMsgType, instanceId, |
| msgType), |
| 0); |
| EXPECT_EQ(msgType, static_cast<uint8_t>( |
| gpu::MessageType::DEVICE_CAPABILITY_DISCOVERY)); |
| |
| uint8_t command = 0; |
| uint8_t dataSize = 0; |
| uint8_t generationSetting = 0; |
| uint8_t targetEid = 0; |
| unpack.unpack(command); |
| unpack.unpack(dataSize); |
| unpack.unpack(generationSetting); |
| unpack.unpack(targetEid); |
| EXPECT_EQ( |
| command, |
| static_cast<uint8_t>( |
| gpu::DeviceCapabilityDiscoveryCommands::SET_EVENT_SUBSCRIPTION)); |
| EXPECT_EQ(generationSetting, 2); |
| EXPECT_EQ(targetEid, 8); // bmc_eid |
| EXPECT_EQ(unpack.getError(), 0); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, InitEventSourcesEncodingHasMask) |
| { |
| std::vector<std::vector<uint8_t>> history; |
| EXPECT_CALL(mctpMock, sendRecvMsg) |
| .WillOnce(captureAndRespond(history, subscriptionSuccess())) |
| .WillOnce(captureAndRespond(history, sourcesSuccess())); |
| |
| constexpr uint8_t eventCode = 5; |
| auto cfg = createConfig( |
| 114, |
| {EventDescriptor{gpu::MessageType::PLATFORM_ENVIRONMENTAL, eventCode, |
| [](const EventInfo&, std::span<const uint8_t>) {}}}); |
| cfg->init(); |
| |
| ASSERT_EQ(history.size(), 2U); |
| |
| UnpackBuffer src(history[1]); |
| ocp::accelerator_management::MessageType ocpMsgType{}; |
| uint8_t instanceId = 0; |
| uint8_t msgType = 0; |
| EXPECT_EQ(ocp::accelerator_management::unpackHeader( |
| src, gpu::nvidiaPciVendorId, ocpMsgType, instanceId, msgType), |
| 0); |
| |
| uint8_t command = 0; |
| uint8_t dataSize = 0; |
| uint8_t messageType = 0; |
| uint64_t mask = 0; |
| src.unpack(command); |
| src.unpack(dataSize); |
| src.unpack(messageType); |
| src.unpack(mask); |
| EXPECT_EQ(dataSize, 9); |
| EXPECT_EQ(messageType, |
| static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL)); |
| EXPECT_EQ(mask, (1ULL << eventCode)); |
| EXPECT_EQ(src.getError(), 0); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, HandleEventDispatchesToRegisteredHandler) |
| { |
| constexpr uint8_t eid = 100; |
| constexpr uint8_t eventId = 0x01; |
| dispatchedEventData().reset(); |
| |
| NvidiaEventHandler::registerEventHandler( |
| eid, gpu::MessageType::PLATFORM_ENVIRONMENTAL, eventId, |
| [](const EventInfo&, std::span<const uint8_t> eventData) { |
| dispatchedEventData() = |
| std::vector<uint8_t>(eventData.begin(), eventData.end()); |
| }); |
| |
| const std::vector<uint8_t> eventData{0xDE, 0xAD, 0xBE, 0xEF}; |
| const auto buf = buildEventBuffer( |
| static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL), eventId, |
| 1, 0, eventData); |
| |
| NvidiaEventHandler::handleEvent(eid, buf); |
| |
| ASSERT_TRUE(dispatchedEventData().has_value()); |
| EXPECT_EQ(dispatchedEventData().value_or(std::vector<uint8_t>{}), |
| eventData); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, HandleEventNoHandlerNoCrash) |
| { |
| const auto buf = buildEventBuffer( |
| static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL), 0x02, 1, |
| 0, {0x01}); |
| // eid 101 has no registered handler. |
| EXPECT_NO_THROW(NvidiaEventHandler::handleEvent(101, buf)); |
| } |
| |
| TEST_F(NvidiaEventReportingTest, HandleEventDecodeErrorNoCrash) |
| { |
| const std::vector<uint8_t> buf{0x00, 0x01, 0x02}; // too short |
| EXPECT_NO_THROW(NvidiaEventHandler::handleEvent(102, buf)); |
| } |
| |
| } // namespace |