blob: a8979bf6044aa2e5e3630614574c4bde545dc711 [file] [log] [blame]
// Copyright 2023 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 "efi_variables.h"
#include <lib/efi/testing/stub_boot_services.h>
#include <lib/efi/testing/stub_runtime_services.h>
#include <lib/stdcompat/span.h>
#include <efi/variable/variable_id.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <phys/efi/main.h>
using ::efi::MockBootServices;
using ::efi::MockRuntimeServices;
using ::efi::StubRuntimeServices;
using ::testing::_;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Test;
const efi_handle kImageHandle = reinterpret_cast<efi_handle>(0x10);
using namespace gigaboot;
namespace {
class EfiVariablesTest : public Test {
public:
void SetUp() override {
// Configure the necessary mocks for memory operation.
system_table_ = efi_system_table{
.RuntimeServices = mock_runtime_services_.services(),
.BootServices = mock_boot_services_.services(),
};
// EfiMain(kImageHandle, &system_table_);
gEfiImageHandle = kImageHandle;
gEfiSystemTable = &system_table_;
ON_CALL(mock_runtime_services_, QueryVariableInfo).WillByDefault(Return(EFI_NOT_FOUND));
ON_CALL(mock_runtime_services_, GetNextVariableName).WillByDefault(Return(EFI_NOT_FOUND));
ON_CALL(mock_runtime_services_, GetVariable).WillByDefault(Return(EFI_NOT_FOUND));
}
void TearDown() override {
gEfiImageHandle = 0;
gEfiSystemTable = nullptr;
}
protected:
NiceMock<MockRuntimeServices> mock_runtime_services_;
NiceMock<MockBootServices> mock_boot_services_;
efi_system_table system_table_ = {};
EfiVariables efi_variables_;
};
class EfiVariablesStubTest : public Test {
public:
void SetUp() override {
// Configure the necessary mocks for memory operation.
system_table_ = efi_system_table{
.RuntimeServices = stub_runtime_services_.services(),
.BootServices = mock_boot_services_.services(),
};
// EfiMain(kImageHandle, &system_table_);
gEfiImageHandle = kImageHandle;
gEfiSystemTable = &system_table_;
}
void TearDown() override {
gEfiImageHandle = 0;
gEfiSystemTable = nullptr;
}
void SetVariables(const std::list<efi::Variable>& vars) {
stub_runtime_services_.SetVariables(vars);
}
protected:
StubRuntimeServices stub_runtime_services_;
NiceMock<MockBootServices> mock_boot_services_;
efi_system_table system_table_ = {};
EfiVariables efi_variables_;
};
class EfiVariableInfoFixture
: public ::testing::TestWithParam<std::list<EfiVariables::EfiVariableInfo>> {};
TEST_P(EfiVariableInfoFixture, Eq) {
std::list<EfiVariables::EfiVariableInfo> input = GetParam();
for (const auto& a : input) {
EXPECT_EQ(a, a);
}
}
TEST_P(EfiVariableInfoFixture, Ne) {
std::list<EfiVariables::EfiVariableInfo> input = GetParam();
for (auto a = input.begin(); a != input.end(); a++) {
for (auto b = std::next(a); b != input.end(); b++) {
EXPECT_NE(*a, *b);
}
}
}
INSTANTIATE_TEST_SUITE_P(EfiVariableInfoTest, EfiVariableInfoFixture,
::testing::Values(std::list<EfiVariables::EfiVariableInfo>{
{0, 0, 0},
{1, 2, 3},
{3, 2, 1},
{std::numeric_limits<uint64_t>::max(), 2, 1},
{3, std::numeric_limits<uint64_t>::max(), 1},
{3, 2, std::numeric_limits<uint64_t>::max()},
{std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max()},
}));
TEST_F(EfiVariablesTest, EfiQueryVariableInfoError) {
auto res = efi_variables_.EfiQueryVariableInfo();
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesTest, DumpVarInfoSuccess) {
constexpr EfiVariables::EfiVariableInfo expected = {
.max_var_storage_size = 1,
.remaining_var_storage_size = 2,
.max_var_size = 3,
};
EXPECT_CALL(mock_runtime_services_, QueryVariableInfo)
.WillOnce([](uint32_t attributes, uint64_t* max_var_storage_size,
uint64_t* remaining_var_storage_size, uint64_t* max_var_size) {
*max_var_storage_size = expected.max_var_storage_size;
*remaining_var_storage_size = expected.remaining_var_storage_size;
*max_var_size = expected.max_var_size;
return EFI_SUCCESS;
});
auto res = efi_variables_.EfiQueryVariableInfo();
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res.value(), expected);
}
TEST_F(EfiVariablesTest, DumpVarInfoMaxValues) {
constexpr EfiVariables::EfiVariableInfo expected = {
.max_var_storage_size = std::numeric_limits<uint64_t>::max(),
.remaining_var_storage_size = std::numeric_limits<uint64_t>::max(),
.max_var_size = std::numeric_limits<uint64_t>::max(),
};
EXPECT_CALL(mock_runtime_services_, QueryVariableInfo)
.WillOnce([](uint32_t attributes, uint64_t* max_var_storage_size,
uint64_t* remaining_var_storage_size, uint64_t* max_var_size) {
*max_var_storage_size = expected.max_var_storage_size;
*remaining_var_storage_size = expected.remaining_var_storage_size;
*max_var_size = expected.max_var_size;
return EFI_SUCCESS;
});
auto res = efi_variables_.EfiQueryVariableInfo();
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res.value(), expected);
}
constexpr efi_guid kGuid[] = {
{0x0, 0x0, 0x0, {0x0}},
{0x1, 0x1, 0x1, {0x1}},
{0x2, 0x2, 0x2, {0x2}},
{0x3, 0x3, 0x3, {0x3}},
};
static const efi::VariableId kVariableId[] = {
{efi::String(u"var0"), kGuid[0]},
{efi::String(u"var1"), kGuid[1]},
{efi::String(u"var2"), kGuid[2]},
{efi::String(u"var3"), kGuid[3]},
};
static const efi::VariableValue kValue[] = {
{0x00},
{0x01, 0x02},
{0x01, 0x02, 0x03},
{0x01, 0x02, 0x03, 0x04},
};
TEST_F(EfiVariablesStubTest, GetVariableNotThere) {
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_error());
}
// Helper function to work around matching `fbl::Vector`
// Alternative is to add `using value_type = T;` to `fbl::Vector` class;
cpp20::span<const uint8_t> ToSpan(const efi::VariableValue& value) {
return cpp20::span<const uint8_t>(value.begin(), value.end());
}
TEST_F(EfiVariablesStubTest, GetVariableGood) {
SetVariables({{kVariableId[0], kValue[0]}});
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_ok());
EXPECT_THAT(ToSpan(kValue[0]), ElementsAreArray(res.value()));
}
TEST_F(EfiVariablesStubTest, GetVariableMultipleGood) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[1], kValue[1]},
{kVariableId[2], kValue[2]},
{kVariableId[3], kValue[3]},
});
for (size_t i = 0; i < std::size(kVariableId); i++) {
auto res = efi_variables_.EfiGetVariable(kVariableId[i]);
ASSERT_TRUE(res.is_ok());
EXPECT_THAT(ToSpan(kValue[i]), ElementsAreArray(res.value()));
}
}
TEST_F(EfiVariablesStubTest, GetVariableBadGuid) {
SetVariables({{{kVariableId[0].name, kGuid[1]}, kValue[0]}});
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetVariableBadName) {
SetVariables({{{kVariableId[1].name, kGuid[0]}, kValue[0]}});
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetVariableGuidDiffers) {
SetVariables({
{{kVariableId[1].name, kGuid[0]}, kValue[1]},
{kVariableId[0], kValue[0]},
{{kVariableId[2].name, kGuid[0]}, kValue[2]},
});
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_ok());
EXPECT_THAT(ToSpan(kValue[0]), ElementsAreArray(res.value()));
}
TEST_F(EfiVariablesTest, GetVariableNotThere) {
auto res = efi_variables_.EfiGetVariable(kVariableId[0]);
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetGuidFromEmptyList) {
SetVariables({});
auto res = efi_variables_.GetGuid(std::u16string_view(kVariableId[0].name));
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetGuidOneExist) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[1], kValue[1]},
});
auto res = efi_variables_.GetGuid(std::u16string_view(kVariableId[0].name));
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res.value(), kVariableId[0].vendor_guid);
}
TEST_F(EfiVariablesStubTest, GetGuidMultipleExist) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[0], kValue[1]},
});
auto res = efi_variables_.GetGuid(std::u16string_view(kVariableId[0].name));
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetGuidMissing) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[1], kValue[1]},
});
auto res = efi_variables_.GetGuid(std::u16string_view(kVariableId[2].name));
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, GetGuidBadInput) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[1], kValue[1]},
});
auto res = efi_variables_.GetGuid(u"");
ASSERT_TRUE(res.is_error());
}
TEST_F(EfiVariablesStubTest, IterateOverEmptyList) {
SetVariables({});
EfiVariables efi_variables;
EXPECT_EQ(std::distance(efi_variables.begin(), efi_variables.end()), 0UL);
EXPECT_EQ(efi_variables.begin(), efi_variables.end());
}
TEST_F(EfiVariablesStubTest, IterateOverList) {
SetVariables({
{kVariableId[0], kValue[0]},
{kVariableId[1], kValue[1]},
{kVariableId[2], kValue[2]},
{kVariableId[3], kValue[3]},
});
EfiVariables efi_variables;
size_t i = 0;
for (const auto& v_id : efi_variables) {
ASSERT_TRUE(v_id.IsValid());
EXPECT_EQ(v_id.name, efi::String(kVariableId[i].name));
EXPECT_EQ(v_id.vendor_guid, kVariableId[i].vendor_guid);
++i;
}
}
} // unnamed namespace