blob: 9852d5f79c094092a7a41f69b303c56c882a05b7 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/acpi_lite.h>
#include <lib/acpi_lite/structures.h>
#include <lib/boot-shim/boot-shim.h>
#include <lib/boot-shim/efi.h>
#include <lib/boot-shim/test-helper.h>
#include <zircon/boot/image.h>
#include <efi/boot-services.h>
#include <efi/system-table.h>
#include <zxtest/zxtest.h>
namespace {
template <class Item, uint32_t Type,
size_t BufferSize = boot_shim::testing::TestHelper::kDefaultBufferSize, typename T,
typename... Args>
void EfiTest(const T& expected_payload, Args&&... args) {
using TestShim = boot_shim::BootShim<Item>;
boot_shim::testing::TestHelper test;
TestShim shim(__func__, test.log());
if constexpr (sizeof...(Args) > 0) {
auto init = [&]() { return shim.template Get<Item>().Init(std::forward<Args>(args)...); };
if constexpr (std::is_void_v<decltype(init())>) {
init();
} else {
auto result = init();
EXPECT_TRUE(result.is_ok(), "EFI error %#zx", result.error_value());
}
}
auto [buffer, owner] = test.GetZbiBuffer(BufferSize);
typename TestShim::DataZbi zbi(buffer);
ASSERT_TRUE(zbi.clear().is_ok());
auto result = shim.AppendItems(zbi);
ASSERT_TRUE(result.is_ok());
size_t found_payload_count = 0;
zbitl::ByteView found_payload;
for (auto [header, payload] : zbi) {
if (header->type == Type) {
++found_payload_count;
found_payload = payload;
}
}
EXPECT_TRUE(zbi.take_error().is_ok());
if constexpr (std::is_same_v<T, std::monostate>) {
EXPECT_EQ(0, found_payload_count);
} else {
EXPECT_EQ(1, found_payload_count);
EXPECT_EQ(found_payload.size(), sizeof(expected_payload));
EXPECT_BYTES_EQ(found_payload.data(), &expected_payload, sizeof(expected_payload));
}
}
TEST(BootShimTests, EfiSystemTableNone) {
ASSERT_NO_FATAL_FAILURE(
(EfiTest<boot_shim::EfiSystemTableItem, ZBI_TYPE_EFI_SYSTEM_TABLE>(std::monostate{})));
}
TEST(BootShimTests, EfiSystemTable) {
constexpr efi_system_table kTable = {};
const uint64_t expected_payload = reinterpret_cast<uintptr_t>(&kTable);
ASSERT_NO_FATAL_FAILURE((EfiTest<boot_shim::EfiSystemTableItem, ZBI_TYPE_EFI_SYSTEM_TABLE>(
expected_payload, &kTable)));
}
TEST(BootShimTests, EfiGetVendorTable) {
constexpr efi_guid kTestGuid = {1, 2, 3, {4, 5, 6, 7, 8}};
static constexpr std::string_view kFakeTable = "VendorPrefix<data here>";
static constexpr efi_configuration_table kConfigTable[] = {
{.VendorGuid = kTestGuid, .VendorTable = kFakeTable.data()}};
static constexpr efi_system_table kSystemTable = {
.NumberOfTableEntries = std::size(kConfigTable),
.ConfigurationTable = kConfigTable,
};
EXPECT_NULL(boot_shim::EfiGetVendorTable(&kSystemTable, {}));
EXPECT_EQ(boot_shim::EfiGetVendorTable(&kSystemTable, kTestGuid, "VendorPrefix"),
kFakeTable.data());
}
TEST(BootShimTests, EfiSmbiosNone) {
ASSERT_NO_FATAL_FAILURE((EfiTest<boot_shim::EfiSmbiosItem, ZBI_TYPE_SMBIOS>(std::monostate{})));
}
TEST(BootShimTests, EfiSmbios) {
static constexpr std::string_view kFakeSmbios = "_SM_<real data here>";
static constexpr efi_configuration_table kConfigTable[] = {
{.VendorGuid = SMBIOS_TABLE_GUID, .VendorTable = kFakeSmbios.data()},
};
static constexpr efi_system_table kSystemTable = {
.NumberOfTableEntries = std::size(kConfigTable),
.ConfigurationTable = kConfigTable,
};
const uint64_t expected_payload = reinterpret_cast<uintptr_t>(kFakeSmbios.data());
ASSERT_NO_FATAL_FAILURE(
(EfiTest<boot_shim::EfiSmbiosItem, ZBI_TYPE_SMBIOS>(expected_payload, &kSystemTable)));
}
TEST(BootShimTests, EfiSmbiosV3) {
static constexpr std::string_view kFakeSmbios = "_SM3_<real data here>";
static constexpr efi_configuration_table kConfigTable[] = {
{.VendorGuid = SMBIOS3_TABLE_GUID, .VendorTable = kFakeSmbios.data()},
};
static constexpr efi_system_table kSystemTable = {
.NumberOfTableEntries = std::size(kConfigTable),
.ConfigurationTable = kConfigTable,
};
const uint64_t expected_payload = reinterpret_cast<uintptr_t>(kFakeSmbios.data());
ASSERT_NO_FATAL_FAILURE(
(EfiTest<boot_shim::EfiSmbiosItem, ZBI_TYPE_SMBIOS>(expected_payload, &kSystemTable)));
}
// Mock up some ACPI tables in memory. This is sample ACPI data from an Intel
// NUC 7i5DN, borrowed from acpi_lite/test_data.cc; but it has to be fixed up
// to point to itself.
class MockAcpiTables {
public:
MockAcpiTables(const MockAcpiTables&) = delete;
MockAcpiTables() {
static_assert(sizeof(kNucRsdp) == sizeof(rsdp_));
memcpy(&rsdp_, kNucRsdp, sizeof(rsdp_));
rsdp_.xsdt_address = reinterpret_cast<uintptr_t>(kNucXsdt);
rsdp_.extended_checksum -= Checksum({
reinterpret_cast<const uint8_t*>(&rsdp_),
sizeof(rsdp_),
});
}
private:
acpi_lite::AcpiRsdpV2 rsdp_;
static uint8_t Checksum(cpp20::span<const uint8_t> bytes) {
uint8_t sum = 0;
for (uint8_t byte : bytes) {
sum = static_cast<uint8_t>(sum + byte);
}
return sum;
}
static constexpr uint8_t kNucRsdp[] = {
0x52, 0x53, 0x44, 0x20, 0x50, 0x54, 0x52, 0x20, 0x8a, 0x49, 0x4e, 0x54,
0x45, 0x4c, 0x00, 0x02, 0x28, 0x90, 0xa2, 0x7f, 0x24, 0x00, 0x00, 0x00,
0xc0, 0x90, 0xa2, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00,
};
static constexpr uint8_t kNucXsdt[] = {
0x58, 0x53, 0x44, 0x54, 0x04, 0x01, 0x00, 0x00, 0x01, 0xc8, 0x49, 0x4e, 0x54, 0x45, 0x4c,
0x20, 0x4e, 0x55, 0x43, 0x37, 0x69, 0x35, 0x44, 0x4e, 0x1f, 0x00, 0x00, 0x00, 0x41, 0x4d,
0x49, 0x20, 0x13, 0x00, 0x01, 0x00, 0x30, 0x31, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x48,
0x32, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x32, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x18, 0x33, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x33, 0xa5, 0x7f, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x33, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x58, 0x37, 0xa5, 0x7f, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x38, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x48, 0x6a, 0xa5, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x80, 0x6a, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x6a, 0xa5, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x38, 0xbd, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xce, 0xa5,
0x7f, 0x00, 0x00, 0x00, 0x00, 0x08, 0xd6, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x50, 0xd6,
0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x98,
0xee, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xef, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x80, 0xf2, 0xa5, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x88, 0x22, 0xa6, 0x7f, 0x00, 0x00, 0x00,
0x00, 0xa0, 0x27, 0xa6, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x27, 0xa6, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x30, 0x28, 0xa6, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3b, 0xa6, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x08, 0x3c, 0xa6, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3c, 0xa6, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x78, 0x3c, 0xa6, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0xa6,
0x7f, 0x00, 0x00, 0x00, 0x00,
};
};
TEST(BootShimTests, EfiGetAcpi) {
constexpr efi_system_table kEmptySystemTable = {};
zx::status<acpi_lite::AcpiParser> result = boot_shim::EfiGetAcpi(&kEmptySystemTable);
ASSERT_TRUE(result.is_error());
EXPECT_EQ(result.status_value(), ZX_ERR_NOT_FOUND);
const MockAcpiTables kAcpiTables;
const uint64_t kRsdpPa = reinterpret_cast<uintptr_t>(&kAcpiTables);
const efi_configuration_table kConfigTable[] = {
{.VendorGuid = ACPI_TABLE_GUID, .VendorTable = &kAcpiTables},
};
const efi_system_table kSystemTable = {
.NumberOfTableEntries = std::size(kConfigTable),
.ConfigurationTable = kConfigTable,
};
result = boot_shim::EfiGetAcpi(&kSystemTable);
ASSERT_TRUE(result.is_ok(), "status %d", result.status_value());
EXPECT_EQ(result->rsdp_pa(), kRsdpPa);
const efi_configuration_table kConfigTable2[] = {
{.VendorGuid = ACPI_20_TABLE_GUID, .VendorTable = &kAcpiTables},
};
const efi_system_table kSystemTable2 = {
.NumberOfTableEntries = std::size(kConfigTable2),
.ConfigurationTable = kConfigTable2,
};
result = boot_shim::EfiGetAcpi(&kSystemTable2);
ASSERT_TRUE(result.is_ok(), "status %d", result.status_value());
EXPECT_EQ(result->rsdp_pa(), kRsdpPa);
}
} // namespace