blob: ffbe2bb6f4b1943c3b3771a161b29dd87eeb9aaa [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/smbios/smbios.h>
#include <fbl/array.h>
#include <memory>
#include <zxtest/zxtest.h>
namespace {
uint8_t ComputeChecksum(const uint8_t* data, size_t len) {
unsigned int sum = 0;
for (size_t i = 0; i < len; ++i) {
sum += data[i];
}
return static_cast<uint8_t>(sum);
}
smbios::EntryPoint2_1 CreateFakeEntryPoint(const fbl::Array<uint8_t>& structs,
uint16_t structures_count) {
smbios::EntryPoint2_1 ep;
memcpy(ep.anchor_string, "_SM_", 4);
ep.checksum = 0;
ep.length = sizeof(ep);
ep.major_ver = 2;
ep.minor_ver = 1;
ep.max_struct_size = 256;
ep.ep_rev = 0;
memset(ep.formatted_area, 0, sizeof(ep.formatted_area));
memcpy(ep.intermediate_anchor_string, "_DMI_", 5);
ep.intermediate_checksum = 0;
ep.struct_table_length = static_cast<uint16_t>(structs.size());
ep.struct_table_phys = 0x1000; // Fake physical address
ep.struct_count = structures_count;
ep.bcd_rev = 0x21;
// The specification defines the offsets for this checksum
ep.intermediate_checksum = static_cast<uint8_t>(
256u - ComputeChecksum(reinterpret_cast<const uint8_t*>(&ep) + 0x10, 0xf));
ep.checksum = static_cast<uint8_t>(
256 - ComputeChecksum(reinterpret_cast<const uint8_t*>(&ep), sizeof(ep)));
return ep;
}
#define BIOS_STRING1 "string1"
#define BIOS_STRING2 "string2"
// Create fake SMBIOSv2.1 structures
void CreateFakeSmbios(smbios::EntryPoint2_1* ep, fbl::Array<uint8_t>* structs) {
constexpr uint16_t kNumStructures = 2;
// A double null terminates the string table
const char bios_info_strings[] =
BIOS_STRING1 "\0"
BIOS_STRING2 "\0";
const size_t bios_info_size = sizeof(smbios::BiosInformationStruct2_0) +
sizeof(bios_info_strings);
const char sys_info_strings[] = "\0";
const size_t sys_info_size = sizeof(smbios::SystemInformationStruct2_1) +
sizeof(sys_info_strings);
const size_t struct_data_size = bios_info_size + sys_info_size;
fbl::Array<uint8_t> struct_data(new uint8_t[struct_data_size](), struct_data_size);
uint8_t* next_struct_data = struct_data.get();
smbios::BiosInformationStruct2_0 bios_info = {};
bios_info.hdr.type = smbios::StructType::BiosInfo;
bios_info.hdr.length = sizeof(bios_info);
bios_info.hdr.handle = 0;
memcpy(next_struct_data, &bios_info, sizeof(bios_info));
next_struct_data += sizeof(bios_info);
memcpy(next_struct_data, bios_info_strings, sizeof(bios_info_strings));
next_struct_data += sizeof(bios_info_strings);
smbios::SystemInformationStruct2_1 sys_info = {};
sys_info.hdr.type = smbios::StructType::SystemInfo;
sys_info.hdr.length = sizeof(sys_info);
sys_info.hdr.handle = 1;
memcpy(next_struct_data, &sys_info, sizeof(sys_info));
next_struct_data += sizeof(sys_info);
memcpy(next_struct_data, sys_info_strings, sizeof(sys_info_strings));
next_struct_data += sizeof(sys_info_strings);
ASSERT_EQ(struct_data.get() + struct_data_size, next_struct_data);
*ep = CreateFakeEntryPoint(struct_data, kNumStructures);
ASSERT_TRUE(ep->IsValid());
*structs = std::move(struct_data);
}
TEST(SmbiosTestCase, WalkStructs) {
smbios::EntryPoint2_1 ep;
fbl::Array<uint8_t> structs;
ASSERT_NO_FATAL_FAILURES(CreateFakeSmbios(&ep, &structs));
bool tables_seen[2] = {};
auto walk_cb = [&ep, &tables_seen](smbios::SpecVersion version, const smbios::Header* h,
const smbios::StringTable& st) {
EXPECT_EQ(version.major_ver, ep.major_ver);
EXPECT_EQ(version.minor_ver, ep.minor_ver);
switch (h->type) {
case smbios::StructType::BiosInfo:
case smbios::StructType::SystemInfo: {
EXPECT_FALSE(tables_seen[static_cast<size_t>(h->type)]);
tables_seen[static_cast<size_t>(h->type)] = true;
break;
}
default:
ADD_FAILURE("Saw unexpected header type");
}
return ZX_OK;
};
ASSERT_OK(ep.WalkStructs(reinterpret_cast<uintptr_t>(structs.get()), walk_cb));
ASSERT_TRUE(tables_seen[0]);
ASSERT_TRUE(tables_seen[1]);
}
TEST(SmbiosTestCase, WalkStructsEarlyStop) {
smbios::EntryPoint2_1 ep;
fbl::Array<uint8_t> structs;
ASSERT_NO_FATAL_FAILURES(CreateFakeSmbios(&ep, &structs));
auto walk_cb = [](smbios::SpecVersion version, const smbios::Header* h,
const smbios::StringTable& st) {
switch (h->type) {
case smbios::StructType::BiosInfo: return ZX_ERR_STOP;
case smbios::StructType::SystemInfo: {
ADD_FAILURE("Iterator saw SystemInfo");
break;
}
default:
ADD_FAILURE("Saw unexpected header type");
}
return ZX_OK;
};
ASSERT_OK(ep.WalkStructs(reinterpret_cast<uintptr_t>(structs.get()), walk_cb));
}
TEST(SmbiosTestCase, GetString) {
smbios::EntryPoint2_1 ep;
fbl::Array<uint8_t> structs;
ASSERT_NO_FATAL_FAILURES(CreateFakeSmbios(&ep, &structs));
auto walk_cb = [](smbios::SpecVersion version, const smbios::Header* h,
const smbios::StringTable& st) {
switch (h->type) {
case smbios::StructType::BiosInfo: {
const char* str = nullptr;
EXPECT_OK(st.GetString(0, &str));
EXPECT_STR_EQ("<null>", str);
EXPECT_OK(st.GetString(1, &str));
EXPECT_STR_EQ(BIOS_STRING1, str);
EXPECT_OK(st.GetString(2, &str));
EXPECT_STR_EQ(BIOS_STRING2, str);
EXPECT_EQ(ZX_ERR_NOT_FOUND, st.GetString(3, &str));
break;
}
case smbios::StructType::SystemInfo: {
const char* str = nullptr;
EXPECT_OK(st.GetString(0, &str));
EXPECT_STR_EQ("<null>", str);
EXPECT_EQ(ZX_ERR_NOT_FOUND, st.GetString(1, &str));
break;
}
default:
ADD_FAILURE("Saw unexpected header type");
}
return ZX_OK;
};
ASSERT_OK(ep.WalkStructs(reinterpret_cast<uintptr_t>(structs.get()), walk_cb));
}
} // namespace