blob: 5575e85570d1f08d90112dd004d4696b838482fb [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/stdcompat/array.h>
#include <lib/zbitl/image.h>
namespace {
using boot_shim::testing::LoadDtb;
using boot_shim::testing::LoadedDtb;
class ArmDevicetreeGicItemTest
: public boot_shim::testing::TestMixin<boot_shim::testing::ArmDevicetreeTest,
boot_shim::testing::SyntheticDevicetreeTest> {
public:
using Mixin::SetUpTestSuite;
using Mixin::TearDownTestSuite;
auto get_mmio_observer() {
return [this](const boot_shim::DevicetreeMmioRange& r) { ranges_.push_back(r); };
}
cpp20::span<const boot_shim::DevicetreeMmioRange> mmio_ranges() const { return ranges_; }
private:
std::vector<boot_shim::DevicetreeMmioRange> ranges_;
};
TEST_F(ArmDevicetreeGicItemTest, ParseQemuGicV2WithMsi) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
{
.address = 0x8010000,
.size = 0x10000,
},
{
.address = 0x8030000,
.size = 0x10000,
},
{
.address = 0x8040000,
.size = 0x10000,
},
{
.address = 0x8020000,
.size = 0x1000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = qemu_arm_gic2();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V2) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v2_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v2_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->msi_frame_phys, 0x08020000);
EXPECT_EQ(dcfg->gicd_offset, 0x00000);
EXPECT_EQ(dcfg->gicc_offset, 0x10000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_TRUE(dcfg->use_msi);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V2 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, GicV2NoMsi) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
{
.address = 0x8010000,
.size = 0x10000,
},
{
.address = 0x8030000,
.size = 0x10000,
},
{
.address = 0x8040000,
.size = 0x10000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = arm_gic2_no_msi();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V2) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v2_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v2_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->msi_frame_phys, 0);
EXPECT_EQ(dcfg->gicd_offset, 0x00000);
EXPECT_EQ(dcfg->gicc_offset, 0x10000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->use_msi);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V2 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, GicV3Uint64Stride) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
// Reflects the GICR base and stride
{
.address = 0x8100000,
.size = 0x20000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = arm_gic3_stride();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 3 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v3_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v3_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->gicd_offset, 0x0);
EXPECT_EQ(dcfg->gicr_offset, 0x0100000);
EXPECT_EQ(dcfg->gicr_stride, 0x20000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V3 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, GicV3FourStride) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
// Reflects the GICR base and aggregate stride (4 regions)
{
.address = 0x8100000,
.size = 0x80000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = arm_gic3_four_stride();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 3 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v3_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v3_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->gicd_offset, 0x0);
EXPECT_EQ(dcfg->gicr_offset, 0x0100000);
EXPECT_EQ(dcfg->gicr_stride, 0x20000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V3 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, GicV3SubsumedStride) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
// Reflects the GICR base which matches the stride
{
.address = 0x8100000,
.size = 0x20000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = arm_gic3_subsumed_stride();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 3 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v3_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v3_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->gicd_offset, 0x0);
EXPECT_EQ(dcfg->gicr_offset, 0x0100000);
EXPECT_EQ(dcfg->gicr_stride, 0x20000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V3 missing.");
}
// We dont support GicV3 with MSI yet, not reflected in the driver configuration.
TEST_F(ArmDevicetreeGicItemTest, ParseQemuGicV3) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x8000000,
.size = 0x10000,
},
{
.address = 0x80a0000,
.size = 0xf60000,
},
});
std::array<std::byte, 256> image_buffer;
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::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v3_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v3_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x08000000);
EXPECT_EQ(dcfg->gicd_offset, 0x00000);
EXPECT_EQ(dcfg->gicr_offset, 0xa0000);
EXPECT_EQ(dcfg->gicr_stride, 0x20000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V3 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, ParseCrosvm) {
constexpr auto kExpectedMmio = cpp20::to_array<boot_shim::DevicetreeMmioRange>({
{
.address = 0x3fff0000,
.size = 0x10000,
},
{
.address = 0x3ffd0000,
.size = 0x20000,
},
});
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = crosvm_arm();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.set_mmio_observer(get_mmio_observer());
ASSERT_TRUE(shim.Init());
boot_shim::testing::CheckMmioRanges(mmio_ranges(), kExpectedMmio);
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v3_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v3_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0x3ffd0000);
EXPECT_EQ(dcfg->gicd_offset, 0x20000);
EXPECT_EQ(dcfg->gicr_offset, 0x00000);
EXPECT_EQ(dcfg->gicr_stride, 0x20000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V3 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, KhadasVim3) {
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = khadas_vim3();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
shim.Init();
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
bool present = false;
for (auto [header, payload] : image) {
if (header->type == ZBI_TYPE_KERNEL_DRIVER && header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V2) {
present = true;
ASSERT_GE(payload.size(), sizeof(zbi_dcfg_arm_gic_v2_driver_t));
auto* dcfg = reinterpret_cast<zbi_dcfg_arm_gic_v2_driver_t*>(payload.data());
EXPECT_EQ(dcfg->mmio_phys, 0xffc01000);
EXPECT_EQ(dcfg->gicd_offset, 0);
EXPECT_EQ(dcfg->gicc_offset, 0x1000);
EXPECT_EQ(dcfg->gich_offset, 0x3000);
EXPECT_EQ(dcfg->gicv_offset, 0x5000);
EXPECT_EQ(dcfg->ipi_base, 0x0);
EXPECT_FALSE(dcfg->optional);
break;
}
}
image.ignore_error();
ASSERT_TRUE(present, "ZBI Driver for GIC V2 missing.");
}
TEST_F(ArmDevicetreeGicItemTest, MissingNode) {
std::array<std::byte, 256> image_buffer;
zbitl::Image<cpp20::span<std::byte>> image(image_buffer);
ASSERT_TRUE(image.clear().is_ok());
auto fdt = empty_fdt();
boot_shim::DevicetreeBootShim<boot_shim::ArmDevicetreeGicItem> shim("test", fdt);
// shim completes successfully even when nothing is matching.
EXPECT_TRUE(shim.Init());
EXPECT_TRUE(shim.AppendItems(image).is_ok());
// Look for a gic 2 driver.
for (auto [header, payload] : image) {
EXPECT_FALSE(header->type == ZBI_TYPE_KERNEL_DRIVER &&
(header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V3 ||
header->extra == ZBI_KERNEL_DRIVER_ARM_GIC_V2));
}
image.ignore_error();
}
} // namespace