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