blob: bc6abeef1ef10637cfcdbf521bcc649eb1b75479 [file] [edit]
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright OpenBMC Authors
#include "base.h"
#include "common-tests.hpp"
#include "device-configuration.h"
#include <cstring>
#include <gtest/gtest.h>
#include <vector>
namespace
{
// Read of the identity register: 8 DWORDs at 0x1080; the digest is the
// P0 map's semantic_sha256
const std::vector<uint8_t> readRequestPayload = {
0x11, 0x00, 0x10, 0x00, 0x00, 0x00, // v2 header: cmd 0x11, size 16
0x02, 0x00, 0x00, 0x00, // configuration type 2
0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // address 0x1080
0x08, 0x00, // 8 DWORDs
0x00, 0x00 // reserved
};
const std::vector<uint8_t> mapDigest = {
0x80, 0x65, 0x3a, 0xc8, 0x5e, 0x22, 0x66, 0x26, 0x5d, 0x66, 0x23,
0xa7, 0x5a, 0x4b, 0xba, 0xbd, 0xcd, 0x84, 0x49, 0x0d, 0xcb, 0xee,
0x1c, 0xf2, 0xf8, 0x0d, 0xf3, 0xef, 0xfa, 0xa5, 0xa2, 0xaf};
} // namespace
TEST(getDeviceConfigMmio, requestLayout)
{
std::vector<uint8_t> requestMsg(
sizeof(nsm_msg_hdr) + sizeof(nsm_common_req_v2) + sizeof(uint32_t) +
sizeof(nsm_mmio_access_query));
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
int rc = encode_get_device_config_mmio_req(
0, NSM_DEVICE_CONFIG_TYPE_MMIO_ACCESS, 0x1080, 8, request);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(request->hdr.nvidia_msg_type, NSM_TYPE_DEVICE_CONFIGURATION);
std::vector<uint8_t> payload(requestMsg.begin() + sizeof(nsm_msg_hdr),
requestMsg.end());
EXPECT_EQ(payload, readRequestPayload);
}
TEST(getDeviceConfigMmio, requestRejectsBadAddressOrLength)
{
std::vector<uint8_t> requestMsg(64);
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
EXPECT_EQ(encode_get_device_config_mmio_req(0, 2, 0x1041, 8, request),
NSM_SW_ERROR_DATA);
EXPECT_EQ(encode_get_device_config_mmio_req(0, 2, 0x1080, 0, request),
NSM_SW_ERROR_DATA);
EXPECT_EQ(
encode_get_device_config_mmio_req(0, 2, 0x1080, 1024, request),
NSM_SW_ERROR_DATA);
EXPECT_EQ(encode_get_device_config_mmio_req(0, 2, 0x1080, 8, nullptr),
NSM_SW_ERROR_NULL);
}
TEST(getDeviceConfigMmio, responseRoundTrip)
{
std::vector<uint8_t> responseMsg(sizeof(nsm_msg_hdr) +
sizeof(nsm_get_device_config_v2_resp) -
1 + mapDigest.size());
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
int rc = encode_get_device_config_v2_resp(
0, NSM_SUCCESS, ERR_NULL, mapDigest.data(),
static_cast<uint16_t>(mapDigest.size()), nullptr, 0, response);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
std::vector<uint8_t> data(32);
rc = decode_get_device_config_mmio_resp(response, responseMsg.size(), 8,
&cc, &reasonCode, data.data());
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(cc, NSM_SUCCESS);
EXPECT_EQ(data, mapDigest);
// asking for a different length than the reply carries is an error
std::vector<uint8_t> big(64);
rc = decode_get_device_config_mmio_resp(
response, responseMsg.size(), 16, &cc, &reasonCode, big.data());
EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH);
}
TEST(getDeviceConfigMmio, responseRejectsPendingData)
{
const std::vector<uint8_t> pending(4, 0xaa);
std::vector<uint8_t> responseMsg(sizeof(nsm_msg_hdr) +
sizeof(nsm_get_device_config_v2_resp) -
1 + mapDigest.size() + pending.size());
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
int rc = encode_get_device_config_v2_resp(
0, NSM_SUCCESS, ERR_NULL, mapDigest.data(),
static_cast<uint16_t>(mapDigest.size()), pending.data(),
static_cast<uint16_t>(pending.size()), response);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
std::vector<uint8_t> data(32);
rc = decode_get_device_config_mmio_resp(response, responseMsg.size(), 8,
&cc, &reasonCode, data.data());
EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH);
}
TEST(getDeviceConfigMmio, errorCompletionIsReported)
{
std::vector<uint8_t> responseMsg(sizeof(nsm_msg_hdr) +
sizeof(nsm_common_non_success_resp));
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
int rc =
encode_get_device_config_v2_resp(0, NSM_ERR_INVALID_DATA, ERR_NULL,
nullptr, 0, nullptr, 0, response);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
uint8_t cc = NSM_SUCCESS;
uint16_t reasonCode = ERR_NULL;
std::vector<uint8_t> data(32);
rc = decode_get_device_config_mmio_resp(response, responseMsg.size(), 8,
&cc, &reasonCode, data.data());
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(cc, NSM_ERR_INVALID_DATA);
}
TEST(setDeviceConfigMmio, requestCarriesQueryAndData)
{
const std::vector<uint8_t> data = {0xde, 0xad, 0xbe, 0xef};
std::vector<uint8_t> requestMsg(
sizeof(nsm_msg_hdr) + sizeof(nsm_common_req_v2) + sizeof(uint32_t) +
sizeof(nsm_mmio_access_query) + data.size());
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
int rc = encode_set_device_config_mmio_req(0, 2, 0x1000, 1, data.data(),
request);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
const std::vector<uint8_t> expected = {
0x10, 0x00, 0x14, 0x00, 0x00, 0x00, // cmd 0x10, size 20
0x02, 0x00, 0x00, 0x00, // type 2
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // address 0x1000
0x01, 0x00, 0x00, 0x00, // 1 DWORD, reserved
0xde, 0xad, 0xbe, 0xef};
std::vector<uint8_t> payload(requestMsg.begin() + sizeof(nsm_msg_hdr),
requestMsg.end());
EXPECT_EQ(payload, expected);
EXPECT_EQ(encode_set_device_config_mmio_req(0, 2, 0x1001, 1,
data.data(), request),
NSM_SW_ERROR_DATA);
}
TEST(getSupportedDeviceConfigs, requestLayout)
{
std::vector<uint8_t> requestMsg(
sizeof(nsm_msg_hdr) + sizeof(nsm_get_supported_device_configs_req));
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
int rc = encode_get_supported_device_configs_req(0, 0, request);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
const std::vector<uint8_t> expected = {0x12, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> payload(requestMsg.begin() + sizeof(nsm_msg_hdr),
requestMsg.end());
EXPECT_EQ(payload, expected);
uint32_t handle = 7;
rc = decode_get_supported_device_configs_req(request, requestMsg.size(),
&handle);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(handle, 0u);
}
TEST(getSupportedDeviceConfigs, responseRoundTrip)
{
const std::vector<uint32_t> types = {1, 2};
std::vector<uint8_t> responseMsg(
sizeof(nsm_msg_hdr) +
sizeof(nsm_get_supported_device_configs_resp) - sizeof(uint32_t) +
types.size() * sizeof(uint32_t));
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
int rc = encode_get_supported_device_configs_resp(
0, NSM_SUCCESS, ERR_NULL, 0, 2, types.data(),
static_cast<uint16_t>(types.size()), response);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
uint32_t nextHandle = 9;
uint32_t totalCount = 0;
std::vector<uint32_t> out(4);
uint16_t count = 0;
rc = decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode, &nextHandle,
&totalCount, out.data(), 4, &count);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(cc, NSM_SUCCESS);
EXPECT_EQ(nextHandle, 0u);
EXPECT_EQ(totalCount, 2u);
EXPECT_EQ(count, 2u);
EXPECT_EQ(out[0], 1u);
EXPECT_EQ(out[1], 2u);
// too small a buffer reports the needed count
rc = decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode, &nextHandle,
&totalCount, out.data(), 1, &count);
EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH);
EXPECT_EQ(count, 2u);
}
TEST(getSupportedDeviceConfigs, decodesDeviceReply)
{
// Reply bytes: 10 de 00 89 05 12 00 00 00 10 00 | next 0 | total 2 |
// 1 | 2
const std::vector<uint8_t> reply = {
0x10, 0xde, 0x00, 0x89, 0x05, 0x12, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
auto response = reinterpret_cast<const nsm_msg *>(reply.data());
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
uint32_t nextHandle = 9;
uint32_t totalCount = 0;
std::vector<uint32_t> out(4);
uint16_t count = 0;
int rc = decode_get_supported_device_configs_resp(
response, reply.size(), &cc, &reasonCode, &nextHandle, &totalCount,
out.data(), 4, &count);
EXPECT_EQ(rc, NSM_SW_SUCCESS);
EXPECT_EQ(cc, NSM_SUCCESS);
EXPECT_EQ(nextHandle, 0u);
EXPECT_EQ(totalCount, 2u);
EXPECT_EQ(count, 2u);
EXPECT_EQ(out[0], 1u);
EXPECT_EQ(out[1], 2u);
}
// Replies the FPGA could corrupt: every truncation and every length field
// that disagrees with the frame is refused before any byte is copied
TEST(getDeviceConfigMmio, truncatedAndInconsistentRepliesAreRejected)
{
std::vector<uint8_t> responseMsg(sizeof(nsm_msg_hdr) +
sizeof(nsm_get_device_config_v2_resp) -
1 + mapDigest.size());
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
ASSERT_EQ(encode_get_device_config_v2_resp(
0, NSM_SUCCESS, ERR_NULL, mapDigest.data(),
static_cast<uint16_t>(mapDigest.size()), nullptr, 0,
response),
NSM_SW_SUCCESS);
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
std::vector<uint8_t> data(32, 0x55);
for (size_t len = 0; len < responseMsg.size(); ++len) {
EXPECT_NE(decode_get_device_config_mmio_resp(
response, len, 8, &cc, &reasonCode, data.data()),
NSM_SW_SUCCESS)
<< "accepted a reply cut to " << len << " bytes";
}
// the data was never touched by a rejected reply
EXPECT_EQ(data, std::vector<uint8_t>(32, 0x55));
// a length field that disagrees with the frame
auto resp = reinterpret_cast<nsm_get_device_config_v2_resp *>(
response->payload);
resp->current_config_length = htole16(36);
EXPECT_EQ(decode_get_device_config_mmio_resp(response,
responseMsg.size(), 8, &cc,
&reasonCode, data.data()),
NSM_SW_ERROR_LENGTH);
resp->current_config_length = htole16(32);
resp->hdr.data_size = htole16(4);
EXPECT_EQ(decode_get_device_config_mmio_resp(response,
responseMsg.size(), 8, &cc,
&reasonCode, data.data()),
NSM_SW_ERROR_LENGTH);
// the length asked for is checked before the frame is read
EXPECT_EQ(decode_get_device_config_mmio_resp(response,
responseMsg.size(), 0, &cc,
&reasonCode, data.data()),
NSM_SW_ERROR_DATA);
EXPECT_EQ(decode_get_device_config_mmio_resp(
response, responseMsg.size(), 1024, &cc, &reasonCode,
data.data()),
NSM_SW_ERROR_DATA);
EXPECT_EQ(decode_get_device_config_mmio_resp(nullptr,
responseMsg.size(), 8, &cc,
&reasonCode, data.data()),
NSM_SW_ERROR_NULL);
EXPECT_EQ(decode_get_device_config_mmio_resp(response,
responseMsg.size(), 8, &cc,
&reasonCode, nullptr),
NSM_SW_ERROR_NULL);
}
TEST(setDeviceConfigMmio, requestRejectsNullAndBadLength)
{
const std::vector<uint8_t> data(8, 0x11);
std::vector<uint8_t> requestMsg(64);
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
EXPECT_EQ(encode_set_device_config_mmio_req(0, 2, 0x1000, 2,
data.data(), nullptr),
NSM_SW_ERROR_NULL);
EXPECT_EQ(encode_set_device_config_mmio_req(0, 2, 0x1000, 2, nullptr,
request),
NSM_SW_ERROR_NULL);
EXPECT_EQ(encode_set_device_config_mmio_req(0, 2, 0x1000, 0,
data.data(), request),
NSM_SW_ERROR_DATA);
EXPECT_EQ(encode_set_device_config_mmio_req(0, 2, 0x1000, 1024,
data.data(), request),
NSM_SW_ERROR_DATA);
}
TEST(getSupportedDeviceConfigs, truncatedAndInconsistentRepliesAreRejected)
{
const std::vector<uint32_t> types = {1, 2, 3};
std::vector<uint8_t> responseMsg(
sizeof(nsm_msg_hdr) +
sizeof(nsm_get_supported_device_configs_resp) - sizeof(uint32_t) +
types.size() * sizeof(uint32_t));
auto response = reinterpret_cast<nsm_msg *>(responseMsg.data());
ASSERT_EQ(encode_get_supported_device_configs_resp(
0, NSM_SUCCESS, ERR_NULL, 0, 3, types.data(),
static_cast<uint16_t>(types.size()), response),
NSM_SW_SUCCESS);
uint8_t cc = NSM_ERROR;
uint16_t reasonCode = ERR_NULL;
uint32_t nextHandle = 0;
uint32_t totalCount = 0;
std::vector<uint32_t> out(4, 0x5555);
uint16_t count = 0;
for (size_t len = 0; len < responseMsg.size(); ++len) {
EXPECT_NE(decode_get_supported_device_configs_resp(
response, len, &cc, &reasonCode, &nextHandle,
&totalCount, out.data(), 4, &count),
NSM_SW_SUCCESS)
<< "accepted a reply cut to " << len << " bytes";
}
EXPECT_EQ(out, std::vector<uint32_t>(4, 0x5555));
auto resp = reinterpret_cast<nsm_get_supported_device_configs_resp *>(
response->payload);
// a data size that is not whole entries
resp->hdr.data_size = htole16(2 * sizeof(uint32_t) + 6);
EXPECT_EQ(decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode,
&nextHandle, &totalCount, out.data(), 4, &count),
NSM_SW_ERROR_LENGTH);
// more entries announced than the frame carries
resp->hdr.data_size =
htole16(2 * sizeof(uint32_t) + 4 * sizeof(uint32_t));
EXPECT_EQ(decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode,
&nextHandle, &totalCount, out.data(), 4, &count),
NSM_SW_ERROR_LENGTH);
// more entries in the chunk than the total the device claims
resp->hdr.data_size =
htole16(2 * sizeof(uint32_t) + 3 * sizeof(uint32_t));
resp->total_count = htole32(2);
EXPECT_EQ(decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode,
&nextHandle, &totalCount, out.data(), 4, &count),
NSM_SW_ERROR_DATA);
EXPECT_EQ(decode_get_supported_device_configs_resp(
nullptr, responseMsg.size(), &cc, &reasonCode,
&nextHandle, &totalCount, out.data(), 4, &count),
NSM_SW_ERROR_NULL);
EXPECT_EQ(decode_get_supported_device_configs_resp(
response, responseMsg.size(), &cc, &reasonCode,
&nextHandle, &totalCount, out.data(), 4, nullptr),
NSM_SW_ERROR_NULL);
// a request cut short or carrying the wrong size is refused too
std::vector<uint8_t> requestMsg(
sizeof(nsm_msg_hdr) + sizeof(nsm_get_supported_device_configs_req));
auto request = reinterpret_cast<nsm_msg *>(requestMsg.data());
ASSERT_EQ(encode_get_supported_device_configs_req(0, 5, request),
NSM_SW_SUCCESS);
uint32_t handle = 0;
EXPECT_EQ(decode_get_supported_device_configs_req(
request, requestMsg.size() - 1, &handle),
NSM_SW_ERROR_LENGTH);
EXPECT_EQ(encode_get_supported_device_configs_req(0, 5, nullptr),
NSM_SW_ERROR_NULL);
}