1use crate::{AggregateEntry, BoardConfig, Device, Iommu, IommuType, ResourceEntry};
6use anyhow::{Context, anyhow, bail};
7use fdf_fidl::DriverChannel;
8use fidl_fuchsia_driver_metadata as fdr;
9use fidl_next_fuchsia_driver_framework as fdf_framework;
10use fidl_next_fuchsia_hardware_platform_bus as fpbus;
11use fidl_next_fuchsia_hardware_power as fpower;
12use futures::future::try_join_all;
13use phf;
14use std::collections::{HashMap, HashSet};
15use zx;
16
17pub const BIND_PROTOCOL_DEVICE: u32 = 85; pub const BIND_PLATFORM_DEV_VID_GENERIC: u32 = 0;
19pub const BIND_PLATFORM_DEV_DID_DEVICETREE: u32 = 50; pub fn property_int(val: u32) -> fdf_framework::NodePropertyValue {
22 fdf_framework::NodePropertyValue::IntValue(val)
23}
24
25pub fn property_string(val: &str) -> fdf_framework::NodePropertyValue {
26 fdf_framework::NodePropertyValue::StringValue(val.to_string())
27}
28
29pub fn property_bool(val: bool) -> fdf_framework::NodePropertyValue {
30 fdf_framework::NodePropertyValue::BoolValue(val)
31}
32
33fn map_interrupt_mode(mode: Option<&str>) -> fpbus::ZirconInterruptMode {
34 match mode {
35 Some("EdgeLow") => fpbus::ZirconInterruptMode::EdgeLow,
36 Some("EdgeHigh") => fpbus::ZirconInterruptMode::EdgeHigh,
37 Some("LevelLow") => fpbus::ZirconInterruptMode::LevelLow,
38 Some("LevelHigh") => fpbus::ZirconInterruptMode::LevelHigh,
39 Some("EdgeBoth") => fpbus::ZirconInterruptMode::EdgeBoth,
40 _ => fpbus::ZirconInterruptMode::Default,
41 }
42}
43
44pub type DriverSpecificMetadata =
45 phf::Map<&'static str, &'static [(&'static str, fn() -> anyhow::Result<Vec<u8>>)]>;
46
47pub type DriverSpecificPowerConfigs =
48 phf::Map<&'static str, fn() -> anyhow::Result<Vec<fpower::PowerElementConfiguration>>>;
49
50pub fn make_accept_bind_rule(
51 key: &str,
52 value: fdf_framework::NodePropertyValue,
53) -> fdf_framework::BindRule2 {
54 fdf_framework::BindRule2 {
55 key: key.to_string(),
56 condition: fdf_framework::Condition::Accept,
57 values: vec![value],
58 }
59}
60
61pub fn make_property2(
62 key: &str,
63 value: fdf_framework::NodePropertyValue,
64) -> fdf_framework::NodeProperty2 {
65 fdf_framework::NodeProperty2 { key: key.to_string(), value }
66}
67
68pub fn add_rule_and_property(
69 bind_rules: &mut Vec<fdf_framework::BindRule2>,
70 properties: &mut Vec<fdf_framework::NodeProperty2>,
71 key: &str,
72 value: fdf_framework::NodePropertyValue,
73) {
74 bind_rules.push(make_accept_bind_rule(key, value.clone()));
75 properties.push(make_property2(key, value));
76}
77
78#[derive(Clone, Copy, Debug)]
79pub enum ValueSource {
80 ConstraintKey(&'static str),
81 ResourceName,
82 ResourceNode,
83 Integer(u32),
84 ProviderId,
85}
86
87#[derive(Clone, Copy, Debug)]
88pub enum RuleValueType {
89 Integer,
90 String,
91 Bool,
92}
93
94#[derive(Clone, Copy, Debug)]
95pub enum Destination {
96 BindRules,
97 Properties,
98 Both,
99}
100
101#[derive(Clone, Copy, Debug)]
102pub struct PropertyRule {
103 pub bind_key: &'static str,
104 pub sources: &'static [ValueSource],
105 pub value_type: RuleValueType,
106 pub destination: Destination,
107}
108
109#[derive(Clone, Copy, Debug)]
110pub enum TransportType {
111 None,
112 Zircon,
113 Driver,
114}
115
116#[derive(Clone, Copy, Debug)]
117pub struct ServiceBindConfig {
118 pub transport: TransportType,
119 pub rules: &'static [PropertyRule],
120 pub parent_key_sources: &'static [ValueSource],
121 pub bind_id: bool,
122}
123
124pub const DEFAULT_SERVICE_BIND_CONFIG: ServiceBindConfig = ServiceBindConfig {
125 transport: TransportType::Zircon,
126 rules: &[],
127 parent_key_sources: &[ValueSource::ResourceName],
128 bind_id: false,
129};
130
131pub const DEFAULT_ID_SERVICE_BIND_CONFIG: ServiceBindConfig =
132 ServiceBindConfig { bind_id: true, ..DEFAULT_SERVICE_BIND_CONFIG };
133
134pub struct DmlParserConfig {
135 pub service_configs: phf::Map<&'static str, ServiceBindConfig>,
136}
137
138pub static STANDARD_SERVICE_CONFIGS: phf::Map<&'static str, ServiceBindConfig> = phf::phf_map! {
139 "fuchsia.clock.Init" => ServiceBindConfig {
140 transport: TransportType::None,
141 rules: &[PropertyRule {
142 bind_key: "fuchsia.BIND_INIT_STEP",
143 sources: &[ValueSource::Integer(0x494B4C43)],
144 value_type: RuleValueType::Integer,
145 destination: Destination::Both,
146 }],
147 ..DEFAULT_SERVICE_BIND_CONFIG
148 },
149 "fuchsia.pwm.Init" => ServiceBindConfig {
150 transport: TransportType::None,
151 rules: &[PropertyRule {
152 bind_key: "fuchsia.BIND_INIT_STEP",
153 sources: &[ValueSource::Integer(0x004D5750)],
154 value_type: RuleValueType::Integer,
155 destination: Destination::Both,
156 }],
157 ..DEFAULT_SERVICE_BIND_CONFIG
158 },
159 "fuchsia.gpio.Init" => ServiceBindConfig {
160 transport: TransportType::None,
161 rules: &[PropertyRule {
162 bind_key: "fuchsia.BIND_INIT_STEP",
163 sources: &[ValueSource::Integer(0x4F495047)],
164 value_type: RuleValueType::Integer,
165 destination: Destination::Both,
166 }],
167 ..DEFAULT_SERVICE_BIND_CONFIG
168 },
169 "fuchsia.hardware.gpu.mali.Service" => ServiceBindConfig {
170 transport: TransportType::Driver,
171 ..DEFAULT_SERVICE_BIND_CONFIG
172 },
173 "fuchsia.hardware.platform.device.Service" => ServiceBindConfig {
174 rules: &[
175 PropertyRule {
176 bind_key: "fuchsia.BIND_PROTOCOL",
177 sources: &[ValueSource::Integer(BIND_PROTOCOL_DEVICE)],
178 value_type: RuleValueType::Integer,
179 destination: Destination::Both,
180 },
181 PropertyRule {
182 bind_key: "fuchsia.BIND_PLATFORM_DEV_VID",
183 sources: &[ValueSource::ConstraintKey("vid")],
184 value_type: RuleValueType::Integer,
185 destination: Destination::Both,
186 },
187 PropertyRule {
188 bind_key: "fuchsia.BIND_PLATFORM_DEV_DID",
189 sources: &[ValueSource::ConstraintKey("did")],
190 value_type: RuleValueType::Integer,
191 destination: Destination::Both,
192 },
193 ],
194 ..DEFAULT_SERVICE_BIND_CONFIG
195 },
196 "fuchsia.hardware.platform.bus.Service" => ServiceBindConfig {
197 transport: TransportType::Driver,
198 rules: &[
199 PropertyRule {
200 bind_key: "fuchsia.BIND_PLATFORM_DEV_VID",
201 sources: &[ValueSource::ConstraintKey("vid")],
202 value_type: RuleValueType::Integer,
203 destination: Destination::Both,
204 },
205 PropertyRule {
206 bind_key: "fuchsia.BIND_PLATFORM_DEV_PID",
207 sources: &[ValueSource::ConstraintKey("pid")],
208 value_type: RuleValueType::Integer,
209 destination: Destination::Both,
210 },
211 PropertyRule {
212 bind_key: "fuchsia.BIND_PLATFORM_DEV_DID",
213 sources: &[ValueSource::ConstraintKey("did")],
214 value_type: RuleValueType::Integer,
215 destination: Destination::Both,
216 },
217 ],
218 ..DEFAULT_SERVICE_BIND_CONFIG
219 },
220};
221
222pub const DEFAULT_DML_PARSER_CONFIG: DmlParserConfig =
223 DmlParserConfig { service_configs: phf::phf_map! {} };
224
225impl Default for DmlParserConfig {
226 fn default() -> Self {
227 DEFAULT_DML_PARSER_CONFIG
228 }
229}
230
231fn resolve_value(
232 _provider: &str,
233 provider_id: u32,
234 source: &ValueSource,
235 res: &ResourceEntry,
236 constraint: &fdr::Dictionary,
237) -> Option<ResolvedValue> {
238 match source {
239 ValueSource::ConstraintKey(key) => {
240 if let Some(i) = crate::get_int64(constraint, key) {
241 Some(ResolvedValue::Integer(i as u32))
242 } else if let Some(s) = crate::get_string(constraint, key) {
243 Some(ResolvedValue::String(s))
244 } else if let Some(b) = crate::get_bool(constraint, key) {
245 Some(ResolvedValue::Bool(b))
246 } else {
247 None
248 }
249 }
250 ValueSource::ResourceName => res.name.clone().map(ResolvedValue::String),
251 ValueSource::ResourceNode => res.node.clone().map(ResolvedValue::String),
252 ValueSource::Integer(i) => Some(ResolvedValue::Integer(*i)),
253 ValueSource::ProviderId => Some(ResolvedValue::Integer(provider_id)),
254 }
255}
256
257enum ResolvedValue {
258 Integer(u32),
259 String(String),
260 Bool(bool),
261}
262
263fn apply_rule(
264 provider: &str,
265 provider_id: u32,
266 rule: &PropertyRule,
267 res: &ResourceEntry,
268 constraint: &fdr::Dictionary,
269 bind_rules: &mut Vec<fdf_framework::BindRule2>,
270 properties: &mut Vec<fdf_framework::NodeProperty2>,
271) -> anyhow::Result<()> {
272 let resolved = rule
273 .sources
274 .iter()
275 .find_map(|source| resolve_value(provider, provider_id, source, res, constraint));
276
277 let Some(val) = resolved else {
278 return Ok(());
279 };
280
281 let property_value = match (val, rule.value_type) {
282 (ResolvedValue::Integer(i), RuleValueType::Integer) => property_int(i),
283 (ResolvedValue::String(s), RuleValueType::String) => property_string(&s),
284 (ResolvedValue::Bool(b), RuleValueType::Bool) => property_bool(b),
285 (ResolvedValue::String(s), RuleValueType::Integer) => {
286 let parsed = if s.starts_with("0x") || s.starts_with("0X") {
287 u32::from_str_radix(&s[2..], 16)
288 } else {
289 s.parse::<u32>()
290 };
291 match parsed {
292 Ok(i) => property_int(i),
293 Err(e) => {
294 bail!("Failed to parse string {} as integer: {}", s, e);
295 }
296 }
297 }
298 (ResolvedValue::String(s), RuleValueType::Bool) => match s.parse::<bool>() {
299 Ok(b) => property_bool(b),
300 Err(e) => {
301 bail!("Failed to parse string {} as bool: {}", s, e);
302 }
303 },
304 _ => {
305 bail!("Type mismatch in rule resolution");
306 }
307 };
308
309 match rule.destination {
310 Destination::BindRules => {
311 bind_rules.push(make_accept_bind_rule(rule.bind_key, property_value));
312 }
313 Destination::Properties => {
314 properties.push(make_property2(rule.bind_key, property_value));
315 }
316 Destination::Both => {
317 add_rule_and_property(bind_rules, properties, rule.bind_key, property_value);
318 }
319 }
320
321 Ok(())
322}
323
324pub fn generate_parent_spec_generic(
325 provider: &str,
326 provider_id: u32,
327 service_name: &str,
328 res: &ResourceEntry,
329 config: &DmlParserConfig,
330) -> anyhow::Result<Option<(fdf_framework::ParentSpec2, String)>> {
331 let mut bind_rules = Vec::new();
332 let mut properties = Vec::new();
333
334 let service_config = config
335 .service_configs
336 .get(service_name)
337 .or_else(|| STANDARD_SERVICE_CONFIGS.get(service_name))
338 .unwrap_or(&DEFAULT_ID_SERVICE_BIND_CONFIG);
339
340 match service_config.transport {
341 TransportType::Zircon => {
342 add_rule_and_property(
343 &mut bind_rules,
344 &mut properties,
345 "fuchsia.Service",
346 property_string(service_name),
347 );
348 properties.push(make_property2(
349 service_name,
350 property_string(&format!("{service_name}.ZirconTransport")),
351 ));
352 }
353 TransportType::Driver => {
354 add_rule_and_property(
355 &mut bind_rules,
356 &mut properties,
357 "fuchsia.Service",
358 property_string(service_name),
359 );
360 properties.push(make_property2(
361 service_name,
362 property_string(&format!("{service_name}.DriverTransport")),
363 ));
364 }
365 TransportType::None => {}
366 }
367
368 let constraint =
369 res.constraint.as_ref().ok_or_else(|| anyhow!("constraint is missing in ResourceEntry"))?;
370
371 for rule in service_config.rules {
372 apply_rule(provider, provider_id, rule, res, constraint, &mut bind_rules, &mut properties)?;
373 }
374
375 if !properties.iter().any(|p| p.key == "fuchsia.NAME") {
376 let name_opt = res.name.clone().or_else(|| crate::get_string(constraint, "name"));
377 if let Some(name) = name_opt {
378 properties.push(make_property2("fuchsia.NAME", property_string(&name)));
379 }
380 }
381
382 if service_config.bind_id
383 && !bind_rules.iter().any(|r| r.key == "fuchsia.ID")
384 && let Some(id) = crate::get_uint32(constraint, "id")
385 {
386 bind_rules.push(make_accept_bind_rule("fuchsia.ID", property_int(id)));
387 }
388
389 let resolved_key = service_config.parent_key_sources.iter().find_map(|source| {
390 match resolve_value(provider, provider_id, source, res, constraint) {
391 Some(ResolvedValue::String(k)) => Some(k),
392 _ => None,
393 }
394 });
395
396 let key = match resolved_key {
397 Some(k) => k,
398 None => {
399 bail!(
400 "Failed to resolve parent key for service {}. If this service should be ignored, add it to the config.",
401 service_name
402 );
403 }
404 };
405
406 let parent = fdf_framework::ParentSpec2 { bind_rules, properties };
407 Ok(Some((parent, key)))
408}
409
410fn get_aggregate_id(agg: &AggregateEntry, devices: &[Device], fallback_id: u32) -> u32 {
411 let device = devices.iter().find(|d| d.name.as_deref() == agg.provider.as_deref());
412 let id = device.and_then(|d| d.id).unwrap_or(fallback_id);
413 log::debug!(
414 "get_aggregate_id: provider={:?}, device_found={}, device_id={:?}, final_id={}, fallback={}",
415 agg.provider,
416 device.is_some(),
417 device.and_then(|d| d.id),
418 id,
419 fallback_id
420 );
421 id
422}
423
424pub async fn publish_dml_devices(
425 pbus: &fidl_next::Client<fpbus::PlatformBus, DriverChannel>,
426 composite_manager: &fidl_next::Client<fdf_framework::CompositeNodeManager, zx::Channel>,
427 config: &BoardConfig,
428 parser_config: &DmlParserConfig,
429 driver_metadata: Option<&DriverSpecificMetadata>,
430 driver_power_configs: Option<&DriverSpecificPowerConfigs>,
431 enabled_nodes: &[String],
432) -> anyhow::Result<()> {
433 let mut provider_metadata = HashMap::<String, Vec<fpbus::Metadata>>::new();
434
435 register_iommus(pbus, config.iommus.as_deref()).await.context("Failed to register IOMMUs")?;
436
437 let devices = config
438 .devices
439 .as_ref()
440 .ok_or_else(|| anyhow!("devices field is missing in BoardConfig"))?;
441
442 let disabled_devices: HashSet<&str> = devices
443 .iter()
444 .filter(|d| crate::is_device_disabled(d, enabled_nodes))
445 .filter_map(|d| d.name.as_deref())
446 .collect();
447
448 if let Some(drv_meta) = driver_metadata {
450 for dev in devices {
451 let name = dev.name.as_deref().unwrap_or("");
452 if disabled_devices.contains(name) {
453 continue;
454 }
455 if let Some(generators) = drv_meta.get(name) {
456 for (metadata_id, gen_fn) in *generators {
457 let data = gen_fn()
458 .with_context(|| format!("Failed to generate metadata for {}", name))?;
459 provider_metadata.entry(name.to_string()).or_default().push(fpbus::Metadata {
460 id: Some(metadata_id.to_string()),
461 data: Some(data),
462 ..Default::default()
463 });
464 }
465 }
466 }
467 }
468
469 for (idx, dev) in devices.iter().enumerate() {
470 let dev_name = dev.name.as_deref().unwrap_or("");
471 if disabled_devices.contains(dev_name) {
472 continue;
473 }
474 if dev.disabled.unwrap_or(false) {
475 log::info!("Publishing disabled DML device '{}' due to runtime override", dev_name);
476 }
477 let instance_id = idx as u32 + 1;
478 let mut node = fpbus::Node {
479 name: dev.name.clone(),
480 vid: Some(0),
481 pid: Some(0),
482 did: Some(0),
483 instance_id: Some(instance_id),
484 driver_host: dev.driver_host.clone(),
485 ..Default::default()
486 };
487 node.interrupt_controller_id = dev.interrupt_controller_id;
488
489 if let Some(compatible) = &dev.compatible {
490 node.properties = Some(vec![fdf_framework::NodeProperty2 {
491 key: "fuchsia.COMPATIBLE".to_string(),
492 value: fdf_framework::NodePropertyValue::StringValue(compatible.clone()),
493 }]);
494 node.did = Some(BIND_PLATFORM_DEV_DID_DEVICETREE);
495 node.vid = Some(BIND_PLATFORM_DEV_VID_GENERIC);
496 }
497
498 let mut mmio_list = Vec::new();
499 let mut irq_list = Vec::new();
500 let mut bti_list = Vec::new();
501 let mut smc_list = Vec::new();
502 let mut boot_metadata_list = Vec::new();
503
504 if let Some(pdev_dict) = crate::pdev_constraints(config, dev.name.as_deref().unwrap_or(""))
505 {
506 for mmio in crate::mmio_list(pdev_dict) {
507 mmio_list.push(fpbus::Mmio {
508 base: Some(mmio.base),
509 length: Some(mmio.length),
510 name: mmio.name,
511 ..Default::default()
512 });
513 }
514 for irq in crate::irq_list(pdev_dict) {
515 let irq_spec = match irq.controller {
516 Some(controller_id) => fpbus::IrqSpec::UserspaceIrq(fpbus::UserspaceIrq {
517 irq: irq.number,
518 controller_id,
519 }),
520 None => fpbus::IrqSpec::Irq(irq.number),
521 };
522 irq_list.push(fpbus::Irq {
523 irq: Some(irq_spec),
524 mode: Some(map_interrupt_mode(irq.mode.as_deref())),
525 name: irq.name.clone(),
526 wake_vector: irq.wake_vector,
527 ..Default::default()
528 });
529 }
530 for bti in crate::bti_list(pdev_dict)? {
531 bti_list.push(fpbus::Bti {
532 iommu_id: Some(bti.iommu_id),
533 bti_id: Some(bti.id),
534 name: bti.name,
535 ..Default::default()
536 });
537 }
538 for smc in crate::smc_list(pdev_dict) {
539 smc_list.push(fpbus::Smc {
540 service_call_num_base: Some(smc.service_call_num_base),
541 count: Some(smc.count),
542 exclusive: Some(smc.exclusive),
543 name: smc.name,
544 ..Default::default()
545 });
546 }
547 for bm in crate::boot_metadata_list(pdev_dict) {
548 boot_metadata_list.push(fpbus::BootMetadata {
549 zbi_type: Some(bm.zbi_type),
550 zbi_extra: bm.zbi_extra.or(Some(0)),
551 ..Default::default()
552 });
553 }
554 }
555
556 if !mmio_list.is_empty() {
557 node.mmio = Some(mmio_list);
558 }
559 if !irq_list.is_empty() {
560 node.irq = Some(irq_list);
561 }
562 if !bti_list.is_empty() {
563 node.bti = Some(bti_list);
564 }
565 if !smc_list.is_empty() {
566 node.smc = Some(smc_list);
567 }
568 if !boot_metadata_list.is_empty() {
569 node.boot_metadata = Some(boot_metadata_list);
570 }
571
572 let mut metadata_list = Vec::new();
573
574 if let Some(metadata) = &dev.metadata {
576 for meta in metadata {
577 let data =
578 meta.data.clone().ok_or_else(|| anyhow!("Static metadata missing data"))?;
579 metadata_list.push(fpbus::Metadata {
580 id: meta.id.clone(),
581 data: Some(data),
582 ..Default::default()
583 });
584 }
585 }
586
587 if let Some(meta) = provider_metadata.get(dev_name) {
588 metadata_list.extend(meta.clone());
589 }
590
591 if !metadata_list.is_empty() {
592 node.metadata = Some(metadata_list);
593 }
594
595 if let Some(gen_fn) = driver_power_configs.and_then(|configs| configs.get(dev_name)) {
596 let power_config = gen_fn()
597 .with_context(|| format!("Failed to generate power config for {}", dev_name))?;
598 if !power_config.is_empty() {
599 node.power_config = Some(power_config);
600 }
601 }
602
603 let mut resource_parents = Vec::new();
604 let mut generated_keys = HashSet::new();
605 if let Some(aggregates) = &config.aggregates {
606 for (agg_idx, agg) in aggregates.iter().enumerate() {
607 if agg.provider.as_deref().is_some_and(|p| disabled_devices.contains(p)) {
608 continue;
609 }
610 if let Some(resources) = &agg.resources {
611 for res in resources {
612 if res.node.as_deref() == dev.name.as_deref() {
613 let parent_and_key = if (agg.service.as_deref()
614 == Some("fuchsia.hardware.platform.device.Service")
615 || agg.service.as_deref()
616 == Some("fuchsia.hardware.interrupt.ControllerRegistryService"))
617 && dev.compatible.is_some()
618 {
619 None
620 } else {
621 generate_parent_spec_generic(
622 agg.provider.as_deref().unwrap_or(""),
623 get_aggregate_id(
624 agg,
625 config.devices.as_deref().unwrap_or(&[]),
626 agg_idx as u32,
627 ),
628 agg.service.as_deref().unwrap_or(""),
629 res,
630 parser_config,
631 )?
632 };
633 if let Some((parent, key)) = parent_and_key {
634 if generated_keys.insert(key) {
635 if matches!(
636 agg.provider.as_deref(),
637 Some("parent") | Some("pdev")
638 ) {
639 resource_parents.insert(0, parent);
640 } else {
641 resource_parents.push(parent);
642 }
643 }
644 }
645 }
646 }
647 }
648 }
649 }
650
651 if resource_parents.is_empty() && dev.driver_host.is_some() {
652 node.driver_host = dev.driver_host.clone();
653 }
654
655 let mut spec = fdf_framework::CompositeNodeSpec {
656 name: dev.name.clone(),
657 driver_host: dev.driver_host.clone(),
658 ..Default::default()
659 };
660
661 let mut parents2 = Vec::new();
662
663 if let Some(compatible) = &dev.compatible {
665 let pdev_parent = fdf_framework::ParentSpec2 {
666 bind_rules: vec![
667 make_accept_bind_rule(
668 "fuchsia.BIND_PROTOCOL",
669 property_int(BIND_PROTOCOL_DEVICE),
670 ),
671 make_accept_bind_rule(
672 "fuchsia.BIND_PLATFORM_DEV_VID",
673 property_int(BIND_PLATFORM_DEV_VID_GENERIC),
674 ),
675 make_accept_bind_rule(
676 "fuchsia.BIND_PLATFORM_DEV_DID",
677 property_int(BIND_PLATFORM_DEV_DID_DEVICETREE),
678 ),
679 make_accept_bind_rule(
680 "fuchsia.BIND_PLATFORM_DEV_INSTANCE_ID",
681 property_int(instance_id),
682 ),
683 make_accept_bind_rule("fuchsia.COMPATIBLE", property_string(compatible)),
684 ],
685 properties: vec![
686 make_property2("fuchsia.BIND_PROTOCOL", property_int(BIND_PROTOCOL_DEVICE)),
691 make_property2(
692 "fuchsia.BIND_PLATFORM_DEV_VID",
693 property_int(BIND_PLATFORM_DEV_VID_GENERIC),
694 ),
695 make_property2(
696 "fuchsia.BIND_PLATFORM_DEV_DID",
697 property_int(BIND_PLATFORM_DEV_DID_DEVICETREE),
698 ),
699 make_property2(
700 "fuchsia.BIND_PLATFORM_DEV_INSTANCE_ID",
701 property_int(instance_id),
702 ),
703 make_property2("fuchsia.COMPATIBLE", property_string(compatible)),
704 make_property2(
705 "fuchsia.devicetree.FIRST_COMPATIBLE",
706 property_string(compatible),
707 ),
708 make_property2(
709 "fuchsia.Service",
710 property_string("fuchsia.hardware.platform.device.Service"),
711 ),
712 ],
713 };
714 parents2.push(pdev_parent);
715 }
716
717 parents2.extend(resource_parents);
718
719 if !parents2.is_empty() {
720 spec.parents2 = Some(parents2);
721
722 log::debug!(
723 "DML-CONFIG: Adding composite spec: name={:?}, parents2={:?}",
724 spec.name,
725 spec.parents2
726 );
727 composite_manager
728 .add_spec_with(spec)
729 .await
730 .context("AddSpec request failed")?
731 .map_err(|e| anyhow!("AddSpec failed: {e:?}"))?;
732 }
733
734 if dev.compatible.is_some() {
735 log::debug!("DML-CONFIG: Adding node: {}", dev_name);
736 pbus.node_add(node)
737 .await
738 .context("NodeAdd request failed")?
739 .map_err(|e| e.err().unwrap_or(zx::Status::INTERNAL))
740 .context("NodeAdd failed")?;
741 }
742 }
743
744 Ok(())
745}
746
747async fn register_iommus(
749 pbus: &fidl_next::Client<fpbus::PlatformBus, DriverChannel>,
750 iommus: Option<&[Iommu]>,
751) -> anyhow::Result<()> {
752 let Some(iommus) = iommus else {
753 return Ok(());
754 };
755
756 let futures = iommus
757 .iter()
758 .map(|iommu| {
759 let iommu_name = iommu.name.as_deref().unwrap_or("unnamed");
760 let iommu_id = iommu.id.with_context(|| format!("IOMMU {iommu_name:?} missing id"))?;
761 let fpbus_iommu = match &iommu.iommu_type {
762 Some(IommuType::ArmSmmu(arm_smmu)) => {
763 fpbus::Iommu::ArmSmmu(fpbus::ArmSmmu { base_address: arm_smmu.base_address })
764 }
765 Some(IommuType::StubIommu(_)) | None => fpbus::Iommu::StubIommu(()),
766 _ => {
767 bail!("Unsupported IOMMU type for IOMMU {iommu_name:?}");
768 }
769 };
770 let future = async move {
771 log::info!("Registering IOMMU '{iommu_name}' (id: {iommu_id})");
772 pbus.register_iommu(iommu_id, &fpbus_iommu)
773 .await
774 .with_context(|| {
775 format!(
776 "Failed to send RegisterIommu FIDL request for IOMMU {iommu_name:?}"
777 )
778 })?
779 .map_err(|e| e.err().unwrap_or(zx::Status::INTERNAL))
780 .with_context(|| format!("Failed to register IOMMU {iommu_name:?}"))?;
781 Ok::<(), anyhow::Error>(())
782 };
783 Ok(future)
784 })
785 .collect::<Result<Vec<_>, _>>()?;
786 try_join_all(futures).await?;
787 Ok(())
788}