| // Copyright 2016 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 <ddk/binding.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/metadata.h> |
| |
| #include <zircon/types.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| |
| #include <fuchsia/sysinfo/c/fidl.h> |
| #include <zircon/boot/image.h> |
| #include <zircon/syscalls/resource.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| |
| |
| #define ID_HJOBROOT 4 |
| |
| typedef struct { |
| zx_device_t* zxdev; |
| zx_handle_t job_root; |
| mtx_t lock; |
| char board_name[ZBI_BOARD_NAME_LEN]; |
| } sysinfo_t; |
| |
| static zx_handle_t get_sysinfo_job_root(sysinfo_t* sysinfo) { |
| mtx_lock(&sysinfo->lock); |
| if (sysinfo->job_root == ZX_HANDLE_INVALID) { |
| sysinfo->job_root = zx_take_startup_handle(PA_HND(PA_USER0, ID_HJOBROOT)); |
| } |
| mtx_unlock(&sysinfo->lock); |
| |
| zx_handle_t h; |
| if ((sysinfo->job_root != ZX_HANDLE_INVALID) && |
| (zx_handle_duplicate(sysinfo->job_root, ZX_RIGHT_SAME_RIGHTS, &h) == ZX_OK)) { |
| return h; |
| } |
| |
| return ZX_HANDLE_INVALID; |
| } |
| |
| static zx_status_t fidl_get_root_job(void* ctx, fidl_txn_t* txn) { |
| sysinfo_t* sysinfo = ctx; |
| |
| zx_handle_t h = get_sysinfo_job_root(sysinfo); |
| zx_status_t status = h == ZX_HANDLE_INVALID ? ZX_ERR_NOT_SUPPORTED : ZX_OK; |
| |
| return fuchsia_sysinfo_DeviceGetRootJob_reply(txn, status, h); |
| } |
| |
| static zx_status_t fidl_get_root_resource(void* ctx, fidl_txn_t* txn) { |
| zx_handle_t h = get_root_resource(); |
| if (h == ZX_HANDLE_INVALID) { |
| return fuchsia_sysinfo_DeviceGetRootResource_reply(txn, ZX_ERR_NOT_SUPPORTED, h); |
| } |
| |
| zx_status_t status = zx_handle_duplicate(h, ZX_RIGHT_TRANSFER, &h); |
| return fuchsia_sysinfo_DeviceGetRootResource_reply(txn, status, h); |
| } |
| |
| static zx_status_t fidl_get_hypervisor_resource(void* ctx, fidl_txn_t* txn) { |
| zx_handle_t h; |
| const char name[] = "hypervisor"; |
| zx_status_t status = zx_resource_create(get_root_resource(), |
| ZX_RSRC_KIND_HYPERVISOR, |
| 0, 0, name, sizeof(name), &h); |
| return fuchsia_sysinfo_DeviceGetHypervisorResource_reply(txn, status, h); |
| } |
| |
| static zx_status_t fidl_get_board_name(void* ctx, fidl_txn_t* txn) { |
| sysinfo_t* sysinfo = ctx; |
| |
| zx_status_t status = ZX_OK; |
| |
| mtx_lock(&sysinfo->lock); |
| if (sysinfo->board_name[0] == 0) { |
| size_t actual = 0; |
| status = device_get_metadata(sysinfo->zxdev, DEVICE_METADATA_BOARD_NAME, |
| sysinfo->board_name, sizeof(sysinfo->board_name), |
| &actual); |
| } |
| mtx_unlock(&sysinfo->lock); |
| |
| size_t board_name_len = strnlen(sysinfo->board_name, sizeof(sysinfo->board_name)); |
| return fuchsia_sysinfo_DeviceGetBoardName_reply(txn, status, sysinfo->board_name, |
| board_name_len); |
| } |
| |
| static zx_status_t fidl_get_interrupt_controller_info(void* ctx, fidl_txn_t* txn) { |
| zx_status_t status = ZX_OK; |
| fuchsia_sysinfo_InterruptControllerInfo info = {}; |
| |
| #if defined(__aarch64__) |
| sysinfo_t* sysinfo = ctx; |
| size_t actual = 0; |
| status = device_get_metadata(sysinfo->zxdev, DEVICE_METADATA_INTERRUPT_CONTROLLER_TYPE, |
| &info.type, sizeof(uint8_t), &actual); |
| #elif defined(__x86_64__) |
| info.type = fuchsia_sysinfo_InterruptControllerType_APIC; |
| #else |
| info.type = fuchsia_sysinfo_InterruptControllerType_UNKNOWN; |
| #endif |
| |
| return fuchsia_sysinfo_DeviceGetInterruptControllerInfo_reply(txn, status, &info); |
| } |
| |
| static fuchsia_sysinfo_Device_ops_t fidl_ops = { |
| .GetRootJob = fidl_get_root_job, |
| .GetRootResource = fidl_get_root_resource, |
| .GetHypervisorResource = fidl_get_hypervisor_resource, |
| .GetBoardName = fidl_get_board_name, |
| .GetInterruptControllerInfo = fidl_get_interrupt_controller_info, |
| }; |
| |
| static zx_status_t sysinfo_message(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) { |
| return fuchsia_sysinfo_Device_dispatch(ctx, txn, msg, &fidl_ops); |
| } |
| |
| static zx_protocol_device_t sysinfo_ops = { |
| .version = DEVICE_OPS_VERSION, |
| .message = sysinfo_message, |
| }; |
| |
| zx_status_t sysinfo_bind(void* ctx, zx_device_t* parent) { |
| sysinfo_t* sysinfo = calloc(1, sizeof(sysinfo_t)); |
| if (!sysinfo) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| mtx_init(&sysinfo->lock, mtx_plain); |
| |
| device_add_args_t args = { |
| .version = DEVICE_ADD_ARGS_VERSION, |
| .name = "sysinfo", |
| .ctx = sysinfo, |
| .ops = &sysinfo_ops, |
| }; |
| |
| return device_add(parent, &args, &sysinfo->zxdev); |
| } |
| |
| static zx_driver_ops_t sysinfo_driver_ops = { |
| .version = DRIVER_OPS_VERSION, |
| .bind = sysinfo_bind, |
| }; |
| |
| ZIRCON_DRIVER_BEGIN(sysinfo, sysinfo_driver_ops, "zircon", "0.1", 1) |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), |
| ZIRCON_DRIVER_END(sysinfo) |