blob: 04578a14df722707a9fb7a53e61b75790cc83a30 [file]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "runtime_probe/functions/memory.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <base/containers/span.h>
#include <base/files/file_path.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include "runtime_probe/probe_function.h"
#include "runtime_probe/utils/file_test_utils.h"
#include "runtime_probe/utils/function_test_utils.h"
namespace runtime_probe {
namespace {
constexpr auto kTestLength = 40; // Should be >= DmiMemoryRaw in bytes.
constexpr auto kKbMask = 1UL << 15;
constexpr auto kMemoryType = 17;
// Refer to SMBIOS specification.
/*
https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.3.0.pdf
*/
struct DmiMemoryRaw {
// Header
uint8_t type;
uint8_t length;
uint16_t handle;
// Memory attributes
uint8_t pad_1[8]; // skipped values
uint16_t size; // bit15: 0=MiB, 1=KiB
uint8_t pad_2[2]; // skipped values
uint8_t locator; // string
uint8_t pad_3[4]; // skipped values
uint16_t speed; // in MHz
uint8_t manufacturer; // string
uint8_t serial_number; // string
uint8_t asset_tag; // string
uint8_t part_number; // string
} __attribute__((packed));
class MemoryTest : public BaseFunctionTest {
protected:
// @param text_strings: <string number, text string>, map string numbers in
// dmi_memory to given strings.
void SetDmiData(int slot_id,
DmiMemoryRaw dmi_memory,
std::map<int, std::string> text_strings) {
std::vector<uint8_t> buffer(dmi_memory.length);
uint8_t* ptr = reinterpret_cast<uint8_t*>(&dmi_memory);
CHECK(buffer.size() >= sizeof(dmi_memory));
std::copy(ptr, ptr + sizeof(dmi_memory), buffer.data());
int current_index = 1;
const std::string dont_care_text("don't care");
for (const auto& [index, text] : text_strings) {
for (; current_index < index; ++current_index) {
// Text strings not in the map.
buffer.insert(buffer.end(), dont_care_text.begin(),
dont_care_text.end());
buffer.push_back('\0');
}
buffer.insert(buffer.end(), text.begin(), text.end());
buffer.push_back('\0');
current_index = index + 1;
}
SetFile({base::StringPrintf("sys/firmware/dmi/entries/%d-%d/raw",
kMemoryType, slot_id)},
base::span<uint8_t>(buffer));
}
};
TEST_F(MemoryTest, ProbeMemory) {
const int locator_1 = 4;
const int part_number_1 = 6;
std::map<int, std::string> text_strings_1{{locator_1, "Channel-0-DIMM-0"},
{part_number_1, "WXYZ1234-ABC"}};
auto dmi_memory_raw_1 = DmiMemoryRaw{.length = kTestLength,
.size = 4096,
.locator = locator_1,
.speed = 2933,
.part_number = part_number_1};
SetDmiData(0, dmi_memory_raw_1, text_strings_1);
const int locator_2 = 4;
const int part_number_2 = 6;
std::map<int, std::string> text_strings_2{{locator_2, "Channel-1-DIMM-0"},
{part_number_2, "WXYZ5678-ABC"}};
auto dmi_memory_raw_2 = DmiMemoryRaw{.length = kTestLength,
.size = 4096,
.locator = locator_2,
.speed = 2933,
.part_number = part_number_2};
SetDmiData(1, dmi_memory_raw_2, text_strings_2);
auto ans = CreateProbeResultFromJson(R"JSON(
[
{
"locator": "Channel-0-DIMM-0",
"part": "WXYZ1234-ABC",
"size": 4096,
"slot": 0,
"speed": 2933
},
{
"locator": "Channel-1-DIMM-0",
"part": "WXYZ5678-ABC",
"size": 4096,
"slot": 1,
"speed": 2933
}
]
)JSON");
ans[0].GetDict().Set(
"path",
GetPathUnderRoot(
{base::StringPrintf("sys/firmware/dmi/entries/%d-0", kMemoryType)})
.value());
ans[1].GetDict().Set(
"path",
GetPathUnderRoot(
{base::StringPrintf("sys/firmware/dmi/entries/%d-1", kMemoryType)})
.value());
auto probe_function = CreateProbeFunction<MemoryFunction>();
auto result = EvalProbeFunction(probe_function.get());
EXPECT_EQ(result, ans);
}
TEST_F(MemoryTest, ProbeKbMemory) {
const int locator = 4;
const int part_number = 6;
std::map<int, std::string> text_strings{{locator, "Channel-0-DIMM-0"},
{part_number, "WXYZ1234-ABC"}};
auto dmi_memory_raw = DmiMemoryRaw{.length = kTestLength,
.size = 4096 | kKbMask,
.locator = locator,
.speed = 2933,
.part_number = part_number};
SetDmiData(0, dmi_memory_raw, text_strings);
auto ans = CreateProbeResultFromJson(R"JSON(
[
{
"locator": "Channel-0-DIMM-0",
"part": "WXYZ1234-ABC",
"size": 4,
"slot": 0,
"speed": 2933
}
]
)JSON");
ans[0].GetDict().Set(
"path",
GetPathUnderRoot(
{base::StringPrintf("sys/firmware/dmi/entries/%d-0", kMemoryType)})
.value());
auto probe_function = CreateProbeFunction<MemoryFunction>();
auto result = EvalProbeFunction(probe_function.get());
EXPECT_EQ(result, ans);
}
TEST_F(MemoryTest, NoDmiData) {
auto ans = CreateProbeResultFromJson(R"JSON(
[]
)JSON");
auto probe_function = CreateProbeFunction<MemoryFunction>();
auto result = EvalProbeFunction(probe_function.get());
EXPECT_EQ(result, ans);
}
TEST_F(MemoryTest, InvalidDmiData) {
SetFile(
{base::StringPrintf("sys/firmware/dmi/entries/%d-0/raw", kMemoryType)},
base::span<uint8_t>{}); // Invalid data.
const int locator = 4;
const int part_number = 6;
std::map<int, std::string> text_strings{{locator, "Channel-0-DIMM-0"},
{part_number, "WXYZ1234-ABC"}};
auto dmi_memory_raw = DmiMemoryRaw{.length = kTestLength,
.size = 4096,
.locator = locator,
.speed = 2933,
.part_number = part_number};
SetDmiData(1, dmi_memory_raw, text_strings);
// Return the valid one.
auto ans = CreateProbeResultFromJson(R"JSON(
[
{
"locator": "Channel-0-DIMM-0",
"part": "WXYZ1234-ABC",
"size": 4096,
"slot": 1,
"speed": 2933
}
]
)JSON");
ans[0].GetDict().Set(
"path",
GetPathUnderRoot(
{base::StringPrintf("sys/firmware/dmi/entries/%d-1", kMemoryType)})
.value());
auto probe_function = CreateProbeFunction<MemoryFunction>();
auto result = EvalProbeFunction(probe_function.get());
EXPECT_EQ(result, ans);
}
} // namespace
} // namespace runtime_probe