| // Copyright 2021 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/instrumentation/asan.h> |
| #include <lib/unittest/unittest.h> |
| |
| #include <efi/types.h> |
| #include <ktl/byte.h> |
| #include <platform/efi.h> |
| |
| #include "efi-private.h" |
| |
| namespace { |
| |
| // Ensure EFI is present on platforms we know should have it. |
| // |
| // This test aims to prevent EFI support from being silently dropped. |
| NO_ASAN bool TestEfiPresent() { |
| BEGIN_TEST; |
| |
| // Grab our current aspace. |
| VmAspace* old_aspace = Thread::Current::active_aspace(); |
| |
| // Attempt to fetch EFI services. |
| EfiServicesActivation services = TryActivateEfiServices(); |
| |
| // Ensure we got back a valid result if EFI meant to be present. |
| if (IsEfiExpected()) { |
| EXPECT_TRUE(services.valid()); |
| // This should switch back to the old aspace. |
| services.reset(); |
| |
| // Make sure it actually did. |
| EXPECT_EQ(old_aspace, Thread::Current::active_aspace()); |
| } else { |
| printf("Unknown if EFI is expected to be supported on platform. Skipping test.\n"); |
| } |
| |
| END_TEST; |
| } |
| |
| NO_ASAN bool TestEfiServices() { |
| BEGIN_TEST; |
| |
| // Fetch EFI services. |
| EfiServicesActivation services = TryActivateEfiServices(); |
| if (!services.valid()) { |
| // We may not have EFI services. |
| return true; |
| } |
| |
| // Ensure we can call `GetTime` and get a reasonable year (between 2000 and 2100). |
| efi_time time; |
| efi_status result = services->GetTime(&time, nullptr); |
| EXPECT_EQ(result, static_cast<efi_status>(EFI_SUCCESS)); |
| EXPECT_GT(time.Year, 2000); |
| EXPECT_LT(time.Year, 2100); |
| |
| END_TEST; |
| } |
| |
| bool TestMemoryAttributeTableInvalid() { |
| BEGIN_TEST; |
| |
| const uint8_t kShortData[] = {0xab, 0xab, 0xab, 0xab}; |
| ktl::span<const ktl::byte> bytes(reinterpret_cast<const std::byte*>(kShortData), |
| sizeof(kShortData)); |
| static_assert(sizeof(kShortData) < sizeof(efi_memory_attributes_table_header), |
| "test data is not small enough"); |
| |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, ForEachMemoryAttributeEntrySafe( |
| bytes, [](const efi_memory_descriptor*) { return ZX_OK; })); |
| |
| END_TEST; |
| } |
| |
| bool TestMemoryAttributeTableTruncated() { |
| BEGIN_TEST; |
| |
| const uint32_t kTruncatedData[] = { |
| 0x2, // header.version |
| 0x10, // header.number_of_entries |
| sizeof(efi_memory_descriptor), // header.descriptor_size |
| 0, // header.reserved |
| |
| // descriptor 0 |
| EfiRuntimeServicesCode, // descriptor.Type |
| 0, // descriptor.Padding |
| 0, |
| 0x1000, // descriptor.PhysicalStart (64-bit) |
| 0, |
| 0, // descriptor.VirtualStart (64-bit) |
| 1, |
| 0, // descriptor.NumberOfPages (64-bit) |
| 0, |
| 0, // descriptor.Attribute (64-bit) |
| |
| // descriptor 1 |
| EfiRuntimeServicesData, |
| }; |
| |
| ktl::span<const ktl::byte> bytes(reinterpret_cast<const std::byte*>(kTruncatedData), |
| sizeof(kTruncatedData)); |
| |
| bool did_callback = false; |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, |
| ForEachMemoryAttributeEntrySafe(bytes, [&](const efi_memory_descriptor*) { |
| // The first descriptor is valid so this should be called at least once. |
| did_callback = true; |
| return ZX_OK; |
| })); |
| |
| EXPECT_TRUE(did_callback); |
| |
| END_TEST; |
| } |
| |
| bool TestMemoryAttributeTableValid() { |
| BEGIN_TEST; |
| |
| const uint32_t kTruncatedData[] = { |
| 0x2, // header.version |
| 0x2, // header.number_of_entries |
| sizeof(efi_memory_descriptor), // header.descriptor_size |
| 0, // header.reserved |
| |
| // descriptor 0 |
| EfiRuntimeServicesCode, // descriptor.Type |
| 0, // descriptor.Padding |
| 0, |
| 0x1000, // descriptor.PhysicalStart (64-bit) |
| 0, |
| 0, // descriptor.VirtualStart (64-bit) |
| 1, |
| 0, // descriptor.NumberOfPages (64-bit) |
| 0, |
| 0, // descriptor.Attribute (64-bit) |
| |
| // descriptor 1 |
| EfiRuntimeServicesCode, // descriptor.Type |
| 0, // descriptor.Padding |
| 0, |
| 0x1000, // descriptor.PhysicalStart (64-bit) |
| 0, |
| 0, // descriptor.VirtualStart (64-bit) |
| 1, |
| 0, // descriptor.NumberOfPages (64-bit) |
| 0, |
| 0, // descriptor.Attribute (64-bit) |
| }; |
| |
| ktl::span<const ktl::byte> bytes(reinterpret_cast<const std::byte*>(kTruncatedData), |
| sizeof(kTruncatedData)); |
| |
| size_t callback_count = 0; |
| EXPECT_EQ(ZX_OK, ForEachMemoryAttributeEntrySafe(bytes, [&](const efi_memory_descriptor*) { |
| // The first descriptor is valid so this should be called at least once. |
| callback_count++; |
| return ZX_OK; |
| })); |
| |
| EXPECT_EQ(2u, callback_count); |
| |
| END_TEST; |
| } |
| |
| bool TestMemoryAttributeTableShortDescriptor() { |
| BEGIN_TEST; |
| |
| const uint32_t kTruncatedData[] = { |
| 0x2, // header.version |
| 0x10, // header.number_of_entries |
| sizeof(efi_memory_descriptor) - 3, // header.descriptor_size |
| 0, // header.reserved |
| |
| // descriptor 0 |
| EfiRuntimeServicesCode, // descriptor.Type |
| 0, // descriptor.Padding |
| 0, |
| 0x1000, // descriptor.PhysicalStart (64-bit) |
| 0, |
| 0, // descriptor.VirtualStart (64-bit) |
| 1, |
| 0, // descriptor.NumberOfPages (64-bit) |
| 0, |
| 0, // descriptor.Attribute (64-bit) |
| |
| // descriptor 1 |
| EfiRuntimeServicesData, |
| }; |
| |
| ktl::span<const ktl::byte> bytes(reinterpret_cast<const std::byte*>(kTruncatedData), |
| sizeof(kTruncatedData)); |
| |
| bool did_callback = false; |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, |
| ForEachMemoryAttributeEntrySafe(bytes, [&](const efi_memory_descriptor*) { |
| // The first descriptor is valid so this should be called at least once. |
| did_callback = true; |
| return ZX_OK; |
| })); |
| |
| EXPECT_FALSE(did_callback); |
| |
| END_TEST; |
| } |
| |
| } // namespace |
| |
| UNITTEST_START_TESTCASE(efi_services_tests) |
| UNITTEST("test_efi_present", TestEfiPresent) |
| UNITTEST("test_efi_services", TestEfiServices) |
| UNITTEST("test_memory_attributes_table_valid", TestMemoryAttributeTableValid) |
| UNITTEST("test_memory_attributes_table_invalid", TestMemoryAttributeTableInvalid) |
| UNITTEST("test_memory_attributes_table_truncated", TestMemoryAttributeTableTruncated) |
| UNITTEST("test_memory_attributes_table_short_descriptor", TestMemoryAttributeTableShortDescriptor) |
| UNITTEST_END_TESTCASE(efi_services_tests, "efi", "EFI service tests") |