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