blob: 12301fb7e96aa6d28eeb57475985b280e5141b76 [file]
// 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 "virt.h"
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <fidl/fuchsia.hardware.pci/cpp/wire.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/driver/fidl.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h>
#include <fidl/fuchsia.kernel/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_export2.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/fdf/cpp/arena.h>
#include <lib/zx/result.h>
#include <lib/zx/vmo.h>
#include <bind/fuchsia/pci/cpp/bind.h>
#include <fbl/algorithm.h>
#include "pciroot.h"
namespace board_virt {
namespace fpbus = fuchsia_hardware_platform_bus;
namespace {
template <class ResourceMoniker>
zx::result<zx::resource> GetResource(const fdf::Namespace& incoming) {
zx::result result = incoming.Connect<ResourceMoniker>();
if (result.is_error()) {
return result.take_error();
}
fidl::WireResult wire_result = fidl::WireCall(result.value())->Get();
if (!wire_result.ok()) {
return zx::error(wire_result.status());
}
return zx::ok(std::move(wire_result.value().resource));
}
} // namespace
void Virt::SysinfoInit() {
fdf::Arena arena('RISC');
fpbus::BootloaderInfo bootloader_info;
bootloader_info.vendor() = "<unknown>";
{
fidl::Arena fidl_arena;
auto result = pbus_.buffer(arena)->SetBootloaderInfo(fidl::ToWire(fidl_arena, bootloader_info));
if (!result.ok()) {
fdf::error("SetBootloaderInfo request failed: {}", result.FormatDescription().data());
} else if (result->is_error()) {
fdf::error("SetBootloaderInfo failed: {}", zx_status_get_string(result->error_value()));
}
}
}
zx::result<> Virt::PcirootInit(fdf::DriverContext& context, const std::string& node_name) {
zx::result<zx::resource> msi = GetResource<fuchsia_kernel::MsiResource>(context.incoming());
if (msi.is_error()) {
fdf::error("Couldn't obtain MSI resource: {}", msi.status_string());
return msi.take_error();
}
msi_resource_ = *std::move(msi);
zx::result<zx::resource> mmio = GetResource<fuchsia_kernel::MmioResource>(context.incoming());
if (mmio.is_error()) {
fdf::error("Couldn't obtain MMIO resource: {}", mmio.status_string());
return mmio.take_error();
}
mmio_resource_ = *std::move(mmio);
// Note: fuchsia.kernel.IoportResource is only routed on x86 platforms; on RISC-V (as on
// ARM/crosvm), ioport_resource_ remains an unbacked/invalid zx::resource handle.
zx::result<zx::resource> ioport = GetResource<fuchsia_kernel::IoportResource>(context.incoming());
if (ioport.is_ok()) {
ioport_resource_ = *std::move(ioport);
}
zx::result<zx::resource> irq = GetResource<fuchsia_kernel::IrqResource>(context.incoming());
if (irq.is_error()) {
fdf::error("Couldn't obtain IRQ resource: {}", irq.status_string());
return irq.take_error();
}
zx::result<zx::resource> iommu = GetResource<fuchsia_kernel::IommuResource>(context.incoming());
if (iommu.is_error()) {
fdf::error("Couldn't obtain IOMMU resource: {}", iommu.status_string());
return iommu.take_error();
}
pci_root_host_.emplace(msi_resource_.borrow(), mmio_resource_.borrow(), ioport_resource_.borrow(),
PCI_ADDRESS_SPACE_MEMORY);
pci_root_host_->mcfgs().push_back(kVirtPcieMcfg);
if (zx_status_t status = pci_root_host_->Io().AddRegion(kVirtPciePio); status != ZX_OK) {
fdf::error("Failed to add IO region {{ {:#x} - {:#x} }} to the PCI root allocator: {}",
kVirtPciePio.base, kVirtPciePio.base + kVirtPciePio.size,
zx_status_get_string(status));
return zx::error(status);
}
if (zx::result<> result = pci_root_host_->AddMmioRange(kVirtPcieMmio.base, kVirtPcieMmio.size);
result.is_error()) {
fdf::error("Failed to add MMIO region {{ {:#x} - {:#x} }} to PCI root allocator: {}",
kVirtPcieMmio.base, kVirtPcieMmio.base + kVirtPcieMmio.size, result.status_string());
return result.take_error();
}
zx::vmo ecam;
const size_t vmo_size = fbl::round_up<size_t>(kVirtPcieEcam.size, zx_system_get_page_size());
zx_status_t status = zx::vmo::create_physical(mmio_resource_, /*paddr=*/kVirtPcieEcam.base,
/*size=*/vmo_size, /*result=*/&ecam);
if (status != ZX_OK) {
fdf::error("Failed to allocate ecam vmo for [{:#x}, {:#x}): {}", kVirtPcieEcam.base,
kVirtPcieEcam.base + kVirtPcieEcam.size, zx_status_get_string(status));
return zx::error(status);
}
pciroot_.emplace(&*pci_root_host_, std::move(ecam), *std::move(irq), *std::move(iommu));
inspector_ = context.CreateInspector(this, pciroot_->inspect());
if (zx::result<> result = pciroot_->CreateInterrupts(); result.is_error()) {
fdf::error("Failed to create interrupts: {}", result.status_string());
return result.take_error();
}
banjo_server_.emplace(bind_fuchsia_pci::BIND_PROTOCOL_ROOT, &*pciroot_,
pciroot_->pciroot_protocol_ops());
compat::DeviceServer::BanjoConfig banjo_config{
.default_proto_id = bind_fuchsia_pci::BIND_PROTOCOL_ROOT,
};
banjo_config.callbacks[bind_fuchsia_pci::BIND_PROTOCOL_ROOT] = banjo_server_->callback();
auto incoming = std::shared_ptr<fdf::Namespace>(context.take_incoming());
zx::result<> result =
compat_server_.Initialize(incoming, outgoing(), node_name, kPcirootName,
compat::ForwardMetadata::None(), std::move(banjo_config));
if (result.is_error()) {
fdf::error("Failed to initialize compat server: {}", result.status_string());
return result.take_error();
}
std::vector offers = compat_server_.CreateOffers2();
zx::result child = AddChild(kPcirootName, {{banjo_server_->property()}}, offers);
if (child.is_error()) {
fdf::error("Failed to add PCI0 child: {}", child.status_string());
return child.take_error();
}
pciroot_controller_.Bind(std::move(child.value()), dispatcher());
return zx::ok();
}
zx::result<> Virt::Start(fdf::DriverContext context) {
auto node_name = context.node_name().value_or("");
zx::result pbus =
context.incoming().Connect<fuchsia_hardware_platform_bus::Service::PlatformBus>();
if (pbus.is_error()) {
fdf::error("Failed to connect to platform bus: {}", pbus.status_string());
return pbus.take_error();
}
pbus_ = fdf::WireSyncClient<fuchsia_hardware_platform_bus::PlatformBus>(std::move(pbus.value()));
SysinfoInit();
if (zx::result<> result = RtcInit(); result.is_error()) {
fdf::error("Couldn't initialize rtc: {}", result.status_string());
return result.take_error();
}
if (zx::result<> result = PcirootInit(context, node_name); result.is_error()) {
fdf::error("Couldn't initialize pciroot: {}", result.status_string());
return result.take_error();
}
fdf::info("QEMU RISC-V board driver initialized successfully");
return zx::ok();
}
} // namespace board_virt
FUCHSIA_DRIVER_EXPORT2(board_virt::Virt);