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