| // 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 |