1 // Copyright 2017 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 "platform-proxy.h"
6
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <threads.h>
12
13 #include <ddk/binding.h>
14 #include <ddk/debug.h>
15 #include <fbl/algorithm.h>
16 #include <fbl/auto_lock.h>
17
18 #include <utility>
19
20 #include "platform-proxy-client.h"
21 #include "platform-proxy-device.h"
22
23 namespace platform_bus {
24
DdkRelease()25 void PlatformProxy::DdkRelease() {
26 // Delete ourselves if the devmgr held the last reference to us.
27 if (Release()) {
28 delete this;
29 }
30 }
31
Rpc(uint32_t device_id,const platform_proxy_req_t * req,size_t req_length,platform_proxy_rsp_t * resp,size_t resp_length,const zx_handle_t * in_handles,size_t in_handle_count,zx_handle_t * out_handles,size_t out_handle_count,size_t * out_actual)32 zx_status_t PlatformProxy::Rpc(uint32_t device_id, const platform_proxy_req_t* req,
33 size_t req_length, platform_proxy_rsp_t* resp,
34 size_t resp_length, const zx_handle_t* in_handles,
35 size_t in_handle_count, zx_handle_t* out_handles,
36 size_t out_handle_count, size_t* out_actual) {
37 uint32_t resp_size, handle_count;
38
39 // We require the client to pass us the device_id and we set here as a precaution
40 // against the code above forgetting to set it.
41 const_cast<platform_proxy_req_t*>(req)->device_id = device_id;
42
43 zx_channel_call_args_t args = {
44 .wr_bytes = req,
45 .wr_handles = in_handles,
46 .rd_bytes = resp,
47 .rd_handles = out_handles,
48 .wr_num_bytes = static_cast<uint32_t>(req_length),
49 .wr_num_handles = static_cast<uint32_t>(in_handle_count),
50 .rd_num_bytes = static_cast<uint32_t>(resp_length),
51 .rd_num_handles = static_cast<uint32_t>(out_handle_count),
52 };
53 auto status = rpc_channel_.call(0, zx::time::infinite(), &args, &resp_size, &handle_count);
54 if (status != ZX_OK) {
55 return status;
56 }
57
58 status = resp->status;
59
60 if (status == ZX_OK && resp_size < sizeof(*resp)) {
61 zxlogf(ERROR, "PlatformProxy::Rpc resp_size too short: %u\n", resp_size);
62 status = ZX_ERR_INTERNAL;
63 goto fail;
64 } else if (status == ZX_OK && handle_count != out_handle_count) {
65 zxlogf(ERROR, "PlatformProxy::Rpc handle count %u expected %zu\n", handle_count,
66 out_handle_count);
67 status = ZX_ERR_INTERNAL;
68 goto fail;
69 }
70
71 if (out_actual) {
72 *out_actual = resp_size;
73 }
74
75 fail:
76 if (status != ZX_OK) {
77 for (uint32_t i = 0; i < handle_count; i++) {
78 zx_handle_close(out_handles[i]);
79 }
80 }
81 return status;
82 }
83
GetProtocol(uint32_t proto_id,void * out)84 zx_status_t PlatformProxy::GetProtocol(uint32_t proto_id, void* out) {
85 auto protocol = protocols_.find(proto_id);
86 if (protocol.IsValid()) {
87 protocol->GetProtocol(out);
88 return ZX_OK;
89 }
90 return ZX_ERR_NOT_SUPPORTED;
91 }
92
RegisterProtocol(uint32_t proto_id,const void * protocol)93 zx_status_t PlatformProxy::RegisterProtocol(uint32_t proto_id, const void* protocol) {
94
95 if (protocols_.find(proto_id).IsValid()) {
96 zxlogf(ERROR, "%s: protocol %08x has already been registered\n", __func__, proto_id);
97 return ZX_ERR_BAD_STATE;
98 }
99
100 fbl::AllocChecker ac;
101 auto proto = fbl::make_unique_checked<PlatformProtocol>(&ac, proto_id,
102 static_cast<const ddk::AnyProtocol*>(protocol));
103 if (!ac.check()) {
104 return ZX_ERR_NO_MEMORY;
105 }
106
107 protocols_.insert(std::move(proto));
108
109 if (protocols_.size() == protocol_count_) {
110 // All the protocols are registered, so we can now add the actual platform device.
111 rpc_pdev_req_t req = {};
112 rpc_pdev_rsp_t resp = {};
113 req.header.device_id = ROOT_DEVICE_ID;
114 req.header.proto_id = ZX_PROTOCOL_PDEV;
115 req.header.op = PDEV_GET_DEVICE_INFO;
116
117 auto status = Rpc(ROOT_DEVICE_ID, &req.header, sizeof(req), &resp.header, sizeof(resp));
118 if (status != ZX_OK) {
119 zxlogf(ERROR, "%s: PDEV_GET_DEVICE_INFO failed %d\n", __func__, status);
120 return status;
121 }
122 const pdev_device_info_t& info = resp.device_info;
123
124 zx_device_prop_t props[] = {
125 {BIND_PLATFORM_DEV_VID, 0, info.vid},
126 {BIND_PLATFORM_DEV_PID, 0, info.pid},
127 {BIND_PLATFORM_DEV_DID, 0, info.did},
128 };
129
130 device_add_args_t args = {};
131 args.version = DEVICE_ADD_ARGS_VERSION;
132 args.name = info.name;
133 args.proto_id = ZX_PROTOCOL_PDEV;
134 args.props = props;
135 args.prop_count = fbl::count_of(props);
136
137 status = ProxyDevice::CreateChild(zxdev(), ROOT_DEVICE_ID, fbl::RefPtr<PlatformProxy>(this),
138 &args, nullptr);
139 if (status != ZX_OK) {
140 zxlogf(ERROR, "%s: ProxyDevice::Create failed %d\n", __func__, status);
141 return status;
142 }
143 }
144
145 return ZX_OK;
146 }
147
UnregisterProtocol(uint32_t proto_id)148 void PlatformProxy::UnregisterProtocol(uint32_t proto_id) {
149 protocols_.erase(proto_id);
150 }
151
Proxy(const void * req_buffer,size_t req_size,const zx_handle_t * req_handle_list,size_t req_handle_count,void * out_resp_buffer,size_t resp_size,size_t * out_resp_actual,zx_handle_t * out_resp_handle_list,size_t resp_handle_count,size_t * out_resp_handle_actual)152 zx_status_t PlatformProxy::Proxy(
153 const void* req_buffer, size_t req_size, const zx_handle_t* req_handle_list,
154 size_t req_handle_count, void* out_resp_buffer, size_t resp_size, size_t* out_resp_actual,
155 zx_handle_t* out_resp_handle_list, size_t resp_handle_count,
156 size_t* out_resp_handle_actual) {
157
158 auto* req = static_cast<const platform_proxy_req*>(req_buffer);
159 auto* resp = static_cast<platform_proxy_rsp*>(out_resp_buffer);
160 if (req->device_id != ROOT_DEVICE_ID) {
161 return ZX_ERR_INVALID_ARGS;
162 }
163 if (req_size > PLATFORM_PROXY_MAX_DATA) {
164 return ZX_ERR_OUT_OF_RANGE;
165 }
166
167 return Rpc(ROOT_DEVICE_ID, req, req_size, resp, resp_size,
168 req_handle_list, req_handle_count, out_resp_handle_list,
169 resp_handle_count, out_resp_actual);
170 }
171
Create(zx_device_t * parent,zx_handle_t rpc_channel)172 zx_status_t PlatformProxy::Create(zx_device_t* parent, zx_handle_t rpc_channel) {
173 fbl::AllocChecker ac;
174
175 auto proxy = fbl::MakeRefCountedChecked<PlatformProxy>(&ac, parent, rpc_channel);
176 if (!ac.check()) {
177 return ZX_ERR_NO_MEMORY;
178 }
179
180 return proxy->Init(parent);
181 }
182
Init(zx_device_t * parent)183 zx_status_t PlatformProxy::Init(zx_device_t* parent) {
184 // Get list of extra protocols to proxy.
185 rpc_pdev_req_t req = {};
186 struct {
187 rpc_pdev_rsp_t pdev;
188 uint32_t protocols[PROXY_MAX_PROTOCOLS];
189 } resp = {};
190 req.header.device_id = ROOT_DEVICE_ID;
191 req.header.proto_id = ZX_PROTOCOL_PDEV;
192 req.header.op = PDEV_GET_PROTOCOLS;
193
194 auto status = Rpc(ROOT_DEVICE_ID, &req.header, sizeof(req), &resp.pdev.header, sizeof(resp));
195 if (status != ZX_OK) {
196 return status;
197 }
198
199 if (resp.pdev.protocol_count > 0) {
200 status = DdkAdd("PlatformProxy");
201 if (status != ZX_OK) {
202 return status;
203 }
204 // Increment our reference count so we aren't destroyed while the devmgr
205 // has a reference to us. We will decrement it in DdkRelease();
206 AddRef();
207
208 // Create device hosts for all the protocols.
209 protocol_count_ = resp.pdev.protocol_count;
210
211 for (uint32_t i = 0; i < protocol_count_; i++) {
212 status = ProxyClient::Create(resp.protocols[i], zxdev(),
213 fbl::RefPtr<PlatformProxy>(this));
214 }
215 } else {
216 status = ProxyDevice::CreateRoot(parent, fbl::RefPtr<PlatformProxy>(this));
217 if (status != ZX_OK) {
218 return status;
219 }
220 }
221
222 return ZX_OK;
223 }
224
225 } // namespace platform_bus
226
platform_proxy_create(void * ctx,zx_device_t * parent,const char * name,const char * args,zx_handle_t rpc_channel)227 zx_status_t platform_proxy_create(void* ctx, zx_device_t* parent, const char* name,
228 const char* args, zx_handle_t rpc_channel) {
229 return platform_bus::PlatformProxy::Create(parent, rpc_channel);
230 }
231