1use crate::runtime_dir::{CachedProcessInfo, create_runtime_dir};
6use async_trait::async_trait;
7use driver_manager_types::to_deprecated_property;
8use driver_manager_utils::{open_lib_dir, open_pkg_file};
9use fidl::endpoints::ClientEnd;
10use fidl_fuchsia_component_runner as frunner;
11use fidl_fuchsia_data as fdata;
12use fidl_fuchsia_driver_framework as fdf;
13use fidl_fuchsia_driver_host as fdh;
14use fidl_fuchsia_driver_loader as floader;
15use fidl_fuchsia_io as fio;
16use fidl_fuchsia_ldsvc as fldsvc;
17use fidl_fuchsia_mem as fmem;
18use log::error;
19use std::sync::Arc;
20use vfs::directory::simple::Simple;
21use vfs::execution_scope::ExecutionScope;
22
23pub struct DriverStartArgs {
24 pub node: ClientEnd<fdf::NodeMarker>,
25 pub node_name: String,
26 pub properties: fdf::NodePropertyDictionary2,
27 pub symbols: Option<Vec<fdf::NodeSymbol>>,
28 pub offers: Vec<fdf::Offer>,
29 pub start_info: frunner::ComponentStartInfo,
30 pub component_instance: zx::Event,
31}
32
33pub struct DriverLoadArgs {
34 pub driver_soname: String,
35 pub driver_file: zx::Vmo,
36 pub lib_dir: ClientEnd<fio::DirectoryMarker>,
37 pub additional_root_modules: Vec<floader::RootModule>,
38}
39
40fn get_program_string_value<'a>(
41 program: &'a fdata::Dictionary,
42 key: &str,
43) -> Result<&'a str, zx::Status> {
44 program
45 .entries
46 .as_ref()
47 .and_then(|entries| {
48 entries.iter().find(|entry| entry.key == key).and_then(|entry| {
49 entry.value.as_ref().and_then(|value| match &**value {
50 fdata::DictionaryValue::Str(s) => Some(s.as_str()),
51 _ => None,
52 })
53 })
54 })
55 .ok_or(zx::Status::NOT_FOUND)
56}
57
58fn get_program_value_as_obj_vector<'a>(
59 program: &'a fdata::Dictionary,
60 key: &str,
61) -> Result<Vec<&'a fdata::Dictionary>, zx::Status> {
62 let entries = program.entries.as_ref().ok_or(zx::Status::NOT_FOUND)?;
63 let entry = entries.iter().find(|e| e.key == key).ok_or(zx::Status::NOT_FOUND)?;
64 let value = entry.value.as_ref().ok_or(zx::Status::NOT_FOUND)?;
65 if let fdata::DictionaryValue::ObjVec(v) = &**value {
66 Ok(v.iter().collect())
67 } else {
68 Err(zx::Status::WRONG_TYPE)
69 }
70}
71
72fn get_filename(path: &str) -> &str {
73 path.rsplit_once('/').map_or(path, |(_, filename)| filename)
74}
75
76const COMPAT_DRIVER_RELATIVE_PATH: &str = "driver/compat.so";
77
78impl DriverLoadArgs {
79 pub async fn new(start_info: &mut frunner::ComponentStartInfo) -> Result<Self, zx::Status> {
80 let program = start_info.program.as_ref().ok_or(zx::Status::INVALID_ARGS)?;
81 let binary = get_program_string_value(program, "binary")?;
82
83 let ns = start_info.ns.as_mut().ok_or(zx::Status::INVALID_ARGS)?;
84 let pkg_dir_handle = ns
85 .iter_mut()
86 .find(|entry| entry.path.as_deref() == Some("/pkg"))
87 .and_then(|entry| entry.directory.take())
88 .ok_or(zx::Status::INVALID_ARGS)?;
89 let pkg_dir = pkg_dir_handle.into_proxy();
90
91 let driver_file = open_pkg_file(&pkg_dir, binary).await.map_err(|e| {
92 error!("Failed to open pkg file '{}': {}", binary, e);
93 zx::Status::INTERNAL
94 })?;
95
96 let lib_dir = open_lib_dir(&pkg_dir).map_err(|e| {
97 error!("Failed to open lib dir: {}", e);
98 zx::Status::INTERNAL
99 })?;
100 let mut additional_root_modules = vec![];
101 if binary == COMPAT_DRIVER_RELATIVE_PATH {
102 let compat = get_program_string_value(program, "compat")?;
103 let v1_driver_file = open_pkg_file(&pkg_dir, compat).await.map_err(|e| {
104 error!("Failed to open pkg file '{}': {}", compat, e);
105 zx::Status::INTERNAL
106 })?;
107 additional_root_modules.push(floader::RootModule {
108 name: Some(get_filename(compat).to_string()),
109 binary: Some(v1_driver_file),
110 ..Default::default()
111 });
112 }
113
114 if let Ok(modules) = get_program_value_as_obj_vector(program, "modules") {
115 for module in modules {
116 let module_name = get_program_string_value(module, "module_name")?;
117 if module_name == "#program.compat" {
118 continue;
119 }
120 let module_vmo = open_pkg_file(&pkg_dir, module_name).await.map_err(|e| {
121 error!("Failed to open pkg file '{}': {}", module_name, e);
122 zx::Status::INTERNAL
123 })?;
124 additional_root_modules.push(floader::RootModule {
125 name: Some(get_filename(module_name).to_string()),
126 binary: Some(module_vmo),
127 ..Default::default()
128 });
129 }
130 }
131
132 Ok(Self {
133 driver_soname: get_filename(binary).to_string(),
134 driver_file,
135 lib_dir,
136 additional_root_modules,
137 })
138 }
139}
140
141fn set_encoded_config(
142 start_info: &mut frunner::ComponentStartInfo,
143) -> Result<Option<zx::Vmo>, zx::Status> {
144 if let Some(encoded_config) = start_info.encoded_config.take() {
145 match encoded_config {
146 fmem::Data::Buffer(fmem::Buffer { vmo, .. }) => Ok(Some(vmo)),
147 fmem::Data::Bytes(bytes) => {
148 let vmo = zx::Vmo::create(bytes.len() as u64)?;
149 vmo.write(&bytes, 0)?;
150 Ok(Some(vmo))
151 }
152 _ => {
153 error!("Unsupported encoded config format");
154 Err(zx::Status::INVALID_ARGS)
155 }
156 }
157 } else {
158 Ok(None)
159 }
160}
161
162#[async_trait]
163pub trait DriverHost {
164 async fn start(
165 &self,
166 start_args: DriverStartArgs,
167 driver: fidl::endpoints::ServerEnd<fdh::DriverMarker>,
168 ) -> Result<(), zx::Status>;
169
170 async fn start_with_dynamic_linker(
171 &self,
172 load_args: DriverLoadArgs,
173 start_args: DriverStartArgs,
174 driver: fidl::endpoints::ServerEnd<fdh::DriverMarker>,
175 ) -> Result<(), zx::Status>;
176
177 fn install_loader(
178 &self,
179 loader: fidl::endpoints::ClientEnd<fldsvc::LoaderMarker>,
180 ) -> Result<(), zx::Status>;
181
182 fn is_dynamic_linking_enabled(&self) -> bool;
183
184 async fn get_process_koid(&self) -> Result<zx::Koid, zx::Status>;
185
186 async fn get_process_info_internal(
187 &self,
188 ) -> Result<crate::runtime_dir::ProcessInfo, zx::Status>;
189
190 async fn get_crash_info(
191 &self,
192 thread_koid: zx::Koid,
193 ) -> Result<fdh::DriverCrashInfo, zx::Status>;
194
195 fn trigger_stack_trace(&self);
196
197 fn name_for_colocation(&self) -> &str;
198}
199
200pub struct DriverHostComponent {
201 driver_host: fdh::DriverHostProxy,
202 dynamic_linker_driver_loader: Option<floader::DriverHostProxy>,
203 scope: ExecutionScope,
204 process_info: Arc<CachedProcessInfo>,
205 runtime_dir: Arc<Simple>,
206 name_for_colocation: String,
207}
208
209impl DriverHostComponent {
210 pub fn new(
211 driver_host: fdh::DriverHostProxy,
212 dynamic_linker_driver_loader: Option<floader::DriverHostProxy>,
213 scope: ExecutionScope,
214 name_for_colocation: String,
215 ) -> Self {
216 let process_info = Arc::new(CachedProcessInfo::new(driver_host.clone()));
217 let runtime_dir = create_runtime_dir(process_info.clone());
218 Self {
219 driver_host,
220 dynamic_linker_driver_loader,
221 scope,
222 process_info,
223 runtime_dir,
224 name_for_colocation,
225 }
226 }
227}
228
229#[async_trait]
230impl DriverHost for DriverHostComponent {
231 async fn start(
232 &self,
233 start_args: DriverStartArgs,
234 driver: fidl::endpoints::ServerEnd<fdh::DriverMarker>,
235 ) -> Result<(), zx::Status> {
236 let mut start_info = start_args.start_info;
237 let config = set_encoded_config(&mut start_info)?;
238
239 let node_properties_2 = Some(start_args.properties);
240 let node_properties = node_properties_2.as_ref().map(|props2| {
241 props2
242 .iter()
243 .map(|entry2| fdf::NodePropertyEntry {
244 name: entry2.name.clone(),
245 properties: entry2.properties.iter().map(to_deprecated_property).collect(),
246 })
247 .collect::<Vec<_>>()
248 });
249
250 let fidl_start_args = fdf::DriverStartArgs {
251 node: Some(start_args.node),
252 node_name: Some(start_args.node_name),
253 symbols: start_args.symbols,
254 node_offers: Some(start_args.offers),
255 node_properties,
256 node_properties_2,
257 node_token: Some(start_args.component_instance),
258 url: start_info.resolved_url.take(),
259 program: start_info.program.take(),
260 incoming: start_info.ns.take(),
261 outgoing_dir: start_info.outgoing_dir.take(),
262 config,
263 ..Default::default()
264 };
265
266 if let Some(runtime_dir) = start_info.runtime_dir.take() {
267 vfs::directory::serve_on(
268 self.runtime_dir.clone(),
269 fio::PERM_READABLE,
270 self.scope.clone(),
271 runtime_dir,
272 );
273 }
274 let host_name = if !self.name_for_colocation().is_empty() {
275 format!("driver-host-{}", self.name_for_colocation())
276 } else {
277 String::new()
278 };
279
280 self.driver_host
281 .start(fidl_start_args, driver, &host_name)
282 .await
283 .map_err(|e| {
284 error!("Failed to start driver in driver host: {}", e);
285 zx::Status::INTERNAL
286 })?
287 .map_err(zx::Status::from_raw)
288 }
289
290 async fn start_with_dynamic_linker(
291 &self,
292 load_args: DriverLoadArgs,
293 start_args: DriverStartArgs,
294 driver: fidl::endpoints::ServerEnd<fdh::DriverMarker>,
295 ) -> Result<(), zx::Status> {
296 let loader = self.dynamic_linker_driver_loader.as_ref().ok_or(zx::Status::NOT_SUPPORTED)?;
297
298 let driver_soname = load_args.driver_soname.clone();
299 let request = floader::DriverHostLoadDriverRequest {
300 driver_soname: Some(driver_soname.clone()),
301 driver_binary: Some(load_args.driver_file),
302 driver_libs: Some(load_args.lib_dir),
303 additional_root_modules: Some(load_args.additional_root_modules),
304 ..Default::default()
305 };
306
307 loader
308 .load_driver(request)
309 .await
310 .map_err(|e| {
311 error!("Failed to load driver '{}' with dynamic linker: {}", driver_soname, e);
312 if e.is_closed() { zx::Status::PEER_CLOSED } else { zx::Status::INTERNAL }
313 })?
314 .map_err(zx::Status::from_raw)?;
315
316 self.start(start_args, driver).await
317 }
318
319 fn install_loader(
320 &self,
321 loader: fidl::endpoints::ClientEnd<fldsvc::LoaderMarker>,
322 ) -> Result<(), zx::Status> {
323 self.driver_host
324 .install_loader(loader)
325 .map_err(|e| if e.is_closed() { zx::Status::PEER_CLOSED } else { zx::Status::INTERNAL })
326 }
327
328 fn is_dynamic_linking_enabled(&self) -> bool {
329 self.dynamic_linker_driver_loader.is_some()
330 }
331
332 async fn get_process_koid(&self) -> Result<zx::Koid, zx::Status> {
333 self.process_info.get().await.map(|info| info.process_koid)
334 }
335
336 async fn get_process_info_internal(
337 &self,
338 ) -> Result<crate::runtime_dir::ProcessInfo, zx::Status> {
339 self.process_info.get().await.cloned()
340 }
341
342 async fn get_crash_info(
343 &self,
344 thread_koid: zx::Koid,
345 ) -> Result<fdh::DriverCrashInfo, zx::Status> {
346 if let Ok(info) = self.process_info.get().await
349 && info.main_thread_koid == thread_koid
350 {
351 return Err(zx::Status::NOT_FOUND);
352 }
353
354 self.driver_host
355 .find_driver_crash_info_by_thread_koid(thread_koid.raw_koid())
356 .await
357 .map_err(|e| {
358 error!("Failed to get crash info from driver host: {}", e);
359 zx::Status::INTERNAL
360 })?
361 .map_err(zx::Status::from_raw)
362 }
363
364 fn name_for_colocation(&self) -> &str {
365 &self.name_for_colocation
366 }
367
368 fn trigger_stack_trace(&self) {
369 let _ = self.driver_host.trigger_stack_trace();
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn test_get_filename() {
379 assert_eq!(get_filename("foo/bar/baz.so"), "baz.so");
380 assert_eq!(get_filename("baz.so"), "baz.so");
381 assert_eq!(get_filename("/baz.so"), "baz.so");
382 assert_eq!(get_filename(""), "");
383 }
384
385 #[test]
386 fn test_get_program_string_value() {
387 let program = fdata::Dictionary {
388 entries: Some(vec![
389 fdata::DictionaryEntry {
390 key: "binary".to_string(),
391 value: Some(Box::new(fdata::DictionaryValue::Str("driver.so".to_string()))),
392 },
393 fdata::DictionaryEntry {
394 key: "other".to_string(),
395 value: Some(Box::new(fdata::DictionaryValue::Str("value".to_string()))),
396 },
397 ]),
398 ..Default::default()
399 };
400
401 assert_eq!(get_program_string_value(&program, "binary"), Ok("driver.so"));
402 assert_eq!(get_program_string_value(&program, "other"), Ok("value"));
403 assert_eq!(get_program_string_value(&program, "missing"), Err(zx::Status::NOT_FOUND));
404 }
405
406 #[test]
407 fn test_get_program_value_as_obj_vector() {
408 let dict1 = fdata::Dictionary {
409 entries: Some(vec![fdata::DictionaryEntry {
410 key: "k1".to_string(),
411 value: Some(Box::new(fdata::DictionaryValue::Str("v1".to_string()))),
412 }]),
413 ..Default::default()
414 };
415 let dict2 = fdata::Dictionary {
416 entries: Some(vec![fdata::DictionaryEntry {
417 key: "k2".to_string(),
418 value: Some(Box::new(fdata::DictionaryValue::Str("v2".to_string()))),
419 }]),
420 ..Default::default()
421 };
422
423 let program = fdata::Dictionary {
424 entries: Some(vec![fdata::DictionaryEntry {
425 key: "modules".to_string(),
426 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
427 dict1.clone(),
428 dict2.clone(),
429 ]))),
430 }]),
431 ..Default::default()
432 };
433
434 let modules = get_program_value_as_obj_vector(&program, "modules").unwrap();
435 assert_eq!(modules.len(), 2);
436 assert_eq!(get_program_string_value(modules[0], "k1"), Ok("v1"));
437 assert_eq!(get_program_string_value(modules[1], "k2"), Ok("v2"));
438 }
439
440 #[test]
441 fn test_set_encoded_config_buffer() {
442 let vmo = zx::Vmo::create(100).unwrap();
443 vmo.write(b"test", 0).unwrap();
444 let mut start_info = frunner::ComponentStartInfo {
445 encoded_config: Some(fmem::Data::Buffer(fmem::Buffer {
446 vmo: vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
447 size: 4,
448 })),
449 ..Default::default()
450 };
451
452 let config_vmo = set_encoded_config(&mut start_info).unwrap().unwrap();
453 let mut buf = [0u8; 4];
454 config_vmo.read(&mut buf, 0).unwrap();
455 assert_eq!(&buf, b"test");
456 }
457
458 #[test]
459 fn test_set_encoded_config_bytes() {
460 let mut start_info = frunner::ComponentStartInfo {
461 encoded_config: Some(fmem::Data::Bytes(b"test_bytes".to_vec())),
462 ..Default::default()
463 };
464
465 let config_vmo = set_encoded_config(&mut start_info).unwrap().unwrap();
466 let mut buf = [0u8; 10];
467 config_vmo.read(&mut buf, 0).unwrap();
468 assert_eq!(&buf, b"test_bytes");
469 }
470}