blob: 72e21f80f00a31c76aec9dbab08f2287d86edb5f [file] [log] [blame]
// Copyright 2023 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/boot-shim/devicetree-boot-shim.h>
#include <lib/boot-shim/devicetree.h>
#include <lib/boot-shim/testing/devicetree-test-fixture.h>
#include <lib/fit/defer.h>
#include <lib/zbitl/image.h>
namespace {
using boot_shim::testing::LoadDtb;
using boot_shim::testing::LoadedDtb;
class ArmDevicetreeTimerItemTest
: public boot_shim::testing::TestMixin<boot_shim::testing::ArmDevicetreeTest,
boot_shim::testing::SyntheticDevicetreeTest> {
public:
static void SetUpTestSuite() {
Mixin::SetUpTestSuite();
auto loaded_dtb = LoadDtb("arm_timer.dtb");
ASSERT_TRUE(loaded_dtb.is_ok(), "%s", loaded_dtb.error_value().c_str());
timer_ = std::move(loaded_dtb).value();
loaded_dtb = LoadDtb("arm_timer_no_frequency_override.dtb");
ASSERT_TRUE(loaded_dtb.is_ok(), "%s", loaded_dtb.error_value().c_str());
timer_no_frequency_override_ = std::move(loaded_dtb).value();
}
static void TearDownTestSuite() {
timer_ = std::nullopt;
Mixin::TearDownTestSuite();
}
devicetree::Devicetree timer() { return timer_->fdt(); }
devicetree::Devicetree timer_no_frequency_override() {
return timer_no_frequency_override_->fdt();
}
private:
static std::optional<LoadedDtb> timer_;
static std::optional<LoadedDtb> timer_no_frequency_override_;
};
std::optional<LoadedDtb> ArmDevicetreeTimerItemTest::timer_ = std::nullopt;
std::optional<LoadedDtb> ArmDevicetreeTimerItemTest::timer_no_frequency_override_ = std::nullopt;
TEST_F(ArmDevicetreeTimerItemTest, TimerWithFrequencyOverride) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = timer();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
ASSERT_TRUE(shim.Init());
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER) {
ASSERT_GE(payload.size_bytes(), sizeof(zbi_dcfg_arm_generic_timer_driver_t));
auto* timer_item = reinterpret_cast<zbi_dcfg_arm_generic_timer_driver_t*>(payload.data());
EXPECT_EQ(timer_item->freq_override, 1234);
EXPECT_EQ(timer_item->irq_phys, 27);
EXPECT_EQ(timer_item->irq_sphys, 26);
EXPECT_EQ(timer_item->irq_virt, 28);
present = true;
}
}
ASSERT_TRUE(present);
}
TEST_F(ArmDevicetreeTimerItemTest, TimerWithoutFrequencyOverride) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = timer_no_frequency_override();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
ASSERT_TRUE(shim.Init());
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER) {
ASSERT_GE(payload.size_bytes(), sizeof(zbi_dcfg_arm_generic_timer_driver_t));
auto* timer_item = reinterpret_cast<zbi_dcfg_arm_generic_timer_driver_t*>(payload.data());
EXPECT_EQ(timer_item->freq_override, 0);
EXPECT_EQ(timer_item->irq_phys, 27);
EXPECT_EQ(timer_item->irq_sphys, 26);
EXPECT_EQ(timer_item->irq_virt, 28);
present = true;
}
}
ASSERT_TRUE(present);
}
TEST_F(ArmDevicetreeTimerItemTest, Qemu) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = qemu_arm_gic3();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
ASSERT_TRUE(shim.Init());
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER) {
ASSERT_GE(payload.size_bytes(), sizeof(zbi_dcfg_arm_generic_timer_driver_t));
auto* timer_item = reinterpret_cast<zbi_dcfg_arm_generic_timer_driver_t*>(payload.data());
EXPECT_EQ(timer_item->freq_override, 0);
EXPECT_EQ(timer_item->irq_phys, 30);
EXPECT_EQ(timer_item->irq_sphys, 29);
EXPECT_EQ(timer_item->irq_virt, 27);
present = true;
}
}
ASSERT_TRUE(present);
}
TEST_F(ArmDevicetreeTimerItemTest, Crosvm) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = crosvm_arm();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
ASSERT_TRUE(shim.Init());
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER) {
ASSERT_GE(payload.size_bytes(), sizeof(zbi_dcfg_arm_generic_timer_driver_t));
auto* timer_item = reinterpret_cast<zbi_dcfg_arm_generic_timer_driver_t*>(payload.data());
EXPECT_EQ(timer_item->freq_override, 0);
EXPECT_EQ(timer_item->irq_phys, 30);
EXPECT_EQ(timer_item->irq_sphys, 29);
EXPECT_EQ(timer_item->irq_virt, 27);
present = true;
}
}
ASSERT_TRUE(present);
}
TEST_F(ArmDevicetreeTimerItemTest, KhadasVim3) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = khadas_vim3();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
shim.Init();
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER) {
ASSERT_GE(payload.size_bytes(), sizeof(zbi_dcfg_arm_generic_timer_driver_t));
auto* timer_item = reinterpret_cast<zbi_dcfg_arm_generic_timer_driver_t*>(payload.data());
EXPECT_EQ(timer_item->freq_override, 0);
EXPECT_EQ(timer_item->irq_phys, 30);
EXPECT_EQ(timer_item->irq_sphys, 29);
EXPECT_EQ(timer_item->irq_virt, 27);
present = true;
}
}
ASSERT_TRUE(present);
}
TEST_F(ArmDevicetreeTimerItemTest, MissingNode) {
std::array<std::byte, 1024> image_buffer;
std::vector<void*> allocs;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = empty_fdt();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeTimerItem> shim("test", fdt);
ASSERT_TRUE(shim.Init());
auto clear_errors = fit::defer([&]() { image.ignore_error(); });
ASSERT_TRUE(shim.AppendItems(image).is_ok());
for (auto [header, payload] : image) {
EXPECT_FALSE(header->type == ZBI_TYPE_KERNEL_DRIVER &&
header->extra == ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER);
}
}
} // namespace