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