blob: b26eb145b239bd0502ad0e4645024a17765c51e5 [file] [log] [blame]
// Copyright 2024 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 <fidl/fuchsia.hardware.platform.device/cpp/wire_test_base.h>
#include <fidl/test.metadata/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/driver/fake-bti/cpp/fake-bti.h>
#include <lib/driver/fake-platform-device/cpp/fake-pdev.h>
#include <lib/driver/fake-resource/cpp/fake-resource.h>
#include <lib/driver/mmio/cpp/mmio-buffer.h>
#include <lib/driver/mmio/testing/cpp/test-helper.h>
#include <lib/driver/platform-device/cpp/pdev.h>
#include <zircon/errors.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <zxtest/zxtest.h>
#if FUCHSIA_API_LEVEL_AT_LEAST(HEAD)
constexpr uint32_t kVid = 1;
constexpr uint32_t kPid = 1;
constexpr char kName[] = "test device";
namespace fhpd = fuchsia_hardware_platform_device;
class DeviceServer : public fidl::testing::WireTestBase<fuchsia_hardware_platform_device::Device> {
public:
zx_status_t Connect(fidl::ServerEnd<fhpd::Device> request) {
if (binding_.has_value()) {
return ZX_ERR_ALREADY_BOUND;
}
binding_.emplace(async_get_default_dispatcher(), std::move(request), this,
fidl::kIgnoreBindingClosure);
return ZX_OK;
}
private:
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetBoardInfo(GetBoardInfoCompleter::Sync& completer) override {
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::BoardInfo::Builder(arena)
.vid(kVid)
.pid(kPid)
.Build());
}
std::optional<fidl::ServerBinding<fuchsia_hardware_platform_device::Device>> binding_;
};
class FakePDevWithThread {
public:
zx::result<fidl::ClientEnd<fuchsia_hardware_platform_device::Device>> Start(
fdf_fake::FakePDev::Config config) {
loop_.StartThread("pdev-fidl-thread");
if (zx_status_t status = server.SyncCall(&fdf_fake::FakePDev::SetConfig, std::move(config));
status != ZX_OK) {
return zx::error(status);
}
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_platform_device::Device>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
if (zx_status_t status =
server.SyncCall(&fdf_fake::FakePDev::Connect, std::move(endpoints->server));
status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(endpoints->client));
}
template <typename FidlType>
void AddFidlMetadata(FidlType metadata) {
server.SyncCall([metadata = std::move(metadata)](auto* pdev) {
pdev->AddFidlMetadata(FidlType::kSerializableName, metadata);
});
}
private:
async::Loop loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
async_patterns::TestDispatcherBound<fdf_fake::FakePDev> server{loop_.dispatcher(), std::in_place};
};
TEST(PDevTest, GetMmios) {
constexpr uint32_t kMmioId = 5;
constexpr zx_off_t kMmioOffset = 10;
constexpr size_t kMmioSize = 11;
constexpr std::string_view kMmioName = "test-name";
std::map<uint32_t, fdf_fake::Mmio> mmios;
{
fdf::PDev::MmioInfo mmio{
.offset = kMmioOffset,
.size = kMmioSize,
};
ASSERT_OK(zx::vmo::create(0, 0, &mmio.vmo));
mmios[kMmioId] = std::move(mmio);
}
fdf_fake::FakePDev::ResourceNamesMap mmio_names = {{std::string(kMmioName), kMmioId}};
FakePDevWithThread infra;
zx::result client_channel =
infra.Start({.mmios = std::move(mmios), .mmio_names = std::move(mmio_names)});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
// Verify that an mmio can be retrieved using its ID.
{
zx::result mmio = pdev.GetMmio(kMmioId);
ASSERT_OK(mmio.status_value());
ASSERT_EQ(kMmioOffset, mmio->offset);
ASSERT_EQ(kMmioSize, mmio->size);
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetMmio(4).status_value());
}
// Verify that an mmio can be retrieved using its name.
{
zx::result mmio = pdev.GetMmio(kMmioName);
ASSERT_OK(mmio.status_value());
ASSERT_EQ(kMmioOffset, mmio->offset);
ASSERT_EQ(kMmioSize, mmio->size);
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetMmio("unknown-name").status_value());
}
}
TEST(PDevTest, GetMmioBuffer) {
constexpr uint32_t kMmioId = 5;
constexpr zx_off_t kMmioOffset = 10;
constexpr size_t kMmioSize = 11;
MMIO_PTR void* mmio_vaddr;
zx_koid_t vmo_koid;
zx_info_handle_basic_t info;
std::map<uint32_t, fdf_fake::Mmio> mmios;
{
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(kMmioSize, 0, &vmo));
ASSERT_OK(vmo.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr));
zx::result<fdf::MmioBuffer> mmio =
fdf::MmioBuffer::Create(kMmioOffset, kMmioSize, std::move(vmo), ZX_CACHE_POLICY_UNCACHED);
ASSERT_OK(mmio.status_value());
vmo_koid = info.koid;
mmio_vaddr = mmio->get();
mmios[kMmioId] = std::move(*mmio);
}
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.mmios = std::move(mmios),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::result mmio = pdev.GetMmio(kMmioId);
ASSERT_OK(mmio.status_value());
ASSERT_NE(kMmioOffset, mmio->offset);
ASSERT_EQ(0, mmio->size);
ASSERT_EQ(ZX_HANDLE_INVALID, mmio->vmo);
auto* mmio_buffer = reinterpret_cast<fdf::MmioBuffer*>(mmio->offset);
ASSERT_EQ(kMmioOffset, mmio_buffer->get_offset());
ASSERT_EQ(kMmioSize, mmio_buffer->get_size());
ASSERT_EQ(mmio_vaddr, mmio_buffer->get());
ASSERT_OK(mmio_buffer->get_vmo()->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr,
nullptr));
ASSERT_EQ(vmo_koid, info.koid);
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetMmio(4).status_value());
}
TEST(PDevTest, InvalidMmioHandle) {
constexpr uint32_t kMmioId = 5;
constexpr zx_off_t kMmioOffset = 10;
constexpr size_t kMmioSize = 11;
std::map<uint32_t, fdf_fake::Mmio> mmios;
mmios[kMmioId] = fdf::PDev::MmioInfo{
.offset = kMmioOffset,
.size = kMmioSize,
};
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.mmios = std::move(mmios),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
ASSERT_NOT_OK(pdev.GetMmio(kMmioId).status_value());
}
TEST(PDevTest, GetIrqs) {
constexpr uint32_t kIrqId = 5;
constexpr std::string_view kIrqName = "test-name";
std::map<uint32_t, zx::interrupt> irqs;
{
zx::interrupt irq;
ASSERT_OK(zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq));
irqs[kIrqId] = std::move(irq);
}
fdf_fake::FakePDev::ResourceNamesMap irq_names = {{std::string(kIrqName), kIrqId}};
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.irqs = std::move(irqs),
.irq_names = std::move(irq_names),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
// Verify that an interrupt can be retrieved using its ID.
ASSERT_OK(pdev.GetInterrupt(kIrqId, 0).status_value());
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetInterrupt(4, 0).status_value());
// Verify that an interrupt can be retrieved using its name.
ASSERT_OK(pdev.GetInterrupt(kIrqName, 0).status_value());
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetInterrupt("unknown-name", 0).status_value());
}
TEST(PDevTest, GetBtis) {
constexpr uint32_t kBtiId = 5;
std::map<uint32_t, zx::bti> btis;
{
zx::result bti = fake_bti::CreateFakeBti();
ASSERT_OK(bti);
btis[kBtiId] = std::move(bti.value());
}
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.btis = std::move(btis),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
ASSERT_OK(pdev.GetBti(kBtiId).status_value());
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetBti(4).status_value());
}
TEST(PDevTest, GetSmc) {
constexpr uint32_t kSmcId = 5;
std::map<uint32_t, zx::resource> smcs;
{
zx::resource smc;
ASSERT_OK(fake_root_resource_create(smc.reset_and_get_address()));
smcs[kSmcId] = std::move(smc);
}
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.smcs = std::move(smcs),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::resource smc;
ASSERT_OK(pdev.GetSmc(kSmcId).status_value());
ASSERT_EQ(ZX_ERR_NOT_FOUND, pdev.GetSmc(4).status_value());
}
TEST(PDevTest, GetDeviceInfo) {
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.device_info =
[]() {
fdf::PDev::DeviceInfo device_info{
.vid = kVid,
.pid = kPid,
.name = kName,
};
return device_info;
}(),
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::result device_info = pdev.GetDeviceInfo();
ASSERT_OK(device_info.status_value());
ASSERT_EQ(kPid, device_info->pid);
ASSERT_EQ(kVid, device_info->vid);
ASSERT_STREQ(kName, device_info->name);
}
TEST(PDevTest, GetBoardInfo) {
FakePDevWithThread infra;
zx::result client_channel = infra.Start({
.board_info =
fdf::PDev::BoardInfo{
.vid = kVid,
.pid = kPid,
},
});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::result board_info = pdev.GetBoardInfo();
ASSERT_OK(board_info.status_value());
ASSERT_EQ(kPid, board_info->pid);
ASSERT_EQ(kVid, board_info->vid);
}
TEST(PDevTest, GetPowerConfiguration) {
fuchsia_hardware_power::PowerElement fidl_element{{.name = "test power element", .levels = {{}}}};
fuchsia_hardware_power::PowerElementConfiguration fidl_config{
{.element = std::move(fidl_element), .dependencies = {{}}}};
std::vector<fuchsia_hardware_power::PowerElementConfiguration> fidl_configs{
std::move(fidl_config)};
FakePDevWithThread infra;
zx::result client_channel = infra.Start({.power_elements = std::move(fidl_configs)});
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::result configs = pdev.GetPowerConfiguration();
ASSERT_OK(configs.status_value());
ASSERT_EQ(configs->size(), 1);
const auto& config = configs.value()[0];
ASSERT_EQ(config.element.name, "test power element");
ASSERT_TRUE(config.element.levels.empty());
ASSERT_TRUE(config.dependencies.empty());
}
TEST(PDevTest, GetFidlMetadata) {
static const test_metadata::Metadata kMetadata{{.test_field = "test value"}};
FakePDevWithThread infra;
zx::result client_channel = infra.Start({});
infra.AddFidlMetadata(kMetadata);
ASSERT_OK(client_channel);
fdf::PDev pdev{std::move(client_channel.value())};
zx::result metadata = pdev.GetFidlMetadata<test_metadata::Metadata>();
ASSERT_OK(metadata);
ASSERT_EQ(metadata.value(), kMetadata);
}
#endif // FUCHSIA_API_LEVEL_AT_LEAST(HEAD)