blob: f96bb81c18f8a957a207d4eec1890f22f087c1e8 [file] [log] [blame]
// Copyright 2017 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 "src/devices/bus/drivers/platform/platform-bus.h"
#include <fidl/fuchsia.kernel/cpp/fidl.h>
#include <fidl/fuchsia.system.state/cpp/fidl.h>
#include <lib/ddk/metadata.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/component/cpp/composite_node_spec.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/driver/outgoing/cpp/handlers.h>
#include <lib/fdf/dispatcher.h>
#include <lib/fidl/cpp/wire/channel.h>
#include <lib/fidl/cpp/wire/object_view.h>
#include <lib/zbi-format/board.h>
#include <lib/zbi-format/driver-config.h>
#include <lib/zbi-format/zbi.h>
#include <zircon/errors.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls/iommu.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/platform/cpp/bind.h>
#include <fbl/algorithm.h>
#include "src/devices/bus/drivers/platform/node-util.h"
#include "src/devices/bus/drivers/platform/platform_bus_config.h"
namespace platform_bus {
namespace {
namespace fhpb = fuchsia_hardware_platform_bus;
namespace fss = fuchsia_system_state;
zx::result<> ValidateResources(fhpb::Node& node) {
if (node.name() == std::nullopt) {
fdf::error("Node has no name?");
return zx::error(ZX_ERR_INVALID_ARGS);
}
std::replace(node.name()->begin(), node.name()->end(), ':', '_');
if (node.mmio() != std::nullopt) {
for (size_t i = 0; i < node.mmio()->size(); i++) {
if (!IsValid(node.mmio().value()[i])) {
fdf::error("node '{}' has invalid mmio {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
if (node.irq() != std::nullopt) {
for (size_t i = 0; i < node.irq()->size(); i++) {
if (!IsValid(node.irq().value()[i])) {
fdf::error("node '{}' has invalid irq {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
if (node.bti() != std::nullopt) {
for (size_t i = 0; i < node.bti()->size(); i++) {
if (!IsValid(node.bti().value()[i])) {
fdf::error("node '{}' has invalid bti {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
if (node.smc() != std::nullopt) {
for (size_t i = 0; i < node.smc()->size(); i++) {
if (!IsValid(node.smc().value()[i])) {
fdf::error("node '{}' has invalid smc {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
if (node.metadata() != std::nullopt) {
for (size_t i = 0; i < node.metadata()->size(); i++) {
if (!IsValid(node.metadata().value()[i])) {
fdf::error("node '{}' has invalid metadata {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
if (node.boot_metadata() != std::nullopt) {
for (size_t i = 0; i < node.boot_metadata()->size(); i++) {
if (!IsValid(node.boot_metadata().value()[i])) {
fdf::error("node '{}' has invalid boot metadata {}", node.name().value(), i);
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
}
return zx::ok();
}
// Adds a passthrough device which forwards all banjo connections to the parent device.
// The device will be added as a child of |parent| with the name |name|, and |props| will
// be applied to the new device's add_args.
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>> AddProtocolPassthrough(
const char* name, cpp20::span<const fuchsia_driver_framework::NodeProperty> props,
platform_bus::PlatformBus* bus, fidl::UnownedClientEnd<fuchsia_driver_framework::Node> parent) {
if (!bus || !name) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
fhpb::Service::InstanceHandler handler({
.platform_bus = bus->bindings().CreateHandler(bus, bus->driver_dispatcher()->get(),
fidl::kIgnoreBindingClosure),
.iommu = bus->iommu_bindings().CreateHandler(bus, bus->driver_dispatcher()->get(),
fidl::kIgnoreBindingClosure),
.firmware = bus->fw_bindings().CreateHandler(bus, bus->driver_dispatcher()->get(),
fidl::kIgnoreBindingClosure),
});
zx::result result = bus->outgoing()->AddService<fhpb::Service>(std::move(handler), name);
if (result.is_error()) {
return result.take_error();
}
std::array offers = {fdf::MakeOffer2<fhpb::Service>(name),
fdf::MakeOffer2<fuchsia_driver_compat::Service>(name)};
return fdf::AddChild(parent, bus->logger(), name, props, offers);
}
// Taken from src/lib/ddk/include/lib/ddk/device.h
#define DEVICE_SUSPEND_REASON_POWEROFF UINT8_C(0x10)
#define DEVICE_SUSPEND_REASON_SUSPEND_RAM UINT8_C(0x20)
#define DEVICE_SUSPEND_REASON_MEXEC UINT8_C(0x30)
#define DEVICE_SUSPEND_REASON_REBOOT UINT8_C(0x40)
#define DEVICE_SUSPEND_REASON_REBOOT_RECOVERY (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x01))
#define DEVICE_SUSPEND_REASON_REBOOT_BOOTLOADER (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x02))
#define DEVICE_SUSPEND_REASON_REBOOT_KERNEL_INITIATED (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x03))
#define DEVICE_SUSPEND_REASON_SELECTIVE_SUSPEND UINT8_C(0x50)
uint8_t PowerStateToSuspendReason(fss::SystemPowerState power_state) {
switch (power_state) {
case fss::SystemPowerState::kReboot:
return DEVICE_SUSPEND_REASON_REBOOT;
case fss::SystemPowerState::kRebootRecovery:
return DEVICE_SUSPEND_REASON_REBOOT_RECOVERY;
case fss::SystemPowerState::kRebootBootloader:
return DEVICE_SUSPEND_REASON_REBOOT_BOOTLOADER;
case fss::SystemPowerState::kMexec:
return DEVICE_SUSPEND_REASON_MEXEC;
case fss::SystemPowerState::kPoweroff:
return DEVICE_SUSPEND_REASON_POWEROFF;
case fss::SystemPowerState::kSuspendRam:
return DEVICE_SUSPEND_REASON_SUSPEND_RAM;
case fss::SystemPowerState::kRebootKernelInitiated:
return DEVICE_SUSPEND_REASON_REBOOT_KERNEL_INITIATED;
default:
return DEVICE_SUSPEND_REASON_SELECTIVE_SUSPEND;
}
}
} // anonymous namespace
zx::result<zx::bti> PlatformBus::GetBti(uint32_t iommu_index, uint32_t bti_id,
std::string_view dev_name) {
if (iommu_index != 0) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
std::pair key(iommu_index, bti_id);
auto bti = cached_btis_.find(key);
if (bti == cached_btis_.end()) {
zx::bti new_bti;
zx_status_t status = zx::bti::create(iommu_handle_, 0, bti_id, &new_bti);
if (status != ZX_OK) {
return zx::error(status);
}
char name[ZX_MAX_NAME_LEN]{};
std::format_to_n(name, sizeof(name) - 1, "{}.pbus[{:02x}:{:02x}]", dev_name, iommu_index,
bti_id);
status = new_bti.set_property(ZX_PROP_NAME, name, std::size(name));
if (status != ZX_OK) {
fdf::warn("Couldn't set name for BTI '{}': {}", std::string_view(name),
zx_status_get_string(status));
}
auto [iter, _] = cached_btis_.emplace(key, std::move(new_bti));
bti = iter;
}
zx::bti out_bti;
zx_status_t status = bti->second.duplicate(ZX_RIGHT_SAME_RIGHTS, &out_bti);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(out_bti));
}
zx::unowned_resource PlatformBus::GetIrqResource() const {
static zx::resource irq_resource;
if (!irq_resource.is_valid()) {
zx::result client = incoming()->Connect<fuchsia_kernel::IrqResource>();
if (client.is_ok()) {
fidl::Result resource = fidl::Call(*client)->Get();
if (resource.is_ok()) {
irq_resource = std::move(resource.value().resource());
}
}
}
return irq_resource.borrow();
}
zx::unowned_resource PlatformBus::GetMmioResource() const {
static zx::resource mmio_resource;
if (!mmio_resource.is_valid()) {
zx::result client = incoming()->Connect<fuchsia_kernel::MmioResource>();
if (client.is_ok()) {
fidl::Result resource = fidl::Call(*client)->Get();
if (resource.is_ok()) {
mmio_resource = std::move(resource.value().resource());
}
}
}
return mmio_resource.borrow();
}
zx::unowned_resource PlatformBus::GetSmcResource() const {
static zx::resource smc_resource;
if (!smc_resource.is_valid()) {
zx::result client = incoming()->Connect<fuchsia_kernel::SmcResource>();
if (client.is_ok()) {
fidl::Result resource = fidl::Call(*client)->Get();
if (resource.is_ok()) {
smc_resource = std::move(resource.value().resource());
}
}
}
return smc_resource.borrow();
}
void PlatformBus::NodeAdd(NodeAddRequestView request, fdf::Arena& arena,
NodeAddCompleter::Sync& completer) {
if (!request->node.has_name()) {
completer.buffer(arena).ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
auto natural = fidl::ToNatural(request->node);
completer.buffer(arena).Reply(NodeAddInternal(natural));
}
zx::result<> PlatformBus::NodeAddInternal(fhpb::Node& node) {
auto result = ValidateResources(node);
if (result.is_error()) {
fdf::error("Failed to validate resources: {}", result);
return result.take_error();
}
zx::result dev = PlatformDevice::Create(std::move(node), this, inspector());
if (dev.is_error()) {
fdf::error("Failed to create platform device: {}", dev);
return dev.take_error();
}
if (zx::result result = dev->CreateNode(); result.is_error()) {
fdf::error("Failed to create platform device node: {}", result);
return result.take_error();
}
devices_.push_back(std::move(dev.value()));
return zx::ok();
}
void PlatformBus::GetBoardName(GetBoardNameCompleter::Sync& completer) {
// Reply immediately if board_name is valid.
if (!board_info_.board_name().empty()) {
completer.Reply(ZX_OK, fidl::StringView::FromExternal(board_info_.board_name()));
return;
}
// Cache the requests until board_name becomes valid.
board_name_completer_.push_back(completer.ToAsync());
}
void PlatformBus::GetBoardRevision(GetBoardRevisionCompleter::Sync& completer) {
completer.Reply(ZX_OK, board_info_.board_revision());
}
void PlatformBus::GetBootloaderVendor(GetBootloaderVendorCompleter::Sync& completer) {
// Reply immediately if vendor is valid.
if (bootloader_info_.vendor() != std::nullopt) {
completer.Reply(ZX_OK, fidl::StringView::FromExternal(bootloader_info_.vendor().value()));
return;
}
// Cache the requests until vendor becomes valid.
bootloader_vendor_completer_.push_back(completer.ToAsync());
}
void PlatformBus::GetInterruptControllerInfo(GetInterruptControllerInfoCompleter::Sync& completer) {
fuchsia_sysinfo::wire::InterruptControllerInfo info = {
.type = interrupt_controller_type_,
};
completer.Reply(
ZX_OK, fidl::ObjectView<fuchsia_sysinfo::wire::InterruptControllerInfo>::FromExternal(&info));
}
void PlatformBus::GetSerialNumber(GetSerialNumberCompleter::Sync& completer) {
auto result = GetBootItem(ZBI_TYPE_SERIAL_NUMBER, {});
if (result.is_error()) {
fdf::info("Boot Item ZBI_TYPE_SERIAL_NUMBER not found");
completer.ReplyError(result.error_value());
return;
}
auto& [vmo, length] = result.value()[0];
if (length > fuchsia_sysinfo::wire::kSerialNumberLen) {
completer.ReplyError(ZX_ERR_BUFFER_TOO_SMALL);
return;
}
char serial[fuchsia_sysinfo::wire::kSerialNumberLen];
zx_status_t status = vmo.read(serial, 0, length);
if (status != ZX_OK) {
fdf::error("Failed to read serial number VMO {}", status);
completer.ReplyError(status);
return;
}
completer.ReplySuccess(fidl::StringView::FromExternal(serial, length));
}
void PlatformBus::GetBoardInfo(fdf::Arena& arena, GetBoardInfoCompleter::Sync& completer) {
fidl::Arena<> fidl_arena;
completer.buffer(arena).ReplySuccess(fidl::ToWire(fidl_arena, board_info_));
}
void PlatformBus::SetBoardInfo(SetBoardInfoRequestView request, fdf::Arena& arena,
SetBoardInfoCompleter::Sync& completer) {
auto& info = request->info;
if (info.has_board_name()) {
board_info_.board_name() = info.board_name().get();
fdf::info("PlatformBus: set board name to \"{}\"", board_info_.board_name());
std::vector<GetBoardNameCompleter::Async> completer_tmp_;
// Respond to pending boardname requests, if any.
board_name_completer_.swap(completer_tmp_);
while (!completer_tmp_.empty()) {
completer_tmp_.back().Reply(ZX_OK, fidl::StringView::FromExternal(board_info_.board_name()));
completer_tmp_.pop_back();
}
}
if (info.has_board_revision()) {
board_info_.board_revision() = info.board_revision();
}
completer.buffer(arena).ReplySuccess();
}
void PlatformBus::SetBootloaderInfo(SetBootloaderInfoRequestView request, fdf::Arena& arena,
SetBootloaderInfoCompleter::Sync& completer) {
auto& info = request->info;
if (info.has_vendor()) {
bootloader_info_.vendor() = info.vendor().get();
fdf::info("PlatformBus: set bootloader vendor to \"{}\"", bootloader_info_.vendor().value());
std::vector<GetBootloaderVendorCompleter::Async> completer_tmp_;
// Respond to pending boardname requests, if any.
bootloader_vendor_completer_.swap(completer_tmp_);
while (!completer_tmp_.empty()) {
completer_tmp_.back().Reply(
ZX_OK, fidl::StringView::FromExternal(bootloader_info_.vendor().value()));
completer_tmp_.pop_back();
}
}
completer.buffer(arena).ReplySuccess();
}
void PlatformBus::RegisterSysSuspendCallback(RegisterSysSuspendCallbackRequestView request,
fdf::Arena& arena,
RegisterSysSuspendCallbackCompleter::Sync& completer) {
suspend_cb_.Bind(std::move(request->suspend_cb),
fdf::Dispatcher::GetCurrent()->async_dispatcher());
completer.buffer(arena).ReplySuccess();
}
void PlatformBus::AddCompositeNodeSpec(AddCompositeNodeSpecRequestView request, fdf::Arena& arena,
AddCompositeNodeSpecCompleter::Sync& completer) {
// Create the pdev fragments
auto vid = request->node.has_vid() ? request->node.vid() : 0;
auto pid = request->node.has_pid() ? request->node.pid() : 0;
auto did = request->node.has_did() ? request->node.did() : 0;
auto instance_id = request->node.has_instance_id() ? request->node.instance_id() : 0;
fuchsia_driver_framework::CompositeNodeSpec composite_node_spec = fidl::ToNatural(request->spec);
if (!composite_node_spec.parents2().has_value()) {
composite_node_spec.parents2().emplace();
}
composite_node_spec.parents2()->push_back(fuchsia_driver_framework::ParentSpec2{{
.bind_rules =
{
fdf::MakeAcceptBindRule2(bind_fuchsia::PROTOCOL,
bind_fuchsia_platform::BIND_PROTOCOL_DEVICE),
fdf::MakeAcceptBindRule2(bind_fuchsia::PLATFORM_DEV_VID, vid),
fdf::MakeAcceptBindRule2(bind_fuchsia::PLATFORM_DEV_PID, pid),
fdf::MakeAcceptBindRule2(bind_fuchsia::PLATFORM_DEV_DID, did),
fdf::MakeAcceptBindRule2(bind_fuchsia::PLATFORM_DEV_INSTANCE_ID, instance_id),
},
.properties =
{
fdf::MakeProperty2(bind_fuchsia::PROTOCOL,
bind_fuchsia_platform::BIND_PROTOCOL_DEVICE),
fdf::MakeProperty2(bind_fuchsia::PLATFORM_DEV_VID, vid),
fdf::MakeProperty2(bind_fuchsia::PLATFORM_DEV_PID, pid),
fdf::MakeProperty2(bind_fuchsia::PLATFORM_DEV_DID, did),
fdf::MakeProperty2(bind_fuchsia::PLATFORM_DEV_INSTANCE_ID, instance_id),
},
}});
zx::result composite_node_manager =
incoming()->Connect<fuchsia_driver_framework::CompositeNodeManager>();
if (composite_node_manager.is_error()) {
fdf::error("Failed to connect to CompositeNodeManager: {}", composite_node_manager);
completer.buffer(arena).ReplyError(composite_node_manager.status_value());
return;
}
fidl::Result result = fidl::Call(*composite_node_manager)->AddSpec(composite_node_spec);
if (result.is_error()) {
fdf::error("Failed to add composite node spec {}", result.error_value().FormatDescription());
if (result.error_value().is_framework_error()) {
completer.buffer(arena).ReplyError(result.error_value().framework_error().status());
} else {
completer.buffer(arena).ReplyError(ZX_ERR_INTERNAL);
}
return;
}
// Create a platform device for the node.
auto natural = fidl::ToNatural(request->node);
auto valid = ValidateResources(natural);
if (valid.is_error()) {
fdf::error("Failed to validate resources: {}", valid);
completer.buffer(arena).ReplyError(valid.error_value());
return;
}
zx::result dev = PlatformDevice::Create(std::move(natural), this, inspector());
if (dev.is_error()) {
fdf::error("Failed to create platform device: {}", dev);
completer.buffer(arena).ReplyError(dev.status_value());
return;
}
if (zx::result result = dev->CreateNode(); result.is_error()) {
fdf::error("Failed to create platform device node: {}", result);
completer.buffer(arena).ReplyError(result.status_value());
return;
}
devices_.push_back(std::move(dev.value()));
completer.buffer(arena).ReplySuccess();
}
void PlatformBus::GetBti(GetBtiRequestView request, fdf::Arena& arena,
GetBtiCompleter::Sync& completer) {
zx::result bti = GetBti(request->iommu_index, request->bti_id, "");
if (bti.is_error()) {
completer.buffer(arena).ReplyError(bti.status_value());
return;
}
completer.buffer(arena).ReplySuccess(std::move(bti.value()));
}
void PlatformBus::GetFirmware(GetFirmwareRequestView request, fdf::Arena& arena,
GetFirmwareCompleter::Sync& completer) {
uint32_t type = 0;
switch (request->type) {
case fhpb::wire::FirmwareType::kDeviceTree:
type = ZBI_TYPE_DEVICETREE;
break;
case fhpb::wire::FirmwareType::kAcpi:
type = ZBI_TYPE_ACPI_RSDP;
break;
case fhpb::wire::FirmwareType::kSmbios:
type = ZBI_TYPE_SMBIOS;
break;
default:
completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED);
return;
}
zx::result result = GetBootItem(type, {});
if (result.is_error()) {
fdf::warn("Platform GetBootItem failed {}", result);
completer.buffer(arena).ReplyError(result.status_value());
return;
}
fidl::VectorView<fhpb::wire::FirmwareBlob> ret(arena, result->size());
for (size_t i = 0; i < result->size(); i++) {
auto& [vmo, length] = result.value()[i];
ret[i] = fhpb::wire::FirmwareBlob{
.vmo = std::move(vmo),
.length = length,
};
}
completer.buffer(arena).ReplySuccess(ret);
}
void PlatformBus::GetInterruptInfo(GetInterruptInfoRequest& request,
GetInterruptInfoCompleter::Sync& completer) {
auto iter = std::find_if(devices_.begin(), devices_.end(), [&request](auto& device) {
if (const auto& vector = request.interrupt_vector(); vector.has_value()) {
return device->HasInterruptVector(vector.value());
}
if (const auto& koid = request.interrupt_koid(); koid.has_value()) {
return device->HasInterruptKoid(koid.value());
}
return false;
});
if (iter != devices_.end()) {
auto* device = iter->get();
completer.Reply(zx::ok(
fhpb::InterruptAttributorGetInterruptInfoResponse(device->name(), device->node_token())));
} else {
completer.Reply(zx::error(ZX_ERR_NOT_FOUND));
}
}
void PlatformBus::handle_unknown_method(fidl::UnknownMethodMetadata<fhpb::PlatformBus> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
fdf::warn("PlatformBus received unknown method with ordinal: {}", metadata.method_ordinal);
}
zx::result<std::vector<PlatformBus::BootItemResult>> PlatformBus::GetBootItem(
uint32_t type, std::optional<uint32_t> extra) {
fidl::Arena arena;
fidl::ObjectView<fuchsia_boot::wire::Extra> extra_struct;
if (extra.has_value()) {
extra_struct = fidl::ObjectView<fuchsia_boot::wire::Extra>(arena, extra.value());
};
auto result = fidl::WireCall(items_svc_)->Get2(type, extra_struct);
if (!result.ok()) {
return zx::error(result.status());
}
if (result->is_error()) {
if (result->error_value() == ZX_ERR_NOT_SUPPORTED) {
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::error(result->error_value());
}
fidl::VectorView items = result->value()->retrieved_items;
if (items.size() == 0) {
return zx::error(ZX_ERR_NOT_FOUND);
}
std::vector<PlatformBus::BootItemResult> ret;
ret.reserve(items.size());
for (size_t i = 0; i < items.size(); i++) {
ret.emplace_back(PlatformBus::BootItemResult{
.vmo = std::move(items[i].payload),
.length = items[i].length,
});
}
return zx::ok(std::move(ret));
}
zx::result<fbl::Array<uint8_t>> PlatformBus::GetBootItemArray(uint32_t type,
std::optional<uint32_t> extra) {
zx::result result = GetBootItem(type, extra);
if (result.is_error()) {
return result.take_error();
}
if (result->size() > 1) {
fdf::warn("Found multiple boot items of type: {}", type);
}
auto& [vmo, length] = result.value()[0];
fbl::Array<uint8_t> data(new uint8_t[length], length);
zx_status_t status = vmo.read(data.data(), 0, data.size());
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(data));
}
PlatformBus::PlatformBus(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase("platform-bus", std::move(start_args), std::move(driver_dispatcher)) {}
zx::result<> PlatformBus::Start() {
zx::result sys = AddOwnedChild("sys");
if (sys.is_error()) {
fdf::error("Failed to create sys with error {}", sys);
return sys.take_error();
}
sys_node_ = std::move(sys.value());
zx::result items_svc = incoming()->Connect<fuchsia_boot::Items>();
if (items_svc.is_error()) {
fdf::error("Failed to get connect to boot items {}", items_svc);
return items_svc.take_error();
}
items_svc_ = std::move(items_svc.value());
// Set up a dummy IOMMU protocol to use in the case where our board driver
// does not set a real one.
zx_iommu_desc_dummy_t desc;
zx::result iommu_client = incoming()->Connect<fuchsia_kernel::IommuResource>();
if (iommu_client.is_ok()) {
auto result = fidl::Call(iommu_client.value())->Get();
if (result.is_ok()) {
zx_status_t status = zx::iommu::create(result->resource(), ZX_IOMMU_TYPE_DUMMY, &desc,
sizeof(desc), &iommu_handle_);
if (status != ZX_OK) {
fdf::error("Failed to get get create iommu {}", zx_status_get_string(status));
return zx::error(status);
}
}
}
// Read kernel driver.
#if __x86_64__
interrupt_controller_type_ = fuchsia_sysinfo::wire::InterruptControllerType::kApic;
#else
std::array<std::pair<zbi_kernel_driver_t, fuchsia_sysinfo::wire::InterruptControllerType>, 3>
interrupt_driver_type_mapping = {
{{ZBI_KERNEL_DRIVER_ARM_GIC_V2, fuchsia_sysinfo::wire::InterruptControllerType::kGicV2},
{ZBI_KERNEL_DRIVER_ARM_GIC_V3, fuchsia_sysinfo::wire::InterruptControllerType::kGicV3},
{ZBI_KERNEL_DRIVER_RISCV_PLIC, fuchsia_sysinfo::wire::InterruptControllerType::kPlic}},
};
for (const auto& [driver, controller] : interrupt_driver_type_mapping) {
auto boot_item = GetBootItem(ZBI_TYPE_KERNEL_DRIVER, driver);
if (boot_item.is_error() && boot_item.status_value() != ZX_ERR_NOT_FOUND) {
return boot_item.take_error();
}
if (boot_item.is_ok()) {
interrupt_controller_type_ = controller;
}
}
#endif
// Read platform ID.
zx::result platform_id_result = GetBootItem(ZBI_TYPE_PLATFORM_ID, {});
if (platform_id_result.is_error() && platform_id_result.status_value() != ZX_ERR_NOT_FOUND) {
return platform_id_result.take_error();
}
#if __aarch64__
{
// For arm64, we do not expect a board to set the bootloader info.
bootloader_info_.vendor() = "<unknown>";
}
#endif
if (platform_id_result.is_ok()) {
if (platform_id_result.value()[0].length != sizeof(zbi_platform_id_t)) {
return zx::error(ZX_ERR_INTERNAL);
}
zbi_platform_id_t platform_id;
zx_status_t status =
platform_id_result.value()[0].vmo.read(&platform_id, 0, sizeof(platform_id));
if (status != ZX_OK) {
return zx::error(status);
}
fdf::info("VID: {} PID: {} board: \"{}\"", platform_id.vid, platform_id.pid,
std::string_view(platform_id.board_name));
board_info_.vid() = platform_id.vid;
board_info_.pid() = platform_id.pid;
board_info_.board_name() = platform_id.board_name;
} else {
#if __x86_64__
// For x64, we might not find the ZBI_TYPE_PLATFORM_ID, old bootloaders
// won't support this, for example. If this is the case, cons up the VID/PID
// here to allow the acpi board driver to load and bind.
board_info_.vid() = PDEV_VID_INTEL;
board_info_.pid() = PDEV_PID_X86;
#else
fdf::error("ZBI_TYPE_PLATFORM_ID not found");
return zx::error(ZX_ERR_INTERNAL);
#endif
}
// Set default board_revision.
zx::result zbi_board_info = GetBoardInfo();
if (zbi_board_info.is_ok()) {
board_info_.board_revision() = zbi_board_info->revision;
}
// Then we attach the platform-bus device below it.
zx::result platform_node = fdf::AddOwnedChild(sys_node_.node_, logger(), "platform");
if (platform_node.is_error()) {
return platform_node.take_error();
}
platform_node_ = std::move(platform_node.value());
std::array passthrough_props = {
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_VID, board_info_.vid()),
fdf::MakeProperty(bind_fuchsia::PLATFORM_DEV_PID, board_info_.pid()),
};
device_server_.Initialize("pt");
device_server_.Serve(dispatcher(), outgoing().get());
zx::result board_data = GetBootItemArray(ZBI_TYPE_DRV_BOARD_PRIVATE, {});
if (board_data.is_error() && board_data.status_value() != ZX_ERR_NOT_FOUND) {
return board_data.take_error();
}
if (board_data.is_ok()) {
zx_status_t status = device_server_.AddMetadata(DEVICE_METADATA_BOARD_PRIVATE,
board_data->data(), board_data->size());
if (status != ZX_OK) {
return zx::error(status);
}
}
zx::result pt_node =
AddProtocolPassthrough("pt", passthrough_props, this, platform_node_.node_.borrow());
if (pt_node.is_error()) {
// We log the error but we do nothing as we've already added the device successfully.
fdf::error("Error while adding pt: {}", pt_node);
} else {
pt_node_ = std::move(pt_node.value());
}
auto config = take_config<platform_bus_config::Config>();
if (config.software_device_ids().size() != config.software_device_names().size()) {
fdf::error(
"Invalid config. software_device_ids and software_device_names must have same length");
return zx::error(ZX_ERR_INVALID_ARGS);
}
for (size_t i = 0; i < config.software_device_ids().size(); i++) {
fhpb::Node device = {};
device.name() = config.software_device_names()[i];
device.vid() = PDEV_VID_GENERIC;
device.pid() = PDEV_PID_GENERIC;
device.did() = config.software_device_ids()[i];
if (zx::result result = NodeAddInternal(device); result.is_error()) {
return result.take_error();
}
}
suspend_enabled_ = config.suspend_enabled();
zx::result result = outgoing()->AddService<fhpb::ObservabilityService>(
fhpb::ObservabilityService::InstanceHandler({
.interrupt =
interrupt_bindings().CreateHandler(this, dispatcher(), fidl::kIgnoreBindingClosure),
}));
ZX_ASSERT(result.is_ok());
result =
outgoing()->AddService<fuchsia_sysinfo::Service>(fuchsia_sysinfo::Service::InstanceHandler({
.device =
sysinfo_bindings().CreateHandler(this, dispatcher(), fidl::kIgnoreBindingClosure),
}));
ZX_ASSERT(result.is_ok());
return zx::ok();
}
void PlatformBus::PrepareStop(fdf::PrepareStopCompleter completer) {
if (suspend_cb().is_valid()) {
auto client = incoming()->Connect<fss::SystemStateTransition>();
if (client.is_error()) {
fdf::error("Failed to connect to fuchsia.system.state/SystemStateTransition: {}", client);
completer(client.take_error());
return;
}
fidl::Result state = fidl::Call(*client)->GetTerminationSystemState();
if (state.is_error()) {
fdf::error("Failed to connect to get termination state: {}", state.error_value());
completer(zx::error(state.error_value().status()));
return;
}
suspend_cb()
->Callback(false, PowerStateToSuspendReason(state->state()))
.ThenExactlyOnce([completer = std::move(completer)](
fidl::WireUnownedResult<fhpb::SysSuspend::Callback>& status) mutable {
if (!status.ok()) {
completer(zx::error(status.status()));
return;
}
if (status->out_status != ZX_OK) {
completer(zx::error(status->out_status));
return;
}
completer(zx::ok());
});
return;
}
completer(zx::ok());
}
zx::result<zbi_board_info_t> PlatformBus::GetBoardInfo() {
zx::result result = GetBootItem(ZBI_TYPE_DRV_BOARD_INFO, {});
if (result.is_error()) {
// This is expected on some boards.
fdf::info("Boot Item ZBI_TYPE_DRV_BOARD_INFO not found");
return result.take_error();
}
auto& [vmo, length] = result.value()[0];
if (length != sizeof(zbi_board_info_t)) {
return zx::error(ZX_ERR_INTERNAL);
}
zbi_board_info_t board_info;
zx_status_t status = vmo.read(&board_info, 0, length);
if (status != ZX_OK) {
fdf::error("Failed to read zbi_board_info_t VMO: {}", zx_status_get_string(status));
return zx::error(status);
}
return zx::ok(board_info);
}
} // namespace platform_bus
FUCHSIA_DRIVER_EXPORT(platform_bus::PlatformBus);