| // 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 <assert.h> |
| #include <fuchsia/boot/llcpp/fidl.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <zircon/boot/driver-config.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls/iommu.h> |
| |
| #include <algorithm> |
| |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/metadata.h> |
| #include <ddk/platform-defs.h> |
| #include <ddktl/fidl.h> |
| #include <fbl/algorithm.h> |
| #include <fbl/auto_lock.h> |
| |
| #include "src/devices/bus/drivers/platform/cpu-trace.h" |
| #include "src/devices/bus/drivers/platform/platform-bus-bind.h" |
| |
| namespace platform_bus { |
| |
| zx_status_t PlatformBus::IommuGetBti(uint32_t iommu_index, uint32_t bti_id, zx::bti* out_bti) { |
| if (iommu_index != 0) { |
| return 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 status; |
| } |
| auto [iter, _] = cached_btis_.emplace(key, std::move(new_bti)); |
| bti = iter; |
| } |
| |
| return bti->second.duplicate(ZX_RIGHT_SAME_RIGHTS, out_bti); |
| } |
| |
| zx_status_t PlatformBus::PBusRegisterProtocol(uint32_t proto_id, const void* protocol, |
| size_t protocol_size) { |
| if (!protocol || protocol_size < sizeof(ddk::AnyProtocol)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| switch (proto_id) { |
| // DO NOT ADD ANY MORE PROTOCOLS HERE. |
| // SYSMEM is needed for the x86 board driver and GPIO_IMPL is needed for board driver |
| // pinmuxing. IOMMU is for potential future use. CLOCK_IMPL and POWER_IMPL are needed by the |
| // mt8167s board driver. Use of this mechanism for all other protocols has been deprecated. |
| case ZX_PROTOCOL_CLOCK_IMPL: { |
| clock_ = ddk::ClockImplProtocolClient(static_cast<const clock_impl_protocol_t*>(protocol)); |
| break; |
| } |
| case ZX_PROTOCOL_GPIO_IMPL: { |
| gpio_ = ddk::GpioImplProtocolClient(static_cast<const gpio_impl_protocol_t*>(protocol)); |
| break; |
| } |
| case ZX_PROTOCOL_IOMMU: { |
| iommu_ = ddk::IommuProtocolClient(static_cast<const iommu_protocol_t*>(protocol)); |
| break; |
| } |
| case ZX_PROTOCOL_POWER_IMPL: { |
| power_ = ddk::PowerImplProtocolClient(static_cast<const power_impl_protocol_t*>(protocol)); |
| break; |
| } |
| case ZX_PROTOCOL_SYSMEM: { |
| sysmem_ = ddk::SysmemProtocolClient(static_cast<const sysmem_protocol_t*>(protocol)); |
| break; |
| } |
| default: |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| fbl::AutoLock lock(&proto_completion_mutex_); |
| sync_completion_signal(&proto_completion_); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusDeviceAdd(const pbus_dev_t* pdev) { |
| if (!pdev->name) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_device_t* parent_dev; |
| if (pdev->vid == PDEV_VID_GENERIC && pdev->pid == PDEV_PID_GENERIC && |
| pdev->did == PDEV_DID_KPCI) { |
| // Add PCI root at top level. |
| parent_dev = parent(); |
| } else { |
| parent_dev = zxdev(); |
| } |
| |
| std::unique_ptr<platform_bus::PlatformDevice> dev; |
| auto status = PlatformDevice::Create(pdev, parent_dev, this, PlatformDevice::Isolated, &dev); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = dev->Start(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // devmgr is now in charge of the device. |
| __UNUSED auto* dummy = dev.release(); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusProtocolDeviceAdd(uint32_t proto_id, const pbus_dev_t* pdev) { |
| if (!pdev->name) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| std::unique_ptr<platform_bus::PlatformDevice> dev; |
| auto status = PlatformDevice::Create(pdev, zxdev(), this, PlatformDevice::Protocol, &dev); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = dev->Start(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // devmgr is now in charge of the device. |
| __UNUSED auto* dummy = dev.release(); |
| |
| // Wait for protocol implementation driver to register its protocol. |
| ddk::AnyProtocol dummy_proto; |
| |
| proto_completion_mutex_.Acquire(); |
| while (DdkGetProtocol(proto_id, &dummy_proto) == ZX_ERR_NOT_SUPPORTED) { |
| sync_completion_reset(&proto_completion_); |
| proto_completion_mutex_.Release(); |
| zx_status_t status = sync_completion_wait(&proto_completion_, ZX_SEC(10)); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s sync_completion_wait(protocol %08x) failed: %d", __FUNCTION__, proto_id, |
| status); |
| return status; |
| } |
| proto_completion_mutex_.Acquire(); |
| } |
| proto_completion_mutex_.Release(); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) { |
| DdkTransaction transaction(txn); |
| llcpp::fuchsia::sysinfo::SysInfo::Dispatch(this, msg, &transaction); |
| return transaction.Status(); |
| } |
| |
| void PlatformBus::GetBoardName(GetBoardNameCompleter::Sync& completer) { |
| fbl::AutoLock lock(&board_info_lock_); |
| // Reply immediately if board_name is valid. |
| if (board_info_.board_name[0]) { |
| completer.Reply(ZX_OK, |
| fidl::StringView(board_info_.board_name, strlen(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) { |
| fbl::AutoLock lock(&board_info_lock_); |
| completer.Reply(ZX_OK, board_info_.board_revision); |
| } |
| |
| void PlatformBus::GetBootloaderVendor(GetBootloaderVendorCompleter::Sync& completer) { |
| fbl::AutoLock lock(&bootloader_info_lock_); |
| // Reply immediately if vendor is valid. |
| if (bootloader_info_.vendor[0]) { |
| completer.Reply(ZX_OK, |
| fidl::StringView(bootloader_info_.vendor, strlen(bootloader_info_.vendor))); |
| return; |
| } |
| // Cache the requests until vendor becomes valid. |
| bootloader_vendor_completer_.push_back(completer.ToAsync()); |
| } |
| |
| void PlatformBus::GetInterruptControllerInfo(GetInterruptControllerInfoCompleter::Sync& completer) { |
| ::llcpp::fuchsia::sysinfo::InterruptControllerInfo info = { |
| .type = interrupt_controller_type_, |
| }; |
| completer.Reply(ZX_OK, fidl::unowned_ptr(&info)); |
| } |
| |
| zx_status_t PlatformBus::PBusGetBoardInfo(pdev_board_info_t* out_info) { |
| fbl::AutoLock lock(&board_info_lock_); |
| memcpy(out_info, &board_info_, sizeof(board_info_)); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusSetBoardInfo(const pbus_board_info_t* info) { |
| fbl::AutoLock lock(&board_info_lock_); |
| if (info->board_name[0]) { |
| strlcpy(board_info_.board_name, info->board_name, sizeof(board_info_.board_name)); |
| zxlogf(INFO, "PlatformBus: set board name to \"%s\"", 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(board_info_.board_name, strlen(board_info_.board_name))); |
| completer_tmp_.pop_back(); |
| } |
| } |
| board_info_.board_revision = info->board_revision; |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusSetBootloaderInfo(const pbus_bootloader_info_t* info) { |
| fbl::AutoLock lock(&bootloader_info_lock_); |
| if (info->vendor[0]) { |
| strlcpy(bootloader_info_.vendor, info->vendor, sizeof(bootloader_info_.vendor)); |
| zxlogf(INFO, "PlatformBus: set bootloader vendor to \"%s\"", bootloader_info_.vendor); |
| |
| 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(bootloader_info_.vendor, strlen(bootloader_info_.vendor))); |
| completer_tmp_.pop_back(); |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusRegisterSysSuspendCallback(const pbus_sys_suspend_t* suspend_cbin) { |
| suspend_cb_ = *suspend_cbin; |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformBus::PBusCompositeDeviceAdd(const pbus_dev_t* pdev, |
| const device_fragment_t* fragments_list, |
| size_t fragments_count, |
| uint32_t coresident_device_index) { |
| if (!pdev || !pdev->name) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // Do not allow adding composite devices in our devhost. |
| // The index must be greater than zero to specify one of the other fragments, or UINT32_MAX |
| // to create a new devhost. |
| if (coresident_device_index == 0) { |
| zxlogf(ERROR, "%s: coresident_device_index cannot be zero", __func__); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| std::unique_ptr<platform_bus::PlatformDevice> dev; |
| auto status = PlatformDevice::Create(pdev, zxdev(), this, PlatformDevice::Fragment, &dev); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = dev->Start(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // devmgr is now in charge of the device. |
| __UNUSED auto* dummy = dev.release(); |
| |
| constexpr size_t kMaxFragments = 100; |
| if (fragments_count + 1 > kMaxFragments) { |
| zxlogf(ERROR, "Too many fragments requested."); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| device_fragment_t fragments[kMaxFragments]; |
| memcpy(&fragments[1], fragments_list, fragments_count * sizeof(fragments[1])); |
| |
| constexpr zx_bind_inst_t root_match[] = { |
| BI_MATCH(), |
| }; |
| const zx_bind_inst_t pdev_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, pdev->vid), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, pdev->pid), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, pdev->did), |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_INSTANCE_ID, pdev->instance_id), |
| }; |
| const device_fragment_part_t pdev_fragment[] = { |
| {countof(root_match), root_match}, |
| {countof(pdev_match), pdev_match}, |
| }; |
| |
| fragments[0].name = "ddk.protocol.platform.device.PDev"; |
| fragments[0].parts_count = std::size(pdev_fragment); |
| fragments[0].parts = pdev_fragment; |
| |
| const zx_device_prop_t props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, pdev->vid}, |
| {BIND_PLATFORM_DEV_PID, 0, pdev->pid}, |
| {BIND_PLATFORM_DEV_DID, 0, pdev->did}, |
| {BIND_PLATFORM_DEV_INSTANCE_ID, 0, pdev->instance_id}, |
| }; |
| |
| const composite_device_desc_t comp_desc = { |
| .props = props, |
| .props_count = std::size(props), |
| .fragments = fragments, |
| .fragments_count = fragments_count + 1, |
| .coresident_device_index = coresident_device_index, |
| .metadata_list = nullptr, |
| .metadata_count = 0, |
| }; |
| |
| return DdkAddComposite(pdev->name, &comp_desc); |
| } |
| |
| zx_status_t PlatformBus::DdkGetProtocol(uint32_t proto_id, void* out) { |
| switch (proto_id) { |
| // DO NOT ADD ANY MORE PROTOCOLS HERE. |
| // SYSMEM is needed for the x86 board driver and GPIO_IMPL is needed for board driver |
| // pinmuxing. IOMMU is for potential future use. CLOCK_IMPL and POWER_IMPL are needed by the |
| // mt8167s board driver. Use of this mechanism for all other protocols has been deprecated. |
| case ZX_PROTOCOL_PBUS: { |
| auto proto = static_cast<pbus_protocol_t*>(out); |
| proto->ctx = this; |
| proto->ops = &pbus_protocol_ops_; |
| return ZX_OK; |
| } |
| case ZX_PROTOCOL_CLOCK_IMPL: |
| if (clock_) { |
| clock_->GetProto(static_cast<clock_impl_protocol_t*>(out)); |
| return ZX_OK; |
| } |
| break; |
| case ZX_PROTOCOL_GPIO_IMPL: |
| if (gpio_) { |
| gpio_->GetProto(static_cast<gpio_impl_protocol_t*>(out)); |
| return ZX_OK; |
| } |
| break; |
| case ZX_PROTOCOL_SYSMEM: |
| if (sysmem_) { |
| sysmem_->GetProto(static_cast<sysmem_protocol_t*>(out)); |
| return ZX_OK; |
| } |
| break; |
| case ZX_PROTOCOL_POWER_IMPL: |
| if (power_) { |
| power_->GetProto(static_cast<power_impl_protocol_t*>(out)); |
| return ZX_OK; |
| } |
| break; |
| case ZX_PROTOCOL_IOMMU: |
| if (iommu_) { |
| iommu_->GetProto(static_cast<iommu_protocol_t*>(out)); |
| return ZX_OK; |
| } else { |
| // return default implementation |
| auto proto = static_cast<iommu_protocol_t*>(out); |
| proto->ctx = this; |
| proto->ops = &iommu_protocol_ops_; |
| return ZX_OK; |
| } |
| break; |
| } |
| |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t PlatformBus::GetBootItem(uint32_t type, uint32_t extra, zx::vmo* vmo, |
| uint32_t* length) { |
| auto result = llcpp::fuchsia::boot::Items::Call::Get(zx::unowned(items_svc_), type, extra); |
| if (result.ok()) { |
| *vmo = std::move(result->payload); |
| *length = result->length; |
| } |
| return result.status(); |
| } |
| |
| zx_status_t PlatformBus::GetBootItem(uint32_t type, uint32_t extra, fbl::Array<uint8_t>* out) { |
| zx::vmo vmo; |
| uint32_t length; |
| zx_status_t status = GetBootItem(type, extra, &vmo, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (vmo.is_valid()) { |
| fbl::Array<uint8_t> data(new uint8_t[length], length); |
| status = vmo.read(data.data(), 0, data.size()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| *out = std::move(data); |
| } |
| return ZX_OK; |
| } |
| |
| void PlatformBus::DdkRelease() { delete this; } |
| |
| typedef struct { |
| void* pbus_instance; |
| zx_device_t* sys_root; |
| } sysdev_suspend_t; |
| |
| static void sys_device_suspend(void* ctx, uint8_t requested_state, bool enable_wake, |
| uint8_t suspend_reason) { |
| auto* p = reinterpret_cast<sysdev_suspend_t*>(ctx); |
| auto* pbus = reinterpret_cast<class PlatformBus*>(p->pbus_instance); |
| |
| if (pbus != nullptr) { |
| pbus_sys_suspend_t suspend_cb = pbus->suspend_cb(); |
| if (suspend_cb.callback != nullptr) { |
| uint8_t out_state = 0; |
| zx_status_t status = suspend_cb.callback(suspend_cb.ctx, requested_state, enable_wake, |
| suspend_reason, &out_state); |
| device_suspend_reply(p->sys_root, status, out_state); |
| return; |
| } |
| } |
| device_suspend_reply(p->sys_root, ZX_OK, 0); |
| } |
| |
| static void sys_device_release(void* ctx) { |
| auto* p = reinterpret_cast<sysdev_suspend_t*>(ctx); |
| delete p; |
| } |
| |
| // cpu-trace provides access to the cpu's tracing and performance counters. |
| // As such the "device" is the cpu itself. |
| static void InitCpuTrace(zx_device_t* parent, const zx::iommu& dummy_iommu) { |
| zx::bti cpu_trace_bti; |
| zx_status_t status = zx::bti::create(dummy_iommu, 0, CPU_TRACE_BTI_ID, &cpu_trace_bti); |
| if (status != ZX_OK) { |
| // This is not fatal. |
| zxlogf(ERROR, "platform-bus: error %d in bti_create(cpu_trace_bti)", status); |
| return; |
| } |
| |
| status = publish_cpu_trace(cpu_trace_bti.release(), parent); |
| if (status != ZX_OK) { |
| // This is not fatal. |
| zxlogf(INFO, "publish_cpu_trace returned %d", status); |
| } |
| } |
| |
| static zx_protocol_device_t sys_device_proto = []() { |
| zx_protocol_device_t result = {}; |
| |
| result.version = DEVICE_OPS_VERSION; |
| result.suspend = sys_device_suspend; |
| result.release = sys_device_release; |
| return result; |
| }(); |
| |
| zx_status_t PlatformBus::Create(zx_device_t* parent, const char* name, zx::channel items_svc) { |
| // This creates the "sys" device. |
| sys_device_proto.version = DEVICE_OPS_VERSION; |
| |
| // The suspend op needs to get access to the PBus instance, to be able to |
| // callback the ACPI suspend hook. Introducing a level of indirection here |
| // to allow us to update the PBus instance in the device context after creating |
| // the device. |
| fbl::AllocChecker ac; |
| std::unique_ptr<uint8_t[]> ptr(new (&ac) uint8_t[sizeof(sysdev_suspend_t)]); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| auto* suspend_buf = reinterpret_cast<sysdev_suspend_t*>(ptr.get()); |
| suspend_buf->pbus_instance = nullptr; |
| |
| device_add_args_t args = {}; |
| args.version = DEVICE_ADD_ARGS_VERSION; |
| args.name = "sys"; |
| args.ops = &sys_device_proto; |
| args.flags = DEVICE_ADD_NON_BINDABLE; |
| args.ctx = suspend_buf; |
| |
| // Create /dev/sys. |
| auto status = device_add(parent, &args, &suspend_buf->sys_root); |
| if (status != ZX_OK) { |
| return status; |
| } else { |
| __UNUSED auto* dummy = ptr.release(); |
| } |
| |
| // Add child of sys for the board driver to bind to. |
| std::unique_ptr<platform_bus::PlatformBus> bus( |
| new (&ac) platform_bus::PlatformBus(suspend_buf->sys_root, std::move(items_svc))); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| suspend_buf->pbus_instance = bus.get(); |
| |
| status = bus->Init(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Create /dev/sys/cpu-trace. |
| // But only do so if we have an iommu handle. Normally we do, but tests |
| // may create us without a root resource, and thus without the iommu |
| // handle. |
| if (bus->iommu_handle_.is_valid()) { |
| // Failure is not fatal. Error message already printed. |
| InitCpuTrace(suspend_buf->sys_root, bus->iommu_handle_); |
| } |
| |
| // devmgr is now in charge of the device. |
| __UNUSED auto* dummy = bus.release(); |
| return ZX_OK; |
| } |
| |
| PlatformBus::PlatformBus(zx_device_t* parent, zx::channel items_svc) |
| : PlatformBusType(parent), items_svc_(std::move(items_svc)) { |
| sync_completion_reset(&proto_completion_); |
| } |
| |
| zx_status_t PlatformBus::GetBoardInfo(zbi_board_info_t* board_info) { |
| zx::vmo vmo; |
| uint32_t len; |
| zx_status_t status = GetBootItem(ZBI_TYPE_DRV_BOARD_INFO, 0, &vmo, &len); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Boot Item ZBI_TYPE_DRV_BOARD_INFO not found"); |
| return status; |
| } |
| if (!vmo.is_valid()) { |
| zxlogf(ERROR, "Invalid zbi_board_info_t VMO"); |
| return ZX_ERR_UNAVAILABLE; |
| } |
| status = vmo.read(board_info, 0, std::min<uint64_t>(len, sizeof(*board_info))); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to read zbi_board_info_t VMO"); |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformBus::Init() { |
| zx_status_t status; |
| // 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; |
| // Please do not use get_root_resource() in new code. See fxbug.dev/31358. |
| zx::unowned_resource root_resource(get_root_resource()); |
| if (root_resource->is_valid()) { |
| status = |
| zx::iommu::create(*root_resource, ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc), &iommu_handle_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| |
| // Read kernel driver. |
| zx::vmo vmo; |
| uint32_t length; |
| #if __x86_64__ |
| interrupt_controller_type_ = ::llcpp::fuchsia::sysinfo::InterruptControllerType::APIC; |
| #else |
| status = GetBootItem(ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2, &vmo, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (vmo.is_valid()) { |
| interrupt_controller_type_ = ::llcpp::fuchsia::sysinfo::InterruptControllerType::GIC_V2; |
| } |
| status = GetBootItem(ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V3, &vmo, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (vmo.is_valid()) { |
| interrupt_controller_type_ = ::llcpp::fuchsia::sysinfo::InterruptControllerType::GIC_V3; |
| } |
| #endif |
| |
| // Read platform ID. |
| status = GetBootItem(ZBI_TYPE_PLATFORM_ID, 0, &vmo, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| #if __aarch64__ |
| { |
| // For arm64, we do not expect a board to set the bootloader info. |
| fbl::AutoLock lock(&bootloader_info_lock_); |
| auto vendor = "<unknown>"; |
| strlcpy(bootloader_info_.vendor, vendor, sizeof(vendor)); |
| } |
| #endif |
| |
| fbl::AutoLock lock(&board_info_lock_); |
| if (vmo.is_valid()) { |
| zbi_platform_id_t platform_id; |
| status = vmo.read(&platform_id, 0, sizeof(platform_id)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| zxlogf(INFO, "platform bus: VID: %u PID: %u board: \"%s\"", platform_id.vid, platform_id.pid, |
| platform_id.board_name); |
| board_info_.vid = platform_id.vid; |
| board_info_.pid = platform_id.pid; |
| memcpy(board_info_.board_name, platform_id.board_name, sizeof(board_info_.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 |
| zxlogf(ERROR, "platform_bus: ZBI_TYPE_PLATFORM_ID not found"); |
| return ZX_ERR_INTERNAL; |
| #endif |
| } |
| |
| // Set default board_revision. |
| zbi_board_info_t zbi_board_info = {}; |
| GetBoardInfo(&zbi_board_info); |
| board_info_.board_revision = zbi_board_info.revision; |
| |
| // Then we attach the platform-bus device below it. |
| zx_device_prop_t props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, board_info_.vid}, |
| {BIND_PLATFORM_DEV_PID, 0, board_info_.pid}, |
| }; |
| return DdkAdd(ddk::DeviceAddArgs("platform").set_props(props)); |
| } |
| |
| void PlatformBus::DdkInit(ddk::InitTxn txn) { |
| fbl::Array<uint8_t> board_data; |
| zx_status_t status = GetBootItem(ZBI_TYPE_DRV_BOARD_PRIVATE, 0, &board_data); |
| if (status != ZX_OK) { |
| return txn.Reply(status); |
| } |
| if (board_data) { |
| status = DdkAddMetadata(DEVICE_METADATA_BOARD_PRIVATE, board_data.data(), board_data.size()); |
| if (status != ZX_OK) { |
| return txn.Reply(status); |
| } |
| } |
| return txn.Reply(ZX_OK); // This will make the device visible and able to be unbound. |
| } |
| |
| zx_status_t platform_bus_create(void* ctx, zx_device_t* parent, const char* name, const char* args, |
| zx_handle_t handle) { |
| return platform_bus::PlatformBus::Create(parent, name, zx::channel(handle)); |
| } |
| |
| static constexpr zx_driver_ops_t driver_ops = []() { |
| zx_driver_ops_t ops = {}; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.create = platform_bus_create; |
| return ops; |
| }(); |
| |
| } // namespace platform_bus |
| |
| ZIRCON_DRIVER(platform_bus, platform_bus::driver_ops, "zircon", "0.1"); |