1use crate::node::Node;
6use driver_manager_devfs::{Connector, ConnectorMsg, TopologicalDevnode};
7use fidl::endpoints::{ClientEnd, ServerEnd};
8use futures::channel::mpsc;
9use futures::{StreamExt, TryStreamExt};
10use log::{error, warn};
11use phf::{Map, Set, phf_map, phf_set};
12use std::rc::{Rc, Weak};
13use {fidl_fuchsia_device as fdevice, fidl_fuchsia_device_fs as fdevfs, fuchsia_async as fasync};
14
15const ALLOW_ALL_USES: &str = "Allow_all_uses";
16
17static CONTROLLER_ALLOWLISTS: Map<&'static str, Set<&'static str>> = phf_map! {
19 "ConnectToController" => phf_set! {
20 "block",
21 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/sample_driver.cm",
22 "driver_runner_test",
23 },
24 "ConnectToDeviceFidl" => phf_set! {
25 "block",
26 "driver_runner_test",
27 "nand",
28 "skip-block",
29 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/fvm.cm",
30 "No_class_name_but_driver_url_is_fuchsia-boot:///gpt#meta/gpt.cm",
31 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/gpt.cm",
32 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/nand-broker.cm",
33 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/sample_driver.cm",
34 },
35 "Bind" => phf_set! {
36 "block",
37 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/test.cm",
38 "driver_runner_test",
39 },
40 "Rebind" => phf_set! {
41 "block",
42 "driver_runner_test",
43 "No_class_name_but_driver_url_is_owned by parent",
44 "nand",
45 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/ddk-lifecycle-test.cm",
46 },
47 "UnbindChildren" => phf_set! {
48 "block",
49 "driver_runner_test",
50 },
51 "ScheduleUnbind" => phf_set! {
52 "bt-emulator",
53 "driver_runner_test",
54 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/ddk-lifecycle-test.cm",
55 "No_class_name_but_driver_url_is_fuchsia-boot:///dtr#meta/fvm.cm",
56 "No_class_name_but_driver_url_is_fuchsia-boot:///fvm#meta/fvm.cm",
57 "No_class_name_but_driver_url_is_owned by parent",
58 },
59 "GetTopologicalPath" => phf_set! {
60 "Allow_all_uses",
61 },
62};
63pub struct ControllerAllowlistPassthrough {
66 node: Weak<Node>,
67 class_name: String,
68 compat_client: Option<fdevice::ControllerProxy>,
69 scope: fasync::Scope,
70}
71
72impl ControllerAllowlistPassthrough {
73 pub fn new(
74 node: Weak<Node>,
75 class_name: String,
76 controller_connector: Option<ClientEnd<fdevfs::ConnectorMarker>>,
77 parent_scope: &fasync::ScopeHandle,
78 ) -> Rc<Self> {
79 let compat_client = controller_connector.and_then(|connector| {
80 let (client, server) = fidl::endpoints::create_proxy::<fdevice::ControllerMarker>();
81 let connector_proxy = connector.into_proxy();
82 connector_proxy.connect(server.into()).ok()?;
83 Some(client)
84 });
85
86 let scope = parent_scope.new_child_with_name("controller passthrough");
87 Rc::new(Self { node, class_name, compat_client, scope })
88 }
89
90 fn check_allowlist(&self, function_name: &str) {
91 let allowlist = CONTROLLER_ALLOWLISTS.get(function_name).unwrap();
92 if allowlist.contains(ALLOW_ALL_USES) {
93 return;
94 }
95 assert!(
96 allowlist.contains(self.class_name.as_str()),
97 "\nUndeclared DEVFS_USAGE detected: {} is using {}.\n",
98 self.class_name,
99 function_name
100 );
101 }
102
103 pub fn serve(
104 self: &Rc<Self>,
105 server_end: ServerEnd<fdevice::ControllerMarker>,
106 ) -> Result<(), zx::Status> {
107 let mut stream = server_end.into_stream();
108 let this = self.clone();
109 self.scope.spawn_local(async move {
110 while let Ok(Some(request)) = stream.try_next().await {
111 this.handle_request(request).await.unwrap_or_else(|e| {
112 error!("Error handling controller request: {}", e);
113 });
114 }
115 });
116 Ok(())
117 }
118
119 async fn handle_request(
120 self: &Rc<Self>,
121 request: fdevice::ControllerRequest,
122 ) -> Result<(), fidl::Error> {
123 match request {
124 fdevice::ControllerRequest::ConnectToDeviceFidl { server, .. } => {
125 self.check_allowlist("ConnectToDeviceFidl");
126 if let Some(client) = &self.compat_client {
127 client.connect_to_device_fidl(server)?;
128 } else if let Some(node) = self.node.upgrade() {
129 node.connect_to_device_fidl(server);
130 }
131 }
132 fdevice::ControllerRequest::ConnectToController { server, .. } => {
133 self.check_allowlist("ConnectToController");
134 self.serve(server).unwrap();
135 }
136 fdevice::ControllerRequest::Bind { driver, responder } => {
137 self.check_allowlist("Bind");
138 if let Some(client) = &self.compat_client {
139 let result = client.bind(driver.as_str()).await?;
140 responder.send(result)?;
141 } else if let Some(node) = self.node.upgrade() {
142 responder.send(node.bind(driver).await.map_err(zx::Status::into_raw))?;
143 } else {
144 responder.send(Err(zx::Status::INTERNAL.into_raw()))?;
145 }
146 }
147 fdevice::ControllerRequest::Rebind { driver, responder } => {
148 self.check_allowlist("Rebind");
149 if let Some(client) = &self.compat_client {
150 let result = client.rebind(driver.as_ref()).await?;
151 responder.send(result)?;
152 } else if let Some(node) = self.node.upgrade() {
153 responder
154 .send(node.rebind(Some(driver)).await.map_err(zx::Status::into_raw))?;
155 } else {
156 responder.send(Err(zx::Status::INTERNAL.into_raw()))?;
157 }
158 }
159 fdevice::ControllerRequest::UnbindChildren { responder } => {
160 self.check_allowlist("UnbindChildren");
161 if let Some(client) = &self.compat_client {
162 let result = client.unbind_children().await?;
163 responder.send(result)?;
164 } else if let Some(node) = self.node.upgrade() {
165 let result = node.unbind_children().await.map_err(zx::Status::into_raw);
166 responder.send(result)?;
167 } else {
168 responder.send(Err(zx::Status::INTERNAL.into_raw()))?;
169 }
170 }
171 fdevice::ControllerRequest::ScheduleUnbind { responder } => {
172 self.check_allowlist("ScheduleUnbind");
173 if let Some(client) = &self.compat_client {
174 let result = client.schedule_unbind().await?;
175 responder.send(result)?;
176 } else if let Some(node) = self.node.upgrade() {
177 node.schedule_unbind();
178 responder.send(Ok(()))?;
179 } else {
180 responder.send(Err(zx::Status::INTERNAL.into_raw()))?;
181 }
182 }
183 fdevice::ControllerRequest::GetTopologicalPath { responder } => {
184 self.check_allowlist("GetTopologicalPath");
185 if let Some(node) = self.node.upgrade() {
186 let path = format!("/{}", node.make_topological_path(false));
187 responder.send(Ok(&path))?;
188 } else {
189 responder.send(Err(zx::Status::INTERNAL.into_raw()))?;
190 }
191 }
192 }
193 Ok(())
194 }
195}
196
197impl Node {
198 pub fn setup_devfs_for_root_node(&self, root: TopologicalDevnode) {
199 let mut device = self.device();
200 device.topological = Some(root);
201 self.set_device(device);
202 }
203
204 pub(crate) fn connect_to_device_fidl(&self, server: zx::Channel) {
205 if let Some(connector) = self.protocol_connector()
206 && let Err(e) = connector.connect(server)
207 {
208 error!("Failed to connect to device fidl: {}", e);
209 }
210 }
211
212 pub(crate) fn connect_to_controller(&self, server_end: ServerEnd<fdevice::ControllerMarker>) {
213 if let Some(passthrough) = self.controller_allowlist_passthrough() {
214 let _ = passthrough.serve(server_end);
215 } else {
216 warn!(
217 concat!(
218 "Connection to {} controller interface failed, as that node ",
219 "did not include controller support in its DevAddArgs"
220 ),
221 self.name()
222 );
223 }
224 }
225
226 pub(crate) fn create_devfs_passthrough(
227 self: &Rc<Self>,
228 protocol_connector: Option<ClientEnd<fdevfs::ConnectorMarker>>,
229 controller_connector: Option<ClientEnd<fdevfs::ConnectorMarker>>,
230 allow_controller_connection: bool,
231 class_name: String,
232 ) -> Connector {
233 if allow_controller_connection {
234 let passthrough = ControllerAllowlistPassthrough::new(
235 self.weak_self.clone(),
236 class_name,
237 controller_connector,
238 self.scope.as_handle(),
239 );
240 self.set_controller_allowlist_passthrough(passthrough);
241 }
242 if let Some(c) = protocol_connector {
243 self.set_protocol_connector(c.into_proxy());
244 }
245 let weak_node = self.weak_self.clone();
246 let node_name = self.name().to_string();
247 let (tx, mut rx) = mpsc::unbounded::<ConnectorMsg>();
248 self.scope.spawn_local(async move {
249 while let Some(msg) = rx.next().await {
250 if let Some(node) = weak_node.upgrade() {
251 match msg {
252 ConnectorMsg::Controller(server_end) => {
253 node.connect_to_controller(server_end);
254 }
255 ConnectorMsg::Protocol(server_end) => {
256 node.connect_to_device_fidl(server_end);
257 }
258 }
259 } else {
260 error!("Node was freed before it was used for {}.", node_name);
261 }
262 }
263 });
264 tx
265 }
266}