1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <ddk/binding.h>
6 #include <ddk/device.h>
7 #include <ddk/driver.h>
8 #include <ddk/metadata.h>
9
10 #include <zircon/types.h>
11 #include <zircon/process.h>
12 #include <zircon/processargs.h>
13
14 #include <fuchsia/sysinfo/c/fidl.h>
15 #include <zircon/boot/image.h>
16 #include <zircon/syscalls/resource.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <threads.h>
22
23
24 #define ID_HJOBROOT 4
25
26 typedef struct {
27 zx_device_t* zxdev;
28 zx_handle_t job_root;
29 mtx_t lock;
30 char board_name[ZBI_BOARD_NAME_LEN];
31 } sysinfo_t;
32
get_sysinfo_job_root(sysinfo_t * sysinfo)33 static zx_handle_t get_sysinfo_job_root(sysinfo_t* sysinfo) {
34 mtx_lock(&sysinfo->lock);
35 if (sysinfo->job_root == ZX_HANDLE_INVALID) {
36 sysinfo->job_root = zx_take_startup_handle(PA_HND(PA_USER0, ID_HJOBROOT));
37 }
38 mtx_unlock(&sysinfo->lock);
39
40 zx_handle_t h;
41 if ((sysinfo->job_root != ZX_HANDLE_INVALID) &&
42 (zx_handle_duplicate(sysinfo->job_root, ZX_RIGHT_SAME_RIGHTS, &h) == ZX_OK)) {
43 return h;
44 }
45
46 return ZX_HANDLE_INVALID;
47 }
48
fidl_get_root_job(void * ctx,fidl_txn_t * txn)49 static zx_status_t fidl_get_root_job(void* ctx, fidl_txn_t* txn) {
50 sysinfo_t* sysinfo = ctx;
51
52 zx_handle_t h = get_sysinfo_job_root(sysinfo);
53 zx_status_t status = h == ZX_HANDLE_INVALID ? ZX_ERR_NOT_SUPPORTED : ZX_OK;
54
55 return fuchsia_sysinfo_DeviceGetRootJob_reply(txn, status, h);
56 }
57
fidl_get_root_resource(void * ctx,fidl_txn_t * txn)58 static zx_status_t fidl_get_root_resource(void* ctx, fidl_txn_t* txn) {
59 zx_handle_t h = get_root_resource();
60 if (h == ZX_HANDLE_INVALID) {
61 return fuchsia_sysinfo_DeviceGetRootResource_reply(txn, ZX_ERR_NOT_SUPPORTED, h);
62 }
63
64 zx_status_t status = zx_handle_duplicate(h, ZX_RIGHT_TRANSFER, &h);
65 return fuchsia_sysinfo_DeviceGetRootResource_reply(txn, status, h);
66 }
67
fidl_get_hypervisor_resource(void * ctx,fidl_txn_t * txn)68 static zx_status_t fidl_get_hypervisor_resource(void* ctx, fidl_txn_t* txn) {
69 zx_handle_t h;
70 const char name[] = "hypervisor";
71 zx_status_t status = zx_resource_create(get_root_resource(),
72 ZX_RSRC_KIND_HYPERVISOR,
73 0, 0, name, sizeof(name), &h);
74 return fuchsia_sysinfo_DeviceGetHypervisorResource_reply(txn, status, h);
75 }
76
fidl_get_board_name(void * ctx,fidl_txn_t * txn)77 static zx_status_t fidl_get_board_name(void* ctx, fidl_txn_t* txn) {
78 sysinfo_t* sysinfo = ctx;
79
80 zx_status_t status = ZX_OK;
81
82 mtx_lock(&sysinfo->lock);
83 if (sysinfo->board_name[0] == 0) {
84 size_t actual = 0;
85 status = device_get_metadata(sysinfo->zxdev, DEVICE_METADATA_BOARD_NAME,
86 sysinfo->board_name, sizeof(sysinfo->board_name),
87 &actual);
88 }
89 mtx_unlock(&sysinfo->lock);
90
91 size_t board_name_len = strnlen(sysinfo->board_name, sizeof(sysinfo->board_name));
92 return fuchsia_sysinfo_DeviceGetBoardName_reply(txn, status, sysinfo->board_name,
93 board_name_len);
94 }
95
fidl_get_interrupt_controller_info(void * ctx,fidl_txn_t * txn)96 static zx_status_t fidl_get_interrupt_controller_info(void* ctx, fidl_txn_t* txn) {
97 zx_status_t status = ZX_OK;
98 fuchsia_sysinfo_InterruptControllerInfo info = {};
99
100 #if defined(__aarch64__)
101 sysinfo_t* sysinfo = ctx;
102 size_t actual = 0;
103 status = device_get_metadata(sysinfo->zxdev, DEVICE_METADATA_INTERRUPT_CONTROLLER_TYPE,
104 &info.type, sizeof(uint8_t), &actual);
105 #elif defined(__x86_64__)
106 info.type = fuchsia_sysinfo_InterruptControllerType_APIC;
107 #else
108 info.type = fuchsia_sysinfo_InterruptControllerType_UNKNOWN;
109 #endif
110
111 return fuchsia_sysinfo_DeviceGetInterruptControllerInfo_reply(txn, status, &info);
112 }
113
114 static fuchsia_sysinfo_Device_ops_t fidl_ops = {
115 .GetRootJob = fidl_get_root_job,
116 .GetRootResource = fidl_get_root_resource,
117 .GetHypervisorResource = fidl_get_hypervisor_resource,
118 .GetBoardName = fidl_get_board_name,
119 .GetInterruptControllerInfo = fidl_get_interrupt_controller_info,
120 };
121
sysinfo_message(void * ctx,fidl_msg_t * msg,fidl_txn_t * txn)122 static zx_status_t sysinfo_message(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
123 return fuchsia_sysinfo_Device_dispatch(ctx, txn, msg, &fidl_ops);
124 }
125
126 static zx_protocol_device_t sysinfo_ops = {
127 .version = DEVICE_OPS_VERSION,
128 .message = sysinfo_message,
129 };
130
sysinfo_bind(void * ctx,zx_device_t * parent)131 zx_status_t sysinfo_bind(void* ctx, zx_device_t* parent) {
132 sysinfo_t* sysinfo = calloc(1, sizeof(sysinfo_t));
133 if (!sysinfo) {
134 return ZX_ERR_NO_MEMORY;
135 }
136
137 mtx_init(&sysinfo->lock, mtx_plain);
138
139 device_add_args_t args = {
140 .version = DEVICE_ADD_ARGS_VERSION,
141 .name = "sysinfo",
142 .ctx = sysinfo,
143 .ops = &sysinfo_ops,
144 };
145
146 return device_add(parent, &args, &sysinfo->zxdev);
147 }
148
149 static zx_driver_ops_t sysinfo_driver_ops = {
150 .version = DRIVER_OPS_VERSION,
151 .bind = sysinfo_bind,
152 };
153
154 ZIRCON_DRIVER_BEGIN(sysinfo, sysinfo_driver_ops, "zircon", "0.1", 1)
155 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT),
156 ZIRCON_DRIVER_END(sysinfo)
157