Skip to main content

driver_manager_core/
driver_runner.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::bootup_tracker::BootupTracker;
6use crate::driver_host_runner::DriverHostRunner;
7use crate::memory_attribution::MemoryAttributor;
8use crate::offer_injection::OfferInjector;
9use crate::runner::Runner;
10use crate::{DriverRunnerBridge, LoaderServiceFactory, perform_bfs, to_collection};
11use driver_manager_bind::BindManagerHandle;
12use driver_manager_composite::{CompositeNodeSpec, CompositeNodeSpecManager};
13use driver_manager_devfs::Devfs;
14use driver_manager_driver_host::{DriverHost, DriverHostComponent};
15use driver_manager_node::Node;
16use driver_manager_shutdown::NodeRemovalTracker;
17use driver_manager_types::{Collection, to_bind_rule2, to_property2};
18use driver_manager_utils::DictionaryUtil;
19use fidl::endpoints::{ServerEnd, create_endpoints};
20use fidl_fuchsia_component as fcomponent;
21use fidl_fuchsia_component_decl as fdecl;
22use fidl_fuchsia_component_sandbox as fsandbox;
23use fidl_fuchsia_driver_crash as fcrash;
24use fidl_fuchsia_driver_development as fdd;
25use fidl_fuchsia_driver_framework as fdf;
26use fidl_fuchsia_driver_host as fdh;
27use fidl_fuchsia_driver_index as fdi;
28use fidl_fuchsia_driver_token as fdt;
29use fidl_fuchsia_io as fio;
30use fuchsia_async as fasync;
31use fuchsia_component::client::connect_to_protocol_at_dir_root;
32use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
33use fuchsia_inspect as inspect;
34use fuchsia_inspect::ArrayProperty;
35use futures::StreamExt;
36use futures::channel::oneshot;
37use log::{debug, error, info, warn};
38use rand::SeedableRng;
39use rand::rngs::StdRng;
40use std::cell::RefCell;
41use std::collections::HashSet;
42use std::rc::{Rc, Weak};
43use std::sync::Arc;
44use vfs::execution_scope::ExecutionScope;
45
46pub struct DriverRunner {
47    pub(crate) driver_index: fdi::DriverIndexProxy,
48    pub(crate) dictionary_util: Rc<DictionaryUtil>,
49    loader_service_factory: LoaderServiceFactory,
50    pub(crate) root_node: Rc<Node>,
51    pub bind_manager: BindManagerHandle,
52    pub(crate) composite_node_spec_manager: CompositeNodeSpecManager,
53    runner: Runner,
54    driver_host_runner: Rc<DriverHostRunner>,
55    pub(crate) removal_tracker: Rc<RefCell<NodeRemovalTracker>>,
56    pub bootup_tracker: Rc<BootupTracker>,
57    driver_hosts: RefCell<Vec<Weak<dyn DriverHost>>>,
58    pub devfs: Arc<Devfs>,
59    pub(crate) memory_attributor: Rc<MemoryAttributor>,
60    launcher: Option<fidl_fuchsia_driver_loader::DriverHostLauncherProxy>,
61    pub(crate) enable_test_shutdown_delays: bool,
62    pub(crate) shutdown_test_rng: Rc<RefCell<StdRng>>,
63    pub(crate) scope: fasync::Scope,
64}
65
66impl DriverRunner {
67    #[allow(clippy::too_many_arguments)]
68    pub fn new(
69        realm: fcomponent::RealmProxy,
70        introspector: fcomponent::IntrospectorProxy,
71        dictionary_util: DictionaryUtil,
72        driver_index: fdi::DriverIndexProxy,
73        loader_service_factory: LoaderServiceFactory,
74        enable_test_shutdown_delays: bool,
75        offer_injector: OfferInjector,
76        devfs: Arc<Devfs>,
77    ) -> Rc<Self> {
78        Rc::new_cyclic(|weak_driver_runner| {
79            let bind_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
80            let bind_manager = BindManagerHandle::new(bind_manager_bridge);
81
82            let composite_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
83            let composite_node_spec_manager =
84                CompositeNodeSpecManager::new(composite_manager_bridge);
85
86            let bootup_tracker = BootupTracker::new(bind_manager.clone());
87
88            let root_node = Node::new(
89                "dev",
90                Weak::new(),
91                Box::new(DriverRunnerBridge(weak_driver_runner.clone())),
92            );
93            root_node.setup_devfs_for_root_node(devfs.root_node());
94
95            let runner = Runner::new(realm.clone(), introspector, offer_injector);
96            let driver_host_runner = DriverHostRunner::new(realm);
97            let removal_tracker = NodeRemovalTracker::new();
98            let memory_attributor = Rc::new(MemoryAttributor::new());
99
100            Self {
101                driver_index,
102                dictionary_util: Rc::new(dictionary_util),
103                loader_service_factory,
104                root_node,
105                bind_manager,
106                composite_node_spec_manager,
107                runner,
108                driver_host_runner,
109                removal_tracker,
110                bootup_tracker,
111                driver_hosts: RefCell::new(Vec::new()),
112                devfs,
113                memory_attributor,
114                launcher: None,
115                enable_test_shutdown_delays,
116                shutdown_test_rng: Rc::new(RefCell::new(StdRng::from_os_rng())),
117                scope: fasync::Scope::new_with_name("driver_runner"),
118            }
119        })
120    }
121
122    pub fn register_notifier(self: &Rc<Self>) -> Result<(), anyhow::Error> {
123        let (client, server) = create_endpoints();
124        self.driver_index.set_notifier(client)?;
125        let weak_self = Rc::downgrade(self);
126        self.scope.spawn_local(async move {
127            let mut stream = server.into_stream();
128            while let Some(Ok(msg)) = stream.next().await {
129                match msg {
130                    fdi::DriverNotifierRequest::NewDriverAvailable { .. } => {
131                        let Some(this) = weak_self.upgrade() else {
132                            return;
133                        };
134                        let _ = this.bind_manager.try_bind_all_available().await;
135                    }
136                }
137            }
138        });
139        Ok(())
140    }
141
142    pub fn root_node(&self) -> Rc<Node> {
143        self.root_node.clone()
144    }
145
146    pub fn get_composite_list_info(&self) -> Vec<fdd::CompositeNodeInfo> {
147        self.composite_node_spec_manager.get_composite_info()
148    }
149
150    pub fn driver_hosts(&self) -> Vec<Rc<dyn DriverHost>> {
151        self.driver_hosts.borrow().iter().filter_map(|w| w.upgrade()).collect()
152    }
153
154    pub fn get_driver_host(
155        &self,
156        driver_host_name_for_colocation: &str,
157    ) -> Option<Rc<dyn DriverHost>> {
158        if driver_host_name_for_colocation.is_empty() {
159            return None;
160        }
161        for host_weak in self.driver_hosts.borrow().iter() {
162            if let Some(host) = host_weak.upgrade()
163                && host.name_for_colocation() == driver_host_name_for_colocation
164            {
165                return Some(host);
166            }
167        }
168        None
169    }
170
171    pub async fn start_root_driver(self: &Rc<Self>, url: String) -> Result<(), zx::Status> {
172        self.bootup_tracker.start();
173        let package_type = if url.starts_with("fuchsia-boot://") {
174            fdf::DriverPackageType::Boot
175        } else {
176            fdf::DriverPackageType::Base
177        };
178
179        let (sender, receiver) = oneshot::channel();
180        self.root_node.set_driver_host_name_for_colocation("root");
181        let self_clone = self.clone();
182        self.scope.spawn_local(async move {
183            if let Err(e) = self_clone.start_driver(&self_clone.root_node, &url, package_type).await
184            {
185                error!("Failed to start root driver: {}", e);
186                sender.send(Err(e)).unwrap();
187            } else {
188                sender.send(Ok(())).unwrap();
189            }
190        });
191        receiver.await.map_err(|_| zx::Status::CANCELED)?
192    }
193
194    pub fn start_devfs_driver(self: &Rc<Self>) {
195        let self_clone = self.clone();
196        self.scope.spawn_local(async move {
197            let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
198            let result = self_clone
199                .runner
200                .create_driver_component(
201                    "devfs_driver",
202                    "fuchsia-boot:///devfs-driver#meta/devfs-driver.cm",
203                    &Collection::Boot.to_string(),
204                    &[],
205                    None,
206                    true,
207                    server,
208                )
209                .await;
210            if let Ok((handle, receiver)) = result {
211                self_clone.devfs.set_component_controller_proxy(client);
212                if let Err(e) = self_clone.devfs.attach_component(handle, receiver).await {
213                    error!("Failed to attach devfs component: {}", e);
214                }
215            } else {
216                error!("Starting the devfs component failed");
217            }
218        });
219    }
220
221    pub async fn start_driver(
222        self: &Rc<Self>,
223        node: &Rc<Node>,
224        url: &str,
225        package_type: fdf::DriverPackageType,
226    ) -> Result<(), zx::Status> {
227        // Ensure `node`'s collection is equal to or higher ranked than its ancestor
228        // nodes' collections. This is to avoid node components having a dependency
229        // cycle with each other. For example, node components in the boot driver
230        // collection depend on the devfs component which ultimately depends on all
231        // components within the package driver collection. If a package driver
232        // component depended on a component in the boot driver collection (a lower
233        // ranked collection than the package driver collection) then a cyclic
234        // dependency would occur.
235        node.set_collection(to_collection(node, package_type));
236        node.set_driver_package_type(package_type);
237
238        let moniker = node.make_component_moniker();
239        self.bootup_tracker.notify_new_start_request(
240            moniker.clone(),
241            url.to_string(),
242            node.weak_from_this(),
243        );
244
245        match self.start_driver_internal(node, url, &moniker).await {
246            Ok(_) => {
247                node.complete_bind(Ok(())).await;
248                self.bootup_tracker.notify_start_complete(&moniker);
249                Ok(())
250            }
251            Err(err) => {
252                node.on_start_error(err);
253                node.complete_bind(Err(err)).await;
254                self.bootup_tracker.notify_start_complete(&moniker);
255                Err(err)
256            }
257        }
258    }
259
260    async fn start_driver_internal(
261        self: &Rc<Self>,
262        node: &Rc<Node>,
263        url: &str,
264        moniker: &str,
265    ) -> Result<(), zx::Status> {
266        let dictionary = node.prepare_dictionary().await;
267
268        if !node.has_component_controller_proxy() {
269            let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
270
271            let offers = node.offers().clone();
272            let create_result = self
273                .runner
274                .create_driver_component(
275                    moniker,
276                    url,
277                    &node.collection().to_string(),
278                    &offers,
279                    dictionary,
280                    node.skip_injected_offers(),
281                    server,
282                )
283                .await;
284
285            match create_result {
286                Ok((handle_info, receiver)) => {
287                    node.set_created_info(client, handle_info, receiver).await;
288                }
289                Err(err) => {
290                    return Err(err);
291                }
292            }
293        }
294
295        node.send_start_request().await?;
296        let (start_info, controller) = node.get_next_start_request().await?;
297        node.start_driver(start_info, controller).await
298    }
299
300    pub async fn create_driver_host(
301        &self,
302        use_next_vdso: bool,
303        driver_host_name_for_colocation: String,
304    ) -> Result<Rc<dyn DriverHost>, zx::Status> {
305        let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
306        let name = if !driver_host_name_for_colocation.is_empty() {
307            format!("driver-host-{}", driver_host_name_for_colocation.trim_start_matches('#'))
308        } else {
309            format!("driver-host-{}", self.driver_hosts.borrow().len())
310        };
311
312        self.create_driver_host_component(&name, exposed_dir_server, use_next_vdso)?;
313
314        let driver_host_proxy =
315            connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
316                .map_err(|_| zx::Status::INTERNAL)?;
317
318        let (tx, rx) = oneshot::channel();
319        let _ = self.loader_service_factory.unbounded_send(tx);
320        let loader_service_client = rx.await.map_err(|e| {
321            error!("Failed to connect to loader service: {}", e);
322            zx::Status::INTERNAL
323        })??;
324
325        let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
326            driver_host_proxy,
327            None,
328            ExecutionScope::new(),
329            driver_host_name_for_colocation,
330        ));
331        driver_host.install_loader(loader_service_client)?;
332
333        self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
334
335        Ok(driver_host)
336    }
337
338    pub async fn create_driver_host_dynamic_linker(
339        self: &Rc<Self>,
340        driver_host_name_for_colocation: String,
341    ) -> Result<Rc<dyn DriverHost>, zx::Status> {
342        let driver_host_runner = self.driver_host_runner.clone();
343        let launcher = self.launcher.clone().unwrap();
344        let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
345        let loader_client = driver_host_runner
346            .start_driver_host(launcher, exposed_dir_server)
347            .await
348            .map_err(|e| {
349                error!("Failed to start driver host: {e:?}");
350                zx::Status::INTERNAL
351            })?;
352
353        let driver_host_client =
354            connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
355                .map_err(|_| zx::Status::INTERNAL)?;
356        let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
357            driver_host_client,
358            Some(loader_client.into_proxy()),
359            ExecutionScope::new(),
360            driver_host_name_for_colocation,
361        ));
362        self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
363        Ok(driver_host)
364    }
365
366    fn create_driver_host_component(
367        &self,
368        moniker: &str,
369        exposed_dir: ServerEnd<fio::DirectoryMarker>,
370        use_next_vdso: bool,
371    ) -> Result<(), zx::Status> {
372        let url = if use_next_vdso {
373            "fuchsia-boot:///driver_host#meta/driver_host_next.cm"
374        } else {
375            "fuchsia-boot:///driver_host#meta/driver_host.cm"
376        };
377
378        let child_decl = fdecl::Child {
379            name: Some(moniker.to_string()),
380            url: Some(url.to_string()),
381            startup: Some(fdecl::StartupMode::Lazy),
382            ..Default::default()
383        };
384
385        let create_child_args = fcomponent::CreateChildArgs::default();
386
387        let realm = self.runner.realm.clone();
388        let child_moniker = moniker.to_string();
389        self.scope.spawn_local(async move {
390            let result = realm
391                .create_child(
392                    &fdecl::CollectionRef { name: "driver-hosts".to_string() },
393                    &child_decl,
394                    create_child_args,
395                )
396                .await;
397
398            if let Err(e) = result {
399                error!("Failed to create driver host '{}': {}", child_moniker, e);
400                return;
401            }
402
403            let child_ref = fdecl::ChildRef {
404                name: child_moniker.clone(),
405                collection: Some("driver-hosts".to_string()),
406            };
407            let open_result = realm.open_exposed_dir(&child_ref, exposed_dir).await;
408            if let Err(e) = open_result {
409                error!(
410                    "Failed to open exposed directory for driver host: '{}': {}",
411                    child_moniker, e
412                );
413            }
414        });
415
416        Ok(())
417    }
418
419    pub async fn destroy_driver_host(
420        &self,
421        driver_host_name_for_colocation: String,
422    ) -> Result<(), zx::Status> {
423        let name = if !driver_host_name_for_colocation.is_empty() {
424            let suffix = driver_host_name_for_colocation.trim_start_matches('#');
425            format!("driver-host-{}", suffix)
426        } else {
427            return Err(zx::Status::INVALID_ARGS);
428        };
429
430        let child_ref =
431            fdecl::ChildRef { name: name.clone(), collection: Some("driver-hosts".to_string()) };
432
433        let result = self.runner.realm.destroy_child(&child_ref).await;
434        match result {
435            Ok(Ok(())) => Ok(()),
436            Ok(Err(fcomponent::Error::InstanceNotFound)) => Ok(()),
437            Ok(Err(fcomponent::Error::InstanceDied)) => Ok(()),
438            Ok(Err(e)) => {
439                error!("Failed to destroy driver host '{}': {:?}", name, e);
440                Err(zx::Status::INTERNAL)
441            }
442            Err(e) => {
443                error!("Failed to destroy driver host '{}': {}", name, e);
444                Err(zx::Status::INTERNAL)
445            }
446        }
447    }
448
449    pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
450        self.runner.publish(fs);
451        self.driver_host_runner.publish(fs);
452        self.memory_attributor.publish(fs);
453
454        let this = self.clone();
455        fs.dir("svc").add_fidl_service(move |stream: fdf::CompositeNodeManagerRequestStream| {
456            this.serve_composite_node_manager(stream);
457        });
458
459        let this = self.clone();
460        fs.dir("svc").add_fidl_service(move |stream: fdt::NodeBusTopologyRequestStream| {
461            this.serve_node_bus_topology(stream);
462        });
463
464        let this = self.clone();
465        fs.dir("svc").add_fidl_service(move |stream: fcrash::CrashIntrospectRequestStream| {
466            this.serve_crash_introspect(stream);
467        });
468
469        let this = self.clone();
470        fs.dir("svc").add_fidl_service(move |stream: fdt::DebugRequestStream| {
471            this.serve_debug(stream);
472        });
473    }
474
475    pub fn serve_debug(self: &Rc<Self>, mut stream: fdt::DebugRequestStream) {
476        let this = self.clone();
477        self.scope.spawn_local(async move {
478            while let Some(Ok(request)) = stream.next().await {
479                match request {
480                    fdt::DebugRequest::LogStackTrace { node_token, responder } => {
481                        let result = this.log_stack_trace(node_token).await;
482                        let _ = match result {
483                            Ok(()) => responder.send(Ok(())),
484                            Err(status) => responder.send(Err(status.into_raw())),
485                        };
486                    }
487                    fdt::DebugRequest::GetHostKoid { node_token, responder } => {
488                        let result = this.get_host_koid(node_token).await;
489                        let _ = match result {
490                            Ok(host_koid) => responder.send(Ok(host_koid.raw_koid())),
491                            Err(status) => responder.send(Err(status.into_raw())),
492                        };
493                    }
494                    fdt::DebugRequest::_UnknownMethod { ordinal, .. } => {
495                        warn!("Unknown Debug request: {}", ordinal);
496                    }
497                }
498            }
499        });
500    }
501
502    async fn log_stack_trace(&self, node_token: zx::Event) -> Result<(), zx::Status> {
503        let token_koid = node_token.basic_info()?.koid;
504        let node = self.find_node_by_token_koid(token_koid).await;
505        if let Some(node) = node {
506            if let Some(host) = node.driver_host() {
507                host.trigger_stack_trace();
508                Ok(())
509            } else {
510                Err(zx::Status::NOT_FOUND)
511            }
512        } else {
513            Err(zx::Status::NOT_FOUND)
514        }
515    }
516
517    async fn get_host_koid(&self, node_token: zx::Event) -> Result<zx::Koid, zx::Status> {
518        let token_koid = node_token.basic_info()?.koid;
519        let node = self.find_node_by_token_koid(token_koid).await;
520        if let Some(node) = node {
521            if let Some(host) = node.driver_host() {
522                host.get_process_koid().await
523            } else {
524                Err(zx::Status::NOT_FOUND)
525            }
526        } else {
527            Err(zx::Status::NOT_FOUND)
528        }
529    }
530
531    pub fn serve_composite_node_manager(
532        self: &Rc<Self>,
533        mut stream: fdf::CompositeNodeManagerRequestStream,
534    ) {
535        let this = self.clone();
536        self.scope.spawn_local(async move {
537            while let Some(Ok(request)) = stream.next().await {
538                this.handle_composite_node_manager_request(request).await;
539            }
540        });
541    }
542
543    async fn handle_composite_node_manager_request(
544        self: &Rc<Self>,
545        request: fdf::CompositeNodeManagerRequest,
546    ) {
547        match request {
548            fdf::CompositeNodeManagerRequest::AddSpec { payload, responder } => {
549                let result = self.add_spec(payload).await;
550                let _ = responder.send(result);
551            }
552            fdf::CompositeNodeManagerRequest::_UnknownMethod { .. } => (),
553        }
554    }
555
556    async fn add_spec(
557        self: &Rc<Self>,
558        spec: fdf::CompositeNodeSpec,
559    ) -> Result<(), fdf::CompositeNodeSpecError> {
560        let name = spec.name.clone().ok_or(fdf::CompositeNodeSpecError::MissingArgs)?;
561
562        let parents_present = spec.parents.is_some();
563        let parents2_present = spec.parents2.is_some();
564
565        if !parents_present && !parents2_present {
566            return Err(fdf::CompositeNodeSpecError::MissingArgs);
567        }
568
569        if parents_present && parents2_present {
570            return Err(fdf::CompositeNodeSpecError::DuplicateParents);
571        }
572
573        let parents2 = if let Some(ref parents) = spec.parents {
574            if parents.is_empty() {
575                return Err(fdf::CompositeNodeSpecError::EmptyNodes);
576            }
577            parents
578                .iter()
579                .map(|parent| {
580                    let bind_rules = parent.bind_rules.iter().map(to_bind_rule2).collect();
581                    let properties = parent.properties.iter().map(to_property2).collect();
582                    fdf::ParentSpec2 { bind_rules, properties }
583                })
584                .collect()
585        } else if let Some(ref parents2) = spec.parents2 {
586            if parents2.is_empty() {
587                return Err(fdf::CompositeNodeSpecError::EmptyNodes);
588            }
589            parents2.clone()
590        } else {
591            unreachable!();
592        };
593
594        let driver_host_name_for_colocation = spec.driver_host.clone().unwrap_or_default();
595
596        let spec_for_manager = CompositeNodeSpec::new(
597            name,
598            parents2,
599            Box::new(DriverRunnerBridge(Rc::downgrade(self))),
600            driver_host_name_for_colocation,
601        );
602
603        self.composite_node_spec_manager.add_spec(spec, spec_for_manager).await
604    }
605
606    pub fn serve_node_bus_topology(self: &Rc<Self>, mut stream: fdt::NodeBusTopologyRequestStream) {
607        let this = self.clone();
608        self.scope.spawn_local(async move {
609            while let Some(Ok(request)) = stream.next().await {
610                match request {
611                    fdt::NodeBusTopologyRequest::Get { token, responder } => {
612                        let result = this.get_bus_topology(token).await;
613                        let _ = match result {
614                            Ok(topology) => responder.send(Ok(&topology)),
615                            Err(status) => responder.send(Err(status.into_raw())),
616                        };
617                    }
618                    fdt::NodeBusTopologyRequest::_UnknownMethod { .. } => (),
619                }
620            }
621        });
622    }
623
624    async fn get_bus_topology(&self, token: zx::Event) -> Result<Vec<fdf::BusInfo>, zx::Status> {
625        let token_koid = token.basic_info()?.koid;
626
627        let node = self.find_node_by_token_koid(token_koid).await;
628
629        if let Some(node) = node { Ok(node.get_bus_topology()) } else { Err(zx::Status::NOT_FOUND) }
630    }
631
632    pub fn serve_crash_introspect(
633        self: &Rc<Self>,
634        mut stream: fcrash::CrashIntrospectRequestStream,
635    ) {
636        let this = self.clone();
637        self.scope.spawn_local(async move {
638            while let Some(Ok(request)) = stream.next().await {
639                match request {
640                    fcrash::CrashIntrospectRequest::FindDriverCrash {
641                        process_koid,
642                        thread_koid,
643                        responder,
644                    } => {
645                        let result = this
646                            .find_driver_crash(
647                                zx::Koid::from_raw(process_koid),
648                                zx::Koid::from_raw(thread_koid),
649                            )
650                            .await;
651                        let _ = match result {
652                            Ok(info) => responder.send(Ok(&info)),
653                            Err(status) => responder.send(Err(status.into_raw())),
654                        };
655                    }
656                }
657            }
658        });
659    }
660
661    async fn find_driver_crash(
662        &self,
663        process_koid: zx::Koid,
664        thread_koid: zx::Koid,
665    ) -> Result<fcrash::DriverCrashInfo, zx::Status> {
666        let hosts = self.driver_hosts.borrow().clone();
667        for host in hosts {
668            if let Some(host) = host.upgrade()
669                && let Ok(koid) = host.get_process_koid().await
670                && koid == process_koid
671            {
672                let crash_info = host.get_crash_info(thread_koid).await?;
673                let token = crash_info.node_token.ok_or(zx::Status::INTERNAL)?;
674                let token_koid = token.into_handle().basic_info()?.koid;
675
676                let node = self.find_node_by_token_koid(token_koid).await;
677
678                if let Some(node) = node {
679                    return Ok(fcrash::DriverCrashInfo {
680                        node_moniker: Some(node.make_component_moniker()),
681                        url: crash_info.url,
682                        ..Default::default()
683                    });
684                } else {
685                    return Err(zx::Status::NOT_FOUND);
686                }
687            }
688        }
689        Err(zx::Status::NOT_FOUND)
690    }
691
692    async fn find_node_by_token_koid(&self, token_koid: zx::Koid) -> Option<Rc<Node>> {
693        let mut result: Option<Rc<Node>> = None;
694        perform_bfs(self.root_node(), async |current| {
695            if result.is_some() {
696                return false; // Already found.
697            }
698            if let Some(current_koid) = current.token_koid()
699                && current_koid == token_koid
700            {
701                result = Some(current.clone());
702                return false;
703            }
704            true
705        })
706        .await;
707        result
708    }
709
710    pub async fn rebind_composites_with_driver(&self, driver_url: &str) -> u32 {
711        let mut names = HashSet::new();
712        perform_bfs(self.root_node(), async |node| {
713            if node.is_composite() && node.driver_url() == driver_url {
714                names.insert(node.name().to_string());
715                return false; // Do not visit children
716            }
717            true // Continue to children
718        })
719        .await;
720
721        let count = names.len() as u32;
722        for name in names {
723            let _ = self.composite_node_spec_manager.rebind(name, None).await;
724        }
725
726        count
727    }
728
729    pub async fn restart_nodes_colocated_with_driver_url(
730        self: &Rc<Self>,
731        url: &str,
732        rematch_flags: fdd::RestartRematchFlags,
733    ) -> Result<u32, zx::Status> {
734        // Step 1: Find all driver hosts with the given driver URL.
735        let mut driver_hosts = HashSet::new();
736        perform_bfs(self.root_node(), async |node| {
737            if node.driver_url() == url
738                && let Some(host) = node.driver_host()
739            {
740                // We need a way to uniquely identify the host.
741                // Using the raw pointer should work.
742                driver_hosts.insert(Rc::as_ptr(&host) as *const ());
743            }
744            true // Continue BFS
745        })
746        .await;
747
748        if driver_hosts.is_empty() {
749            warn!(
750                "restart_nodes_colocated_with_driver_url: no driver hosts found with url {}",
751                url
752            );
753            return Ok(0);
754        }
755
756        let driver_host_count = driver_hosts.len() as u32;
757
758        // Step 2: Perform another BFS to restart the nodes.
759        let this = self.clone();
760        perform_bfs(self.root_node(), async |node| {
761            let host_ptr = node.driver_host().map(|host| Rc::as_ptr(&host) as *const ());
762            if host_ptr.is_none() || !driver_hosts.contains(&host_ptr.unwrap()) {
763                // Not in one of the restarting hosts. Continue to children.
764                return true;
765            }
766
767            // This node is in a driver host that needs to be restarted.
768            if node.evaluate_rematch_flags(rematch_flags, url) {
769                if node.is_composite() {
770                    debug!(
771                        "RestartNodesColocatedWithDriverUrl rebinding composite {}",
772                        node.make_component_moniker()
773                    );
774                    let _ = this
775                        .composite_node_spec_manager
776                        .rebind(node.name().to_string(), None)
777                        .await;
778                } else {
779                    debug!(
780                        "RestartNodesColocatedWithDriverUrl restarting node with rematch {}",
781                        node.make_component_moniker()
782                    );
783                    let (tx, _rx) = oneshot::channel();
784                    node.restart_node_with_rematch(Some("".to_string()), tx);
785                }
786            } else {
787                info!(
788                    "RestartNodesColocatedWithDriverUrl restarting node {}",
789                    node.make_component_moniker()
790                );
791                node.restart_node();
792            }
793
794            false // Do not visit children.
795        })
796        .await;
797
798        Ok(driver_host_count)
799    }
800
801    pub async fn restart_with_dictionary(
802        self: &Rc<Self>,
803        moniker: String,
804        dictionary: fsandbox::DictionaryRef,
805        reset_eventpair: zx::EventPair,
806    ) {
807        let imported = self.dictionary_util.import_dictionary(dictionary).await;
808        let imported = match imported {
809            Ok(imported) => imported,
810            Err(e) => {
811                error!("Failed to import dictionary: {}", e);
812                return;
813            }
814        };
815
816        let mut restarted_node: Option<Rc<Node>> = None;
817        perform_bfs(self.root_node(), async |node| {
818            if restarted_node.is_some() {
819                return false; // Already found, stop searching.
820            }
821
822            if node.make_component_moniker() == moniker {
823                if node.has_subtree_dictionary() {
824                    error!(concat!(
825                        "RestartWithDictionary requested node id already contains a ",
826                        "dictionary_ref from another RestartWithDictionary operation."
827                    ));
828                    return false; // Stop searching
829                }
830                assert!(restarted_node.is_none(), "Multiple nodes with same moniker not possible.");
831                restarted_node = Some(node.clone());
832                node.set_subtree_dictionary(imported);
833                node.restart_node();
834                return false; // Found it, stop searching.
835            }
836
837            true // Continue searching
838        })
839        .await;
840
841        if let Some(restarted_node) = restarted_node {
842            self.scope.spawn_local(async move {
843                let signals = zx::Signals::EVENTPAIR_PEER_CLOSED | zx::Signals::EVENTPAIR_SIGNALED;
844                let on_signals = fasync::OnSignals::new(&reset_eventpair, signals);
845                on_signals.await.expect("failed to wait on eventpair");
846
847                info!("RestartWithDictionary operation released.");
848                restarted_node.remove_subtree_dictionary();
849                restarted_node.restart_node();
850            });
851        }
852    }
853
854    pub fn inspect(&self) -> inspect::Inspector {
855        let inspector =
856            inspect::Inspector::new(inspect::InspectorConfig::default().size(2 * 256 * 1024));
857
858        let mut roots = Vec::new();
859        let mut unique_nodes = HashSet::new();
860
861        let device_tree = inspector.root().create_child("node_topology");
862        let mut root_node_inspect = device_tree.create_child(self.root_node.name());
863
864        self.inspect_node_recursive(
865            &self.root_node,
866            &mut root_node_inspect,
867            &mut roots,
868            &mut unique_nodes,
869        );
870
871        device_tree.record(root_node_inspect);
872        inspector.root().record(device_tree);
873
874        for root in roots {
875            inspector.root().record(root);
876        }
877
878        self.bind_manager.record_inspect(inspector.root());
879
880        inspector
881    }
882
883    fn inspect_node_recursive(
884        &self,
885        node: &Rc<Node>,
886        inspect_node: &mut inspect::Node,
887        roots: &mut Vec<inspect::Node>,
888        unique_nodes: &mut HashSet<*const Node>,
889    ) {
890        let node_ptr = Rc::as_ptr(node);
891        if !unique_nodes.insert(node_ptr) {
892            return;
893        }
894
895        let offers = node.offers();
896        if !offers.is_empty() {
897            let array = inspect_node.create_string_array("offers", offers.len());
898            for (i, offer) in offers.iter().enumerate() {
899                array.set(i, &offer.service_name);
900            }
901            inspect_node.record(array);
902        }
903
904        let symbols = node.symbols();
905        if !symbols.is_empty() {
906            let array = inspect_node.create_string_array("symbols", symbols.len());
907            for (i, symbol) in symbols.iter().enumerate() {
908                if let Some(name) = &symbol.name {
909                    array.set(i, name);
910                }
911            }
912            inspect_node.record(array);
913        }
914
915        if let Some(properties) = node.get_node_properties(None)
916            && !properties.is_empty()
917        {
918            inspect_node.record_child("properties", |properties_node| {
919                for (i, property) in properties.iter().enumerate() {
920                    properties_node.record_child(i.to_string(), |inspect_property| {
921                        inspect_property.record_string("key", &property.key);
922                        match &property.value {
923                            fdf::NodePropertyValue::StringValue(s) => {
924                                inspect_property.record_string("value", s)
925                            }
926                            fdf::NodePropertyValue::IntValue(i) => {
927                                inspect_property.record_uint("value", *i as u64)
928                            }
929                            fdf::NodePropertyValue::EnumValue(e) => {
930                                inspect_property.record_string("value", e)
931                            }
932                            fdf::NodePropertyValue::BoolValue(b) => {
933                                inspect_property.record_bool("value", *b)
934                            }
935                            _ => inspect_property.record_string("value", "UNKNOWN VALUE TYPE"),
936                        }
937                    });
938                }
939            });
940        }
941
942        inspect_node
943            .record_string("type", if node.is_composite() { "Composite Device" } else { "Device" });
944        inspect_node.record_string("topological_path", node.make_topological_path(false));
945        inspect_node.record_string("driver", node.driver_url());
946
947        for child in node.children() {
948            let mut child_inspect_node = inspect_node.create_child(child.name());
949            self.inspect_node_recursive(&child, &mut child_inspect_node, roots, unique_nodes);
950            roots.push(child_inspect_node);
951        }
952    }
953}
954
955#[cfg(test)]
956mod tests {
957    use super::*;
958    use crate::testing::{MockDriverHost, MockNodeManager};
959    use futures::channel::mpsc;
960    use std::sync::atomic::Ordering;
961
962    use fidl_fuchsia_driver_index as fdi;
963
964    #[fasync::run_singlethreaded(test)]
965    async fn test_log_stack_trace() {
966        let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
967        let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
968        let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
969        let (capability_store, _) =
970            fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
971        let (loader_service_factory, _) = mpsc::unbounded();
972
973        let driver_runner = DriverRunner::new(
974            realm,
975            introspector,
976            DictionaryUtil::new(capability_store),
977            driver_index,
978            loader_service_factory,
979            false,
980            OfferInjector::new(crate::offer_injection::PowerOffersConfig {
981                power_inject_offer: false,
982                power_suspend_enabled: false,
983            }),
984            Devfs::new(mpsc::unbounded().0),
985        );
986
987        let node_manager = Box::new(MockNodeManager);
988        let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
989        let host = Rc::new(MockDriverHost::new());
990        node.set_host(host.clone());
991
992        // We need the node to have a token and be found by find_node_by_token_koid.
993        // Node::token_koid() returns Some(koid) if it's in DriverComponent state.
994        let token = zx::Event::create();
995        let token_remote = token.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
996        let koid = token.koid().unwrap();
997
998        node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
999            driver_manager_node::types::DriverComponent::new(
1000                "url".to_string(),
1001                token_remote,
1002                koid,
1003                None,
1004                None,
1005                None,
1006                driver_manager_node::types::DriverState::Running,
1007            ),
1008        ));
1009
1010        // Add the node to the driver runner's root node as a child.
1011        driver_runner.root_node.add_child_to_children_for_testing(node);
1012
1013        // Call LogStackTrace with the token.
1014        let result = driver_runner.log_stack_trace(token).await;
1015        assert!(result.is_ok());
1016        assert_eq!(host.stack_trace_count.load(Ordering::SeqCst), 1);
1017
1018        // Call with a random token should return NOT_FOUND.
1019        let random_token = zx::Event::create();
1020        let result = driver_runner.log_stack_trace(random_token).await;
1021        assert_eq!(result, Err(zx::Status::NOT_FOUND));
1022    }
1023
1024    #[fasync::run_singlethreaded(test)]
1025    async fn test_get_host_koid() {
1026        let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
1027        let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
1028        let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
1029        let (capability_store, _) =
1030            fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
1031        let (loader_service_factory, _) = mpsc::unbounded();
1032
1033        let driver_runner = DriverRunner::new(
1034            realm,
1035            introspector,
1036            DictionaryUtil::new(capability_store),
1037            driver_index,
1038            loader_service_factory,
1039            false,
1040            OfferInjector::new(crate::offer_injection::PowerOffersConfig {
1041                power_inject_offer: false,
1042                power_suspend_enabled: false,
1043            }),
1044            Devfs::new(mpsc::unbounded().0),
1045        );
1046
1047        let node_manager = Box::new(MockNodeManager);
1048        let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
1049        let host = Rc::new(MockDriverHost::new());
1050        node.set_host(host.clone());
1051
1052        let token = zx::Event::create();
1053        let token_remote = token.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1054        let koid = token.koid().unwrap();
1055
1056        node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
1057            driver_manager_node::types::DriverComponent::new(
1058                "url".to_string(),
1059                token_remote,
1060                koid,
1061                None,
1062                None,
1063                None,
1064                driver_manager_node::types::DriverState::Running,
1065            ),
1066        ));
1067
1068        driver_runner.root_node.add_child_to_children_for_testing(node);
1069
1070        let result = driver_runner.get_host_koid(token).await;
1071        assert_eq!(result, Ok(zx::Koid::from_raw(0)));
1072
1073        let random_token = zx::Event::create();
1074        let result = driver_runner.get_host_koid(random_token).await;
1075        assert_eq!(result, Err(zx::Status::NOT_FOUND));
1076    }
1077}