Skip to main content

driver_manager_driver_host/
driver_host.rs

1// Copyright 2026 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
5use 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        // Bypass the driver host if the crashing thread is the main thread which means the driver host
345        // itself is what crashed.
346        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}