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