1use crate::runtime_dir::{CachedProcessInfo, create_runtime_dir};
6use async_trait::async_trait;
7
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
241 let fidl_start_args = fdf::DriverStartArgs {
242 node: Some(start_args.node),
243 node_name: Some(start_args.node_name),
244 symbols: start_args.symbols,
245 node_offers: Some(start_args.offers),
246 node_properties_2,
247 node_token: Some(start_args.component_instance),
248 url: start_info.resolved_url.take(),
249 program: start_info.program.take(),
250 incoming: start_info.ns.take(),
251 outgoing_dir: start_info.outgoing_dir.take(),
252 config,
253 ..Default::default()
254 };
255
256 if let Some(runtime_dir) = start_info.runtime_dir.take() {
257 vfs::directory::serve_on(
258 self.runtime_dir.clone(),
259 fio::PERM_READABLE,
260 self.scope.clone(),
261 runtime_dir,
262 );
263 }
264 let host_name = if !self.name_for_colocation().is_empty() {
265 format!("driver-host-{}", self.name_for_colocation())
266 } else {
267 String::new()
268 };
269
270 self.driver_host
271 .start(fidl_start_args, driver, &host_name)
272 .await
273 .map_err(|e| {
274 error!("Failed to start driver in driver host: {}", e);
275 zx::Status::INTERNAL
276 })?
277 .map_err(zx::Status::err_from_raw)
278 }
279
280 async fn start_with_dynamic_linker(
281 &self,
282 load_args: DriverLoadArgs,
283 start_args: DriverStartArgs,
284 driver: fidl::endpoints::ServerEnd<fdh::DriverMarker>,
285 ) -> Result<(), zx::Status> {
286 let loader = self.dynamic_linker_driver_loader.as_ref().ok_or(zx::Status::NOT_SUPPORTED)?;
287
288 let driver_soname = load_args.driver_soname.clone();
289 let request = floader::DriverHostLoadDriverRequest {
290 driver_soname: Some(driver_soname.clone()),
291 driver_binary: Some(load_args.driver_file),
292 driver_libs: Some(load_args.lib_dir),
293 additional_root_modules: Some(load_args.additional_root_modules),
294 ..Default::default()
295 };
296
297 loader
298 .load_driver(request)
299 .await
300 .map_err(|e| {
301 error!("Failed to load driver '{}' with dynamic linker: {}", driver_soname, e);
302 if e.is_closed() { zx::Status::PEER_CLOSED } else { zx::Status::INTERNAL }
303 })?
304 .map_err(zx::Status::err_from_raw)?;
305
306 self.start(start_args, driver).await
307 }
308
309 fn install_loader(
310 &self,
311 loader: fidl::endpoints::ClientEnd<fldsvc::LoaderMarker>,
312 ) -> Result<(), zx::Status> {
313 self.driver_host
314 .install_loader(loader)
315 .map_err(|e| if e.is_closed() { zx::Status::PEER_CLOSED } else { zx::Status::INTERNAL })
316 }
317
318 fn is_dynamic_linking_enabled(&self) -> bool {
319 self.dynamic_linker_driver_loader.is_some()
320 }
321
322 async fn get_process_koid(&self) -> Result<zx::Koid, zx::Status> {
323 self.process_info.get().await.map(|info| info.process_koid)
324 }
325
326 async fn get_process_info_internal(
327 &self,
328 ) -> Result<crate::runtime_dir::ProcessInfo, zx::Status> {
329 match self.driver_host.get_process_info().await {
330 Ok(Ok((job_koid, process_koid, main_thread_koid, threads, dispatchers))) => {
331 let info = crate::runtime_dir::ProcessInfo {
332 job_koid: zx::Koid::from_raw(job_koid),
333 process_koid: zx::Koid::from_raw(process_koid),
334 main_thread_koid: zx::Koid::from_raw(main_thread_koid),
335 threads,
336 dispatchers,
337 };
338 self.process_info.cache_koids(&info).await;
339 Ok(info)
340 }
341 Ok(Err(e)) => Err(zx::Status::err_from_raw(e)),
342 Err(e) => {
343 if e.is_closed() {
344 Err(zx::Status::PEER_CLOSED)
345 } else {
346 Err(zx::Status::INTERNAL)
347 }
348 }
349 }
350 }
351
352 async fn get_crash_info(
353 &self,
354 thread_koid: zx::Koid,
355 ) -> Result<fdh::DriverCrashInfo, zx::Status> {
356 if let Ok(info) = self.process_info.get().await
359 && info.main_thread_koid == thread_koid
360 {
361 return Err(zx::Status::NOT_FOUND);
362 }
363
364 self.driver_host
365 .find_driver_crash_info_by_thread_koid(thread_koid.raw_koid())
366 .await
367 .map_err(|e| {
368 error!("Failed to get crash info from driver host: {}", e);
369 zx::Status::INTERNAL
370 })?
371 .map_err(zx::Status::err_from_raw)
372 }
373
374 fn name_for_colocation(&self) -> &str {
375 &self.name_for_colocation
376 }
377
378 fn trigger_stack_trace(&self) {
379 let _ = self.driver_host.trigger_stack_trace();
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
388 fn test_get_filename() {
389 assert_eq!(get_filename("foo/bar/baz.so"), "baz.so");
390 assert_eq!(get_filename("baz.so"), "baz.so");
391 assert_eq!(get_filename("/baz.so"), "baz.so");
392 assert_eq!(get_filename(""), "");
393 }
394
395 #[test]
396 fn test_get_program_string_value() {
397 let program = fdata::Dictionary {
398 entries: Some(vec![
399 fdata::DictionaryEntry {
400 key: "binary".to_string(),
401 value: Some(Box::new(fdata::DictionaryValue::Str("driver.so".to_string()))),
402 },
403 fdata::DictionaryEntry {
404 key: "other".to_string(),
405 value: Some(Box::new(fdata::DictionaryValue::Str("value".to_string()))),
406 },
407 ]),
408 ..Default::default()
409 };
410
411 assert_eq!(get_program_string_value(&program, "binary"), Ok("driver.so"));
412 assert_eq!(get_program_string_value(&program, "other"), Ok("value"));
413 assert_eq!(get_program_string_value(&program, "missing"), Err(zx::Status::NOT_FOUND));
414 }
415
416 #[test]
417 fn test_get_program_value_as_obj_vector() {
418 let dict1 = fdata::Dictionary {
419 entries: Some(vec![fdata::DictionaryEntry {
420 key: "k1".to_string(),
421 value: Some(Box::new(fdata::DictionaryValue::Str("v1".to_string()))),
422 }]),
423 ..Default::default()
424 };
425 let dict2 = fdata::Dictionary {
426 entries: Some(vec![fdata::DictionaryEntry {
427 key: "k2".to_string(),
428 value: Some(Box::new(fdata::DictionaryValue::Str("v2".to_string()))),
429 }]),
430 ..Default::default()
431 };
432
433 let program = fdata::Dictionary {
434 entries: Some(vec![fdata::DictionaryEntry {
435 key: "modules".to_string(),
436 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
437 dict1.clone(),
438 dict2.clone(),
439 ]))),
440 }]),
441 ..Default::default()
442 };
443
444 let modules = get_program_value_as_obj_vector(&program, "modules").unwrap();
445 assert_eq!(modules.len(), 2);
446 assert_eq!(get_program_string_value(modules[0], "k1"), Ok("v1"));
447 assert_eq!(get_program_string_value(modules[1], "k2"), Ok("v2"));
448 }
449
450 #[test]
451 fn test_set_encoded_config_buffer() {
452 let vmo = zx::Vmo::create(100).unwrap();
453 vmo.write(b"test", 0).unwrap();
454 let mut start_info = frunner::ComponentStartInfo {
455 encoded_config: Some(fmem::Data::Buffer(fmem::Buffer {
456 vmo: vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
457 size: 4,
458 })),
459 ..Default::default()
460 };
461
462 let config_vmo = set_encoded_config(&mut start_info).unwrap().unwrap();
463 let mut buf = [0u8; 4];
464 config_vmo.read(&mut buf, 0).unwrap();
465 assert_eq!(&buf, b"test");
466 }
467
468 #[test]
469 fn test_set_encoded_config_bytes() {
470 let mut start_info = frunner::ComponentStartInfo {
471 encoded_config: Some(fmem::Data::Bytes(b"test_bytes".to_vec())),
472 ..Default::default()
473 };
474
475 let config_vmo = set_encoded_config(&mut start_info).unwrap().unwrap();
476 let mut buf = [0u8; 10];
477 config_vmo.read(&mut buf, 0).unwrap();
478 assert_eq!(&buf, b"test_bytes");
479 }
480}