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 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        // Bypass the driver host if the crashing thread is the main thread which means the driver host
347        // itself is what crashed.
348        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}