blob: 2a784b63a8b28a82b6cd8cc8271cd6d6a9321443 [file] [log] [blame] [edit]
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION &
* AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
*
* 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 "common-tests.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "device-capability-discovery.h"
using ::testing::ElementsAre;
void testEncodeRequest(
std::function<int(uint8_t, const uint8_t *, nsm_msg *)> function,
uint8_t nvidiaMsgType, uint8_t command, uint8_t payloadSize,
const uint8_t *expectedPayload, uint8_t *payload)
{
uint8_t instanceId = 0;
Request requestMsg(sizeof(nsm_msg_hdr) + sizeof(nsm_common_req));
requestMsg.insert(requestMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
nsm_common_req *data =
reinterpret_cast<nsm_common_req *>(request->payload);
// Bad tests
auto rc = function(instanceId, nullptr, request);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(instanceId, expectedPayload, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(NSM_INSTANCE_MAX + 1, expectedPayload, request);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
// Good test
rc = function(instanceId, expectedPayload, request);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(1, request->hdr.request);
EXPECT_EQ(0, request->hdr.datagram);
EXPECT_EQ(instanceId, request->hdr.instance_id);
EXPECT_EQ(nvidiaMsgType, request->hdr.nvidia_msg_type);
EXPECT_EQ(command, data->command);
EXPECT_EQ(payloadSize, data->data_size);
if (payload != nullptr) {
memcpy(payload, request->payload + sizeof(nsm_common_req),
payloadSize);
}
}
void testEncodeCommonRequest(std::function<int(uint8_t, nsm_msg *)> function,
uint8_t nvidiaMsgType, uint8_t command)
{
uint8_t payload = 0;
testEncodeRequest(
[function](uint8_t instanceId, const uint8_t *data, nsm_msg *msg) {
if (data == nullptr) {
// special case for function with one parameter less
return (int)NSM_SW_ERROR_NULL;
}
return function(instanceId, msg);
},
nvidiaMsgType, command, 0, &payload, nullptr);
}
void testDecodeRequest(
std::function<int(nsm_msg *, uint16_t, uint8_t *)> function,
uint8_t nvidiaMsgType, uint8_t command, uint8_t payloadSize,
const uint8_t *expectedPayload, uint8_t *payload)
{
const uint8_t instanceId = 0;
Request requestMsg{
0x10,
0xDE, // PCI VID: NVIDIA 0x10DE
0x80 | (instanceId & 0x1f), // RQ=1, D=0, RSVD=0, INSTANCE_ID=0
0x89, // OCP_TYPE=8, OCP_VER=9
nvidiaMsgType, // NVIDIA_MSG_TYPE
command, // command
payloadSize // data size
};
requestMsg.insert(requestMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
auto data = reinterpret_cast<nsm_common_req *>(request->payload);
auto len = requestMsg.size();
// Good test
auto rc = function(request, len, payload);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
nsm_header_info header;
rc = unpack_nsm_header(&request->hdr, &header);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(nvidiaMsgType, header.nvidia_msg_type);
EXPECT_EQ(command, data->command);
EXPECT_EQ(payloadSize, data->data_size);
EXPECT_EQ(instanceId, header.instance_id);
// Bad tests
rc = function(nullptr, len, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(request, len, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(request, len - 1, payload);
EXPECT_EQ(NSM_SW_ERROR_LENGTH, rc);
requestMsg[0] = 0;
rc = function(request, len, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
}
void testDecodeCommonRequest(std::function<int(nsm_msg *, uint16_t)> function,
uint8_t nvidiaMsgType, uint8_t command)
{
uint8_t payload = 0;
testDecodeRequest(
[function](nsm_msg *msg, uint16_t len, uint8_t *data) {
if (data == nullptr) {
// special case for function with one parameter less
return (int)NSM_SW_ERROR_NULL;
}
return function(msg, len);
},
nvidiaMsgType, command, 0, &payload, &payload);
}
void testEncodeResponse(
std::function<int(uint8_t, uint8_t, uint16_t, const uint8_t *, nsm_msg *)>
function,
uint8_t nvidiaMsgType, uint8_t command, size_t payloadSize,
const uint8_t *expectedPayload, uint8_t *payload)
{
// Test common response
const uint8_t instanceId = 0;
Response responseMsg(sizeof(nsm_msg_hdr) + sizeof(nsm_common_resp));
responseMsg.insert(responseMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
auto common = reinterpret_cast<nsm_common_resp *>(response->payload);
// Good test
uint8_t cc = NSM_SUCCESS;
uint16_t reasonCode = ERR_NULL;
auto rc =
function(instanceId, cc, reasonCode, expectedPayload, response);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(0, response->hdr.request);
EXPECT_EQ(0, response->hdr.datagram);
EXPECT_EQ(instanceId, response->hdr.instance_id);
EXPECT_EQ(nvidiaMsgType, response->hdr.nvidia_msg_type);
EXPECT_EQ(command, common->command);
EXPECT_EQ(cc, common->completion_code);
EXPECT_EQ(payloadSize, le16toh(common->data_size));
if (payload != nullptr) {
memcpy(payload, response->payload + sizeof(nsm_common_resp),
payloadSize);
}
// Bad tests
Response badResponseMsg(sizeof(nsm_msg_hdr) +
sizeof(nsm_common_non_success_resp));
auto badResponse = reinterpret_cast<nsm_msg *>(badResponseMsg.data());
auto badData = reinterpret_cast<nsm_common_non_success_resp *>(
badResponse->payload);
rc = function(instanceId, cc, reasonCode, nullptr, response);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(instanceId, cc, reasonCode, expectedPayload, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(NSM_INSTANCE_MAX + 1, cc, reasonCode, expectedPayload,
response);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
cc = NSM_ERROR;
reasonCode = ERR_TIMEOUT;
rc = function(instanceId, cc, reasonCode, expectedPayload, badResponse);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(command, badData->command);
EXPECT_EQ(cc, badData->completion_code);
EXPECT_EQ(reasonCode, badData->reason_code);
}
void testEncodeCommonResponse(
std::function<int(uint8_t, uint8_t, uint16_t, nsm_msg *)> function,
uint8_t nvidiaMsgType, uint8_t command)
{
const uint8_t payload = 0;
testEncodeResponse(
[function](uint8_t instanceId, uint8_t cc, uint16_t reasonCode,
const uint8_t *data, nsm_msg *msg) {
if (data == nullptr) {
// special case for function with one parameter less
return (int)NSM_SW_ERROR_NULL;
}
return function(instanceId, cc, reasonCode, msg);
},
nvidiaMsgType, command, 0, &payload, nullptr);
}
void testDecodeResponse(std::function<int(const nsm_msg *, size_t, uint8_t *,
uint16_t *, uint8_t *)>
function,
uint8_t nvidiaMsgType, uint8_t command,
size_t payloadSize, const uint8_t *expectedPayload,
uint8_t *payload)
{
const uint8_t instanceId = 0;
Response responseMsg{
0x10,
0xDE, // PCI VID: NVIDIA 0x10DE
(instanceId & 0x1f), // RQ=0, D=0, RSVD=0, INSTANCE_ID=0
0x89, // OCP_TYPE=8, OCP_VER=9
nvidiaMsgType, // NVIDIA_MSG_TYPE
command, // command
NSM_SUCCESS, // completion code
0, // reserved
0, // reserved
uint8_t(payloadSize), // data size
uint8_t(payloadSize >> 8) // data size
};
responseMsg.insert(responseMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
auto len = responseMsg.size();
// Good test
uint8_t cc = NSM_SUCCESS;
uint16_t reasonCode = ERR_NULL;
auto rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(NSM_SUCCESS, cc);
EXPECT_EQ(ERR_NULL, reasonCode);
// Bad tests
rc = function(nullptr, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len, nullptr, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len, &cc, nullptr, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len, &cc, &reasonCode, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len - 1, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_LENGTH, rc);
Response badResponseMsg{
0x10,
0xDE, // PCI VID: NVIDIA 0x10DE
(instanceId & 0x1f), // RQ=0, D=0, RSVD=0, INSTANCE_ID=0
0x89, // OCP_TYPE=8, OCP_VER=9
nvidiaMsgType, // NVIDIA_MSG_TYPE
command, // command
NSM_ERROR, // completion code
ERR_TIMEOUT, // reason code
0, // reason code
};
response = reinterpret_cast<nsm_msg *>(badResponseMsg.data());
len = badResponseMsg.size();
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(NSM_ERROR, cc);
EXPECT_EQ(ERR_TIMEOUT, reasonCode);
}
void testDecodeCommonResponse(
std::function<int(const nsm_msg *, size_t, uint8_t *, uint16_t *)> function,
uint8_t nvidiaMsgType, uint8_t command)
{
uint8_t payload = 0;
testDecodeResponse(
[function](const nsm_msg *msg, size_t len, uint8_t *cc,
uint16_t *reasonCode, uint8_t *data) {
if (data == nullptr) {
// special case for function with one parameter less
return (int)NSM_SW_ERROR_NULL;
}
return function(msg, len, cc, reasonCode);
},
nvidiaMsgType, command, 0, &payload, &payload);
}
void testEncodeLongRunningResponse(
std::function<int(uint8_t, uint8_t, uint16_t, const uint8_t *, nsm_msg *)>
function,
uint8_t nvidiaMsgType, uint8_t command, size_t payloadSize,
const uint8_t *expectedPayload, uint8_t *payload)
{
const uint8_t instanceId = 0;
Response responseMsg(sizeof(struct nsm_msg_hdr) + NSM_EVENT_MIN_LEN +
sizeof(struct nsm_long_running_resp));
responseMsg.insert(responseMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
auto event = reinterpret_cast<nsm_event *>(response->payload);
auto longRunning =
reinterpret_cast<nsm_long_running_resp *>(event->data);
// Good test
uint16_t eventStateData;
nsm_long_running_event_state eventState{
.nvidia_message_type = nvidiaMsgType, .command = command};
memcpy(&eventStateData, &eventState, sizeof(uint16_t));
uint8_t cc = NSM_SUCCESS;
uint16_t reasonCode = ERR_NULL;
auto rc =
function(instanceId, cc, reasonCode, expectedPayload, response);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(1, response->hdr.request);
EXPECT_EQ(1, response->hdr.datagram);
EXPECT_EQ(instanceId, response->hdr.instance_id);
EXPECT_EQ(NSM_TYPE_DEVICE_CAPABILITY_DISCOVERY,
response->hdr.nvidia_msg_type);
EXPECT_EQ(NSM_LONG_RUNNING_EVENT, event->event_id);
EXPECT_EQ(NSM_NVIDIA_GENERAL_EVENT_CLASS, event->event_class);
EXPECT_EQ(eventStateData, event->event_state);
EXPECT_EQ(cc, longRunning->completion_code);
EXPECT_EQ(instanceId, longRunning->instance_id);
EXPECT_EQ(payloadSize + sizeof(nsm_long_running_resp),
event->data_size);
if (payload != nullptr) {
memcpy(payload,
event->data + sizeof(struct nsm_long_running_resp),
payloadSize);
}
// Bad tests
Response badResponseMsg(sizeof(nsm_msg_hdr) + NSM_EVENT_MIN_LEN +
sizeof(nsm_long_running_non_success_resp));
auto badResponse = reinterpret_cast<nsm_msg *>(badResponseMsg.data());
auto badEvent = reinterpret_cast<nsm_event *>(badResponse->payload);
auto badData = reinterpret_cast<nsm_long_running_non_success_resp *>(
badEvent->data);
rc = function(instanceId, cc, reasonCode, nullptr, response);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(instanceId, cc, reasonCode, expectedPayload, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(NSM_INSTANCE_MAX + 1, cc, reasonCode, expectedPayload,
response);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
cc = NSM_ERROR;
reasonCode = ERR_TIMEOUT;
rc = function(instanceId, cc, reasonCode, expectedPayload, badResponse);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(cc, badData->completion_code);
EXPECT_EQ(reasonCode, badData->reason_code);
}
void testDecodeLongRunningResponse(
std::function<int(const nsm_msg *, size_t, uint8_t *, uint16_t *,
uint8_t *)>
function,
uint8_t nvidiaMsgType, uint8_t command, size_t payloadSize,
const uint8_t *expectedPayload, uint8_t *payload)
{
const uint8_t instanceId = 0;
Response responseMsg{
0x10,
0xDE, // PCI VID: NVIDIA 0x10DE
(instanceId & 0x1f), // RQ=0, D=0, RSVD=0, INSTANCE_ID=0
0x89, // OCP_TYPE=8, OCP_VER=9
NSM_TYPE_DEVICE_CAPABILITY_DISCOVERY, // NVIDIA_MSG_TYPE
0x08, // NSM_EVENT_VERSION=0, ACK=1, RES=0
NSM_LONG_RUNNING_EVENT, // EVENT_ID
NSM_NVIDIA_GENERAL_EVENT_CLASS, // EVENT_CLASS
nvidiaMsgType, // EVENT_STATE >> 8
command, // EVENT_STATE & 0xFF
uint8_t(payloadSize +
sizeof(struct nsm_long_running_resp)), // data size
instanceId, // instance id
0, // completion code
0, // reserved
0, // reserved
};
responseMsg.insert(responseMsg.end(), expectedPayload,
expectedPayload + payloadSize);
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
auto len = responseMsg.size();
// Good test
uint8_t cc = NSM_SUCCESS;
uint16_t reasonCode = ERR_NULL;
auto rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_SUCCESS, rc);
EXPECT_EQ(NSM_SUCCESS, cc);
EXPECT_EQ(ERR_NULL, reasonCode);
rc = function(response, len, &cc, &reasonCode, nullptr);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
// Bad tests
rc = function(nullptr, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len, nullptr, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len, &cc, nullptr, payload);
EXPECT_EQ(NSM_SW_ERROR_NULL, rc);
rc = function(response, len - 1, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_LENGTH, rc);
Response badResponseMsg{
0x10,
0xDE, // PCI VID: NVIDIA 0x10DE
(instanceId & 0x1f), // RQ=0, D=0, RSVD=0, INSTANCE_ID=0
0x89, // OCP_TYPE=8, OCP_VER=9
NSM_TYPE_DEVICE_CAPABILITY_DISCOVERY, // EVENT_MSG_TYPE
0x08, // NSM_EVENT_VERSION=0, ACK=1, RES=0
NSM_LONG_RUNNING_EVENT, // EVENT_ID
NSM_NVIDIA_GENERAL_EVENT_CLASS, // EVENT_CLASS
command, // Bad order EVENT_STATE >> 8
nvidiaMsgType, // Bad order EVENT_STATE & 0xFF
sizeof(struct nsm_long_running_non_success_resp), // data size
instanceId, // instance id
NSM_ERROR, // completion code
ERR_TIMEOUT, // reason_code >> 8
0, // reason_code & 0xFF
};
response = reinterpret_cast<nsm_msg *>(badResponseMsg.data());
len = badResponseMsg.size();
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_LENGTH, rc);
EXPECT_EQ(NSM_ERROR, cc);
EXPECT_EQ(ERR_TIMEOUT, reasonCode);
auto event = reinterpret_cast<nsm_event *>(response->payload);
nsm_long_running_event_state badEventState;
memcpy(&badEventState, &event->event_state, sizeof(uint16_t));
EXPECT_EQ(command, badEventState.nvidia_message_type);
EXPECT_EQ(nvidiaMsgType, badEventState.command);
// Changing to good message
badResponseMsg = responseMsg;
response = reinterpret_cast<nsm_msg *>(badResponseMsg.data());
event = reinterpret_cast<nsm_event *>(response->payload);
auto eventState = reinterpret_cast<nsm_long_running_event_state *>(
&event->event_state);
len = badResponseMsg.size();
const auto offset = [response](uint8_t *data) {
return (size_t)data - (size_t)response;
};
badResponseMsg[offset(&response->hdr.nvidia_msg_type)] =
NSM_TYPE_PLATFORM_ENVIRONMENTAL; // Injecting Bad EVENT_MSG_TYPE
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
badResponseMsg = responseMsg; // Reverting good msg
badResponseMsg[offset(&event->event_id)] =
0x00; // Injecting Bad EVENT_ID
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
badResponseMsg = responseMsg; // Reverting good msg
badResponseMsg[offset(&event->event_class)] =
1; // Injecting Bad EVENT_CLASS
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
badResponseMsg = responseMsg; // Reverting good msg
badResponseMsg[offset(&eventState->nvidia_message_type)] =
NSM_TYPE_DEVICE_CAPABILITY_DISCOVERY; // Injecting Bad
// NVIDIA_MSG_TYPE
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
badResponseMsg = responseMsg; // Reverting good msg
badResponseMsg[offset(&eventState->command)] =
command + 1; // Injecting Bad command
rc = function(response, len, &cc, &reasonCode, payload);
EXPECT_EQ(NSM_SW_ERROR_DATA, rc);
}