blob: 0ab83ac17bb0d72089523d8142b42a9c9a1b108f [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-device.h"
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h>
#include <fidl/fuchsia.hardware.power/cpp/fidl.h>
#include <lib/ddk/metadata.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/fit/function.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/zbi-format/partition.h>
#include <lib/zircon-internal/align.h>
#include <zircon/errors.h>
#include <zircon/syscalls/resource.h>
#include <zircon/system/public/zircon/syscalls-next.h>
#include <algorithm>
#include <unordered_set>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/platform/cpp/bind.h>
#include <bind/fuchsia/resource/cpp/bind.h>
#include <fbl/algorithm.h>
#include "src/devices/bus/drivers/platform/node-util.h"
#include "src/devices/bus/drivers/platform/platform-bus.h"
#include "src/devices/bus/drivers/platform/platform-interrupt.h"
namespace fdf {
using namespace fuchsia_driver_framework;
}
namespace {
fuchsia_boot_metadata::SerialNumberMetadata CreateSerialNumberMetadata(
std::span<const uint8_t> bytes) {
std::string serial_number{bytes.begin(), bytes.end()};
return fuchsia_boot_metadata::SerialNumberMetadata{{.serial_number{std::move(serial_number)}}};
}
zx::result<fuchsia_boot_metadata::PartitionMap> CreatePartitionMap(std::span<const uint8_t> bytes) {
if (bytes.size() < sizeof(zbi_partition_map_t)) {
fdf::error("Incorrect number of bytes: Expected at least {} bytes but actual is {} bytes",
sizeof(zbi_partition_map_t), bytes.size());
return zx::error(ZX_ERR_INVALID_ARGS);
}
const auto& src_map = *reinterpret_cast<const zbi_partition_map_t*>(bytes.data());
fuchsia_boot_metadata::PartitionMap dst_map{{.block_count = src_map.block_count,
.block_size = src_map.block_size,
.reserved = src_map.reserved,
.guid{{0}}}};
std::ranges::copy(src_map.guid, dst_map.guid().value().begin());
auto partitions_byte_count = (bytes.size() - sizeof(zbi_partition_map_t));
auto expected_partitions_byte_count = src_map.partition_count * sizeof(zbi_partition_t);
if (partitions_byte_count != expected_partitions_byte_count) {
fdf::error("Incorrect number of bytes: Expected {} bytes but actual is {} bytes",
sizeof(zbi_partition_map_t) + expected_partitions_byte_count,
sizeof(zbi_partition_map_t) + partitions_byte_count);
return zx::error(ZX_ERR_INVALID_ARGS);
}
const auto* first_partition =
reinterpret_cast<const zbi_partition_t*>(bytes.data() + sizeof(zbi_partition_map_t));
std::span<const zbi_partition_t> src_partitions{first_partition, src_map.partition_count};
auto dst_partitions = src_partitions | std::views::transform([](const zbi_partition_t& src) {
fuchsia_boot_metadata::Partition dst{{.type_guid{{0}},
.unique_guid{{0}},
.first_block = src.first_block,
.last_block = src.last_block,
.flags = src.flags,
.name{src.name}}};
std::ranges::copy(src.type_guid, dst.type_guid().begin());
std::ranges::copy(src.uniq_guid, dst.unique_guid().begin());
return dst;
});
dst_map.partitions().emplace(dst_partitions.begin(), dst_partitions.end());
return zx::ok(std::move(dst_map));
}
zx::result<fuchsia_boot_metadata::MacAddressMetadata> CreateMacAddressMetadata(
std::span<const uint8_t> bytes) {
fuchsia_net::MacAddress mac_address;
if (bytes.size() != mac_address.octets().size()) {
fdf::error("Size of encoded MAC address is incorrect: expected {} bytes but actual is {} bytes",
mac_address.octets().size(), bytes.size());
return zx::error(ZX_ERR_INVALID_ARGS);
}
std::ranges::copy(bytes.begin(), bytes.end(), mac_address.octets().begin());
return zx::ok(fuchsia_boot_metadata::MacAddressMetadata{{.mac_address = std::move(mac_address)}});
}
} // namespace
namespace platform_bus {
namespace fpbus = fuchsia_hardware_platform_bus;
zx::result<std::unique_ptr<PlatformDevice>> PlatformDevice::Create(
fpbus::Node node, PlatformBus* bus, inspect::ComponentInspector& inspector) {
auto inspect_node_name = node.name().value() + "-platform-device";
auto dev = std::make_unique<platform_bus::PlatformDevice>(
bus, inspector.root().CreateChild(inspect_node_name), std::move(node));
zx::result result = dev->Init();
if (result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(dev));
}
fpromise::promise<inspect::Inspector> PlatformDevice::InspectNodeCallback() const {
inspect::Inspector inspector;
auto interrupt_vectors =
inspector.GetRoot().CreateUintArray("interrupt_vectors", interrupt_vectors_.size());
size_t i = 0;
for (const auto& vector : interrupt_vectors_) {
interrupt_vectors.Set(i++, vector);
}
inspector.emplace(std::move(interrupt_vectors));
return fpromise::make_result_promise(fpromise::ok(std::move(inspector)));
}
PlatformDevice::PlatformDevice(PlatformBus* bus, inspect::Node inspect_node, fpbus::Node node)
: bus_(bus),
name_(node.name().value()),
vid_(node.vid().value_or(0)),
pid_(node.pid().value_or(0)),
did_(node.did().value_or(0)),
instance_id_(node.instance_id().value_or(0)),
node_(std::move(node)),
inspect_node_(std::move(inspect_node)) {}
zx::result<PlatformDevice::Mmio> PlatformDevice::GetMmio(uint32_t index) const {
if (node_.mmio() == std::nullopt || index >= node_.mmio()->size()) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
const auto& mmio = node_.mmio().value()[index];
if (unlikely(!IsValid(mmio))) {
return zx::error(ZX_ERR_INTERNAL);
}
if (mmio.base() == std::nullopt) {
return zx::error(ZX_ERR_NOT_FOUND);
}
const zx_paddr_t vmo_base = fbl::round_down(mmio.base().value(), zx_system_get_page_size());
const size_t vmo_size = fbl::round_up(mmio.base().value() + mmio.length().value() - vmo_base,
zx_system_get_page_size());
zx::vmo vmo;
zx_status_t status = zx::vmo::create_physical(*bus_->GetMmioResource(), vmo_base, vmo_size, &vmo);
if (status != ZX_OK) {
fdf::error("creating vmo failed {}", zx_status_get_string(status));
return zx::error(status);
}
char name[32]{};
std::format_to_n(name, sizeof(name) - 1, "mmio {}", index);
status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name));
if (status != ZX_OK) {
fdf::error("setting vmo name failed {}", zx_status_get_string(status));
return zx::error(status);
}
return zx::ok(Mmio{
.offset = mmio.base().value() - vmo_base,
.size = mmio.length().value(),
.vmo = vmo.release(),
});
}
zx::result<zx::interrupt> PlatformDevice::GetInterrupt(uint32_t index, uint32_t flags) {
if (node_.irq() == std::nullopt || index >= node_.irq()->size()) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
const auto& irq = node_.irq().value()[index];
if (unlikely(!IsValid(irq))) {
return zx::error(ZX_ERR_INTERNAL);
}
// If the driver chose "default" for the IRQ mode, use the configuration we have instead.
const uint32_t cfg_mode = static_cast<uint32_t>(irq.mode().value()) & ZX_INTERRUPT_MODE_MASK;
const uint32_t drv_mode = flags & ZX_INTERRUPT_MODE_MASK;
const auto vector = irq.irq().value();
if (drv_mode == ZX_INTERRUPT_MODE_DEFAULT) {
fdf::info(
"IRQ vector {} for platform device \"{}\" has default mode {:#08x}. "
"Using config mode {:08x} instead.",
vector, name_, drv_mode, cfg_mode);
flags = (flags & ~ZX_INTERRUPT_MODE_MASK) | cfg_mode;
} else {
// If the driver explicitly passed in a mode value which conflicts with our
// mode value, print a warning, but use their value anyway.
if (drv_mode != cfg_mode) {
fdf::warn(
"IRQ vector {} for platform device \"{}\" explicitly requesting mode {:#08x} which"
" does not match platform configuration {:#08x}",
vector, name_, drv_mode, cfg_mode);
}
}
if (flags & ZX_INTERRUPT_WAKE_VECTOR) {
fdf::warn("Client passing in ZX_INTERRUPT_WAKE_VECTOR. This will be an error in the future.");
}
if (bus_->suspend_enabled() && irq.wake_vector().has_value() && irq.wake_vector().value()) {
flags |= ZX_INTERRUPT_WAKE_VECTOR;
}
fdf::info("Creating interrupt with vector {} (flags {:#08x}) for platform device \"{}\"", vector,
flags, name_);
zx::interrupt out_irq;
zx_status_t status = zx::interrupt::create(*bus_->GetIrqResource(), vector, flags, &out_irq);
if (status != ZX_OK) {
fdf::error("zx_interrupt_create failed {}", zx_status_get_string(status));
return zx::error(status);
}
interrupt_vectors_.insert(vector);
zx_info_handle_basic_t info;
status = out_irq.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
ZX_ASSERT(status == ZX_OK);
interrupt_koids_.insert(info.koid);
return zx::ok(std::move(out_irq));
}
zx::result<zx::bti> PlatformDevice::GetBti(uint32_t index) const {
if (node_.bti() == std::nullopt || index >= node_.bti()->size()) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
const auto& bti = node_.bti().value()[index];
if (unlikely(!IsValid(bti))) {
return zx::error(ZX_ERR_INTERNAL);
}
return bus_->GetBti(bti.iommu_index().value(), bti.bti_id().value(), name());
}
zx::result<zx::resource> PlatformDevice::GetSmc(uint32_t index) const {
if (node_.smc() == std::nullopt || index >= node_.smc()->size()) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
const auto& smc = node_.smc().value()[index];
if (unlikely(!IsValid(smc))) {
return zx::error(ZX_ERR_INTERNAL);
}
uint32_t options = ZX_RSRC_KIND_SMC;
if (smc.exclusive().value()) {
options |= ZX_RSRC_FLAG_EXCLUSIVE;
}
char rsrc_name[ZX_MAX_NAME_LEN] = {};
std::format_to_n(rsrc_name, sizeof(rsrc_name) - 1, "{}.pbus[{}]", name_, index);
zx::resource out_resource;
zx_status_t status =
zx::resource::create(*bus_->GetSmcResource(), options, smc.service_call_num_base().value(),
smc.count().value(), rsrc_name, sizeof(rsrc_name), &out_resource);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(out_resource));
}
PlatformDevice::DeviceInfo PlatformDevice::GetDeviceInfo() const {
return DeviceInfo{
.vid = vid_,
.pid = pid_,
.did = did_,
.mmio_count = static_cast<uint32_t>(node_.mmio().has_value() ? node_.mmio()->size() : 0),
.irq_count = static_cast<uint32_t>(node_.irq().has_value() ? node_.irq()->size() : 0),
.bti_count = static_cast<uint32_t>(node_.bti().has_value() ? node_.bti()->size() : 0),
.smc_count = static_cast<uint32_t>(node_.smc().has_value() ? node_.smc()->size() : 0),
.metadata_count =
static_cast<uint32_t>(node_.metadata().has_value() ? node_.metadata()->size() : 0),
.reserved = {},
.name = name_,
};
}
PlatformDevice::BoardInfo PlatformDevice::GetBoardInfo() const {
auto info = bus_->board_info();
return BoardInfo{
.vid = info.vid(),
.pid = info.pid(),
.board_revision = info.board_revision(),
.board_name = info.board_name(),
};
}
zx::result<> PlatformDevice::CreateNode() {
// TODO(b/340283894): Remove.
static const std::unordered_set<std::string> kLegacyNameAllowlist{
"aml-thermal-pll", // 05:05:a,05:03:a,05:04:a
"thermistor", // 03:0a:27
"pll-temp-sensor", // 05:06:39
};
std::optional<fdf::DeviceAddress> address;
auto bus_type = fdf::BusType::kPlatform;
std::string name;
if (vid_ == PDEV_VID_GENERIC && pid_ == PDEV_PID_GENERIC && did_ == PDEV_DID_KPCI) {
name = "pci";
address = fdf::DeviceAddress::WithStringValue("pci");
} else if (did_ == PDEV_DID_DEVICETREE_NODE) {
name = name_;
bus_type = fdf::BusType::kDeviceTree;
address = fdf::DeviceAddress::WithStringValue(name_);
} else {
// TODO(b/340283894): Remove legacy name format once `kLegacyNameAllowlist` is removed.
if (kLegacyNameAllowlist.find(name_) != kLegacyNameAllowlist.end()) {
if (instance_id_ == 0) {
// For backwards compatibility, we elide instance id when it is 0.
name = std::format("{:02x}_{:02x}_{:01x}", vid_, pid_, did_);
address = fdf::DeviceAddress::WithArrayIntValue(
{static_cast<uint8_t>(vid_), static_cast<uint8_t>(pid_), static_cast<uint8_t>(did_)});
} else {
name = std::format("{:02x}_{:02x}_{:01x}_{:01x}", vid_, pid_, did_, instance_id_);
address = fdf::DeviceAddress::WithArrayIntValue(
{static_cast<uint8_t>(vid_), static_cast<uint8_t>(pid_), static_cast<uint8_t>(did_),
static_cast<uint8_t>(instance_id_)});
}
} else {
name = name_;
address = fdf::DeviceAddress::WithStringValue(name_);
}
}
auto bus_info = fdf::BusInfo{{
.bus = bus_type,
.address = address,
.address_stability = fdf::DeviceAddressStability::kStable,
}};
std::vector props{
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_),
fdf::MakeProperty2(bind_fuchsia::PROTOCOL, bind_fuchsia_platform::BIND_PROTOCOL_DEVICE)};
if (const auto& node_props = node_.properties(); node_props.has_value()) {
std::copy(node_props->cbegin(), node_props->cend(), std::back_inserter(props));
}
auto add_props = [&props](const auto& resource, const std::string& count_key,
const char* resource_key_prefix) {
const uint32_t count = resource.has_value() ? static_cast<uint32_t>(resource->size()) : 0u;
props.emplace_back(fdf::MakeProperty2(count_key, count));
for (uint32_t i = 0; i < count; i++) {
const auto& name = resource.value()[i].name();
const std::string key = resource_key_prefix + std::to_string(i);
const std::string value = name.has_value() ? name.value() : "unknown";
props.emplace_back(fdf::MakeProperty2(key, value));
}
};
add_props(node_.mmio(), bind_fuchsia_resource::MMIO_COUNT, "fuchsia.resource.MMIO_");
add_props(node_.irq(), bind_fuchsia_resource::INTERRUPT_COUNT, "fuchsia.resource.INTERRUPT_");
add_props(node_.bti(), bind_fuchsia_resource::BTI_COUNT, "fuchsia.resource.BTI_");
add_props(node_.smc(), bind_fuchsia_resource::SMC_COUNT, "fuchsia.resource.SMC_");
std::vector offers = {
fdf::MakeOffer2<fuchsia_hardware_platform_device::Service>(name_),
};
if (device_server_) {
offers.push_back(fdf::MakeOffer2<fuchsia_driver_compat::Service>(name_));
}
if (serial_number_metadata_server_) {
offers.push_back(serial_number_metadata_server_->MakeOffer());
if (zx::result result =
serial_number_metadata_server_->Serve(*bus()->outgoing(), bus()->dispatcher());
result.is_error()) {
fdf::error("Failed to serve serial number metadata server: {}", result);
return result.take_error();
}
}
if (partition_map_metadata_server_) {
offers.push_back(partition_map_metadata_server_->MakeOffer());
if (zx::result result =
partition_map_metadata_server_->Serve(*bus()->outgoing(), bus()->dispatcher());
result.is_error()) {
fdf::error("Failed to serve partition map metadata server: {}", result);
return result.take_error();
}
}
if (mac_address_metadata_server_) {
offers.push_back(mac_address_metadata_server_->MakeOffer());
if (zx::result result =
mac_address_metadata_server_->Serve(*bus()->outgoing(), bus()->dispatcher());
result.is_error()) {
fdf::error("Failed to serve mac address metadata server: {}", result);
return result.take_error();
}
}
// Add our offers to the outgoing directory
{
zx::result result = bus()->outgoing()->AddService<fuchsia_hardware_platform_device::Service>(
fuchsia_hardware_platform_device::Service::InstanceHandler({
.device = device_bindings_.CreateHandler(
this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
}),
name_);
if (result.is_error()) {
fdf::warn("Failed to add platform device service: {}", result);
return result.take_error();
}
}
auto [client, server] = fidl::Endpoints<fuchsia_driver_framework::NodeController>::Create();
fuchsia_driver_framework::NodeAddArgs args{{
.name = {name},
.offers2 = std::move(offers),
.bus_info = std::move(bus_info),
.properties2 = std::move(props),
}};
auto parent = bus()->platform_node();
fidl::Result result = fidl::Call(parent)->AddChild({std::move(args), std::move(server), {}});
if (result.is_error()) {
fdf::error("Failed to add child {}. Error: {}", name, result.error_value().FormatDescription());
return zx::error(result.error_value().is_framework_error()
? result.error_value().framework_error().status()
: ZX_ERR_INTERNAL);
}
node_controller_.Bind(std::move(client), bus()->dispatcher());
node_controller_->WaitForDriver().Then(
[this](fidl::Result<fuchsia_driver_framework::NodeController::WaitForDriver>& result) {
if (result.is_error()) {
fdf::warn("Failed to wait for driver start for {}: {}", name_,
result.error_value().FormatDescription());
return;
}
switch (result.value().Which()) {
case fuchsia_driver_framework::DriverResult::Tag::kDriverStartedNodeToken: {
fdf::debug("platform device {} had driver start.", name_);
node_token_ = result.value().driver_started_node_token().take();
break;
}
case fuchsia_driver_framework::DriverResult::Tag::kMatchError: {
fdf::info("platform device {} did not match a driver/composite.", name_);
break;
}
case fuchsia_driver_framework::DriverResult::Tag::kStartError: {
fdf::warn("platform device {} failed to start.", name_);
break;
}
default: {
fdf::error("platform device {} unrecognized driver result type.", name_);
}
}
});
return zx::ok();
}
zx::result<> PlatformDevice::Init() {
if (node_.irq().has_value()) {
for (uint32_t i = 0; i < node_.irq()->size(); i++) {
auto fragment = std::make_unique<PlatformInterruptFragment>(
this, i, fdf::Dispatcher::GetCurrent()->async_dispatcher());
auto name = std::format("{}-irq{:03}", name_, i);
zx::result result = fragment->Add(name.c_str(), this, node_.irq().value()[i]);
if (result.is_error()) {
fdf::warn("Failed to create interrupt fragment {}", i);
continue;
}
fragments_.push_back(std::move(fragment));
}
}
const size_t metadata_count = node_.metadata() == std::nullopt ? 0 : node_.metadata()->size();
const size_t boot_metadata_count =
node_.boot_metadata() == std::nullopt ? 0 : node_.boot_metadata()->size();
if (metadata_count > 0 || boot_metadata_count > 0) {
device_server_ = std::make_unique<compat::DeviceServer>();
device_server_->Initialize(name_);
device_server_->Serve(bus()->dispatcher(), bus()->outgoing().get());
}
inspect_node_.RecordLazyValues("interrupt_vectors",
fit::bind_member<&PlatformDevice::InspectNodeCallback>(this));
for (size_t i = 0; i < metadata_count; i++) {
const auto& metadata = node_.metadata().value()[i];
if (!IsValid(metadata)) {
fdf::info("Metadata at index {} is invalid", i);
return zx::error(ZX_ERR_INTERNAL);
}
auto metadata_id = metadata.id();
ZX_ASSERT(metadata_id.has_value());
auto metadata_data = metadata.data();
ZX_ASSERT(metadata_data.has_value());
// TODO(b/341981272): Remove `device_server.AddMetadata()` once all drivers bound to platform
// devices do not use `device_get_metadata()` to retrieve metadata. They should be using
// fuchsia.hardware.platform.device/Device::GetMetadata().
errno = 0;
char* metadata_id_end{};
const char* metadata_id_start = metadata_id.value().c_str();
auto metadata_type =
static_cast<uint32_t>(std::strtol(metadata_id_start, &metadata_id_end, 10));
if (!metadata_id.value().empty() && errno == 0 && *metadata_id_end == '\0') {
zx_status_t status =
device_server_->AddMetadata(metadata_type, metadata_data->data(), metadata_data->size());
if (status != ZX_OK) {
fdf::info("Failed to add metadata with ID {}: {}", metadata_id.value().c_str(),
zx_status_get_string(status));
return zx::error(status);
}
}
metadata_.emplace(metadata_id.value(), metadata_data.value());
}
for (size_t i = 0; i < boot_metadata_count; i++) {
const auto& metadata = node_.boot_metadata().value()[i];
if (!IsValid(metadata)) {
fdf::info("Boot metadata at index {} is invalid", i);
return zx::error(ZX_ERR_INTERNAL);
}
auto metadata_zbi_type = metadata.zbi_type();
ZX_ASSERT(metadata_zbi_type.has_value());
zx::result data =
bus_->GetBootItemArray(metadata_zbi_type.value(), metadata.zbi_extra().value());
if (data.is_ok()) {
// TODO(b/341981272): Remove `device_server_.AddMetadata()` once all drivers bound to platform
// devices do not use `device_get_metadata()` to retrieve metadata.
zx_status_t status =
device_server_->AddMetadata(metadata_zbi_type.value(), data->data(), data->size());
if (status != ZX_OK) {
fdf::warn("Failed to add boot metadata with ZBI type {}: {}", metadata_zbi_type.value(),
zx_status_get_string(status));
}
metadata_.emplace(std::to_string(metadata_zbi_type.value()),
std::vector<uint8_t>{data->begin(), data->end()});
switch (metadata_zbi_type.value()) {
case ZBI_TYPE_SERIAL_NUMBER: {
auto metadata = CreateSerialNumberMetadata(data.value());
serial_number_metadata_server_ = std::make_unique<
fdf_metadata::MetadataServer<fuchsia_boot_metadata::SerialNumberMetadata>>(name_);
if (zx::result result = serial_number_metadata_server_->SetMetadata(metadata);
result.is_error()) {
fdf::error("Failed to set metadata for serial number metadata server: {}", result);
return result.take_error();
}
break;
}
case ZBI_TYPE_DRV_PARTITION_MAP: {
zx::result metadata = CreatePartitionMap(data.value());
if (metadata.is_error()) {
fdf::error("Failed to create partition map metadata: {}", metadata);
return metadata.take_error();
}
partition_map_metadata_server_ =
std::make_unique<fdf_metadata::MetadataServer<fuchsia_boot_metadata::PartitionMap>>(
name_);
if (zx::result result = partition_map_metadata_server_->SetMetadata(metadata.value());
result.is_error()) {
fdf::error("Failed to set metadata for partition map metadata server: {}", result);
return result.take_error();
}
break;
}
case ZBI_TYPE_DRV_MAC_ADDRESS: {
zx::result metadata = CreateMacAddressMetadata(data.value());
if (metadata.is_error()) {
fdf::error("Failed to create mac address metadata: {}", metadata);
return metadata.take_error();
}
mac_address_metadata_server_ = std::make_unique<
fdf_metadata::MetadataServer<fuchsia_boot_metadata::MacAddressMetadata>>(name_);
if (zx::result result = mac_address_metadata_server_->SetMetadata(metadata.value());
result.is_error()) {
fdf::error("Failed to set metadata for mac address metadata server: {}", result);
return result.take_error();
}
break;
}
default:
fdf::info("Ignoring boot metadata with zbi type {}", metadata_zbi_type.value());
break;
}
}
}
return zx::ok();
}
void PlatformDevice::GetMmioById(GetMmioByIdRequestView request,
GetMmioByIdCompleter::Sync& completer) {
zx::result mmio_result = GetMmio(request->index);
if (mmio_result.is_error()) {
completer.ReplyError(mmio_result.status_value());
return;
}
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::Mmio::Builder(arena)
.offset(mmio_result->offset)
.size(mmio_result->size)
.vmo(zx::vmo(mmio_result->vmo))
.Build());
}
void PlatformDevice::GetMmioByName(GetMmioByNameRequestView request,
GetMmioByNameCompleter::Sync& completer) {
if (request->name.empty()) {
return completer.ReplyError(ZX_ERR_INVALID_ARGS);
}
std::optional<uint32_t> index = GetMmioIndex(node_, request->name.get());
if (!index.has_value()) {
return completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
}
zx::result mmio_result = GetMmio(index.value());
if (mmio_result.is_error()) {
completer.ReplyError(mmio_result.status_value());
return;
}
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::Mmio::Builder(arena)
.offset(mmio_result->offset)
.size(mmio_result->size)
.vmo(zx::vmo(mmio_result->vmo))
.Build());
}
void PlatformDevice::GetInterruptById(GetInterruptByIdRequestView request,
GetInterruptByIdCompleter::Sync& completer) {
zx::result interrupt = GetInterrupt(request->index, request->flags);
if (interrupt.is_ok()) {
completer.ReplySuccess(std::move(interrupt.value()));
} else {
completer.ReplyError(interrupt.status_value());
}
}
void PlatformDevice::GetInterruptByName(GetInterruptByNameRequestView request,
GetInterruptByNameCompleter::Sync& completer) {
if (request->name.empty()) {
return completer.ReplyError(ZX_ERR_INVALID_ARGS);
}
std::optional<uint32_t> index = GetIrqIndex(node_, request->name.get());
if (!index.has_value()) {
return completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
}
zx::result interrupt = GetInterrupt(index.value(), request->flags);
if (interrupt.is_ok()) {
completer.ReplySuccess(std::move(interrupt.value()));
} else {
completer.ReplyError(interrupt.status_value());
}
}
void PlatformDevice::GetBtiById(GetBtiByIdRequestView request,
GetBtiByIdCompleter::Sync& completer) {
zx::result bti = GetBti(request->index);
if (bti.is_ok()) {
completer.ReplySuccess(std::move(bti.value()));
} else {
completer.ReplyError(bti.status_value());
}
}
void PlatformDevice::GetBtiByName(GetBtiByNameRequestView request,
GetBtiByNameCompleter::Sync& completer) {
if (request->name.empty()) {
return completer.ReplyError(ZX_ERR_INVALID_ARGS);
}
std::optional<uint32_t> index = GetBtiIndex(node_, request->name.get());
if (!index.has_value()) {
return completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
}
zx::result bti = GetBti(index.value());
if (bti.is_ok()) {
completer.ReplySuccess(std::move(bti.value()));
} else {
completer.ReplyError(bti.status_value());
}
}
void PlatformDevice::GetSmcById(GetSmcByIdRequestView request,
GetSmcByIdCompleter::Sync& completer) {
zx::result smc = GetSmc(request->index);
if (smc.is_ok()) {
completer.ReplySuccess(std::move(smc.value()));
} else {
completer.ReplyError(smc.status_value());
}
}
void PlatformDevice::GetSmcByName(GetSmcByNameRequestView request,
GetSmcByNameCompleter::Sync& completer) {
if (request->name.empty()) {
return completer.ReplyError(ZX_ERR_INVALID_ARGS);
}
std::optional<uint32_t> index = GetSmcIndex(node_, request->name.get());
if (!index.has_value()) {
return completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
}
zx::result smc = GetSmc(index.value());
if (smc.is_ok()) {
completer.ReplySuccess(std::move(smc.value()));
} else {
completer.ReplyError(smc.status_value());
}
}
void PlatformDevice::GetPowerConfiguration(GetPowerConfigurationCompleter::Sync& completer) {
std::optional<std::vector<fuchsia_hardware_power::PowerElementConfiguration>> config =
node_.power_config();
if (config.has_value()) {
auto element_configs = config.value();
fidl::Arena arena;
fidl::VectorView<fuchsia_hardware_power::wire::PowerElementConfiguration> elements;
elements.Allocate(arena, element_configs.size());
size_t offset = 0;
for (auto& config : element_configs) {
fuchsia_hardware_power::wire::PowerElementConfiguration wire_config =
fidl::ToWire(arena, config);
elements.at(offset) = wire_config;
offset++;
}
completer.ReplySuccess(elements);
} else {
completer.ReplyError(ZX_ERR_NOT_FOUND);
}
}
void PlatformDevice::GetNodeDeviceInfo(GetNodeDeviceInfoCompleter::Sync& completer) {
DeviceInfo device_info = GetDeviceInfo();
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::NodeDeviceInfo::Builder(arena)
.vid(device_info.vid)
.pid(device_info.pid)
.did(device_info.did)
.mmio_count(device_info.mmio_count)
.irq_count(device_info.irq_count)
.bti_count(device_info.bti_count)
.smc_count(device_info.smc_count)
.metadata_count(device_info.metadata_count)
.name(device_info.name)
.Build());
}
void PlatformDevice::GetBoardInfo(GetBoardInfoCompleter::Sync& completer) {
BoardInfo board_info = GetBoardInfo();
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::BoardInfo::Builder(arena)
.vid(board_info.vid)
.pid(board_info.pid)
.board_name(board_info.board_name)
.board_revision(board_info.board_revision)
.Build());
}
void PlatformDevice::GetMetadata(GetMetadataRequestView request,
GetMetadataCompleter::Sync& completer) {
if (auto metadata = metadata_.find(request->id.get()); metadata != metadata_.end()) {
completer.ReplySuccess(fidl::VectorView<uint8_t>::FromExternal(metadata->second));
return;
}
completer.ReplyError(ZX_ERR_NOT_FOUND);
}
void PlatformDevice::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_platform_device::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
fdf::warn("PlatformDevice received unknown method with ordinal: {}", metadata.method_ordinal);
}
} // namespace platform_bus