1use anyhow::Context;
6use fidl_fuchsia_board_dml_config as fbdc;
7use fidl_fuchsia_driver_metadata as fdr;
8
9pub use fbdc::{
11 AggregateEntry, ArmSmmu, BoardConfig, Device, Iommu, IommuType, ResourceEntry, StaticMetadata,
12 StubIommu,
13};
14
15#[derive(Debug, Clone, Default)]
16pub struct Mmio {
17 pub name: Option<String>,
18 pub base: u64,
19 pub length: u64,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct Irq {
24 pub name: Option<String>,
25 pub number: u32,
26 pub mode: Option<String>,
27 pub wake_vector: Option<bool>,
28 pub controller: Option<u32>,
29}
30
31#[derive(Debug, Clone, Default)]
32pub struct Bti {
33 pub id: u32,
34 pub name: Option<String>,
35 pub iommu_id: u32,
36}
37
38#[derive(Debug, Clone, Default)]
39pub struct Smc {
40 pub service_call_num_base: u32,
41 pub count: u32,
42 pub exclusive: bool,
43 pub name: Option<String>,
44}
45
46#[derive(Debug, Clone, Default)]
47pub struct BootMetadata {
48 pub zbi_type: u32,
49 pub zbi_extra: Option<u32>,
50}
51
52pub fn find_value<'a>(dict: &'a fdr::Dictionary, key: &str) -> Option<&'a fdr::DictionaryValue> {
54 dict.entries.as_ref()?.iter().find(|e| e.key == key).map(|e| &e.value)
55}
56
57pub fn get_int64(dict: &fdr::Dictionary, key: &str) -> Option<i64> {
58 match find_value(dict, key)? {
59 fdr::DictionaryValue::Int64(i) => Some(*i),
60 _ => None,
61 }
62}
63
64pub fn get_uint32(dict: &fdr::Dictionary, key: &str) -> Option<u32> {
65 get_int64(dict, key).and_then(|i| u32::try_from(i).ok())
66}
67
68pub fn get_uint64(dict: &fdr::Dictionary, key: &str) -> Option<u64> {
69 get_int64(dict, key).map(|i| i as u64)
70}
71
72pub fn get_string(dict: &fdr::Dictionary, key: &str) -> Option<String> {
73 match find_value(dict, key)? {
74 fdr::DictionaryValue::Str(s) => Some(s.clone()),
75 _ => None,
76 }
77}
78
79pub fn get_bool(dict: &fdr::Dictionary, key: &str) -> Option<bool> {
80 match find_value(dict, key)? {
81 fdr::DictionaryValue::Boolean(b) => Some(*b),
82 _ => None,
83 }
84}
85
86pub fn mmio_list(dict: &fdr::Dictionary) -> Vec<Mmio> {
88 let mut list = Vec::new();
89 for i in 0.. {
90 let prefix = format!("mmio.{}", i);
91 let base = get_uint64(dict, &format!("{}.base", prefix))
92 .or_else(|| get_uint64(dict, &format!("{}.address", prefix)));
93 if let Some(base) = base {
94 let length = get_uint64(dict, &format!("{}.length", prefix))
95 .or_else(|| get_uint64(dict, &format!("{}.size", prefix)))
96 .unwrap_or(0);
97 let name = get_string(dict, &format!("{}.name", prefix));
98 list.push(Mmio { name, base, length });
99 } else {
100 break;
101 }
102 }
103 list
104}
105
106pub fn irq_list(dict: &fdr::Dictionary) -> Vec<Irq> {
107 let mut list = Vec::new();
108 for i in 0.. {
109 let prefix = format!("interrupts.{}", i);
110 if let Some(number) = get_uint32(dict, &format!("{}.number", prefix)) {
111 let name = get_string(dict, &format!("{}.name", prefix));
112 let mode = get_string(dict, &format!("{}.mode", prefix));
113 let wake_vector = get_bool(dict, &format!("{}.wake_vector", prefix));
114 let controller = get_uint32(dict, &format!("{}.controller", prefix));
115 list.push(Irq { name, number, mode, wake_vector, controller });
116 } else {
117 break;
118 }
119 }
120 list
121}
122
123pub fn bti_list(dict: &fdr::Dictionary) -> anyhow::Result<Vec<Bti>> {
124 let mut list = Vec::new();
125 let entries = dict.entries.as_deref().unwrap_or(&[]);
126 for i in 0.. {
127 let prefix = format!("btis.{i}.");
128 if !entries.iter().any(|e| e.key.starts_with(&prefix)) {
129 break;
130 }
131
132 let id = get_uint32(dict, &format!("btis.{i}.id"))
133 .with_context(|| format!("BTI at index {i} is missing required \"id\""))?;
134 let iommu_id = get_uint32(dict, &format!("btis.{i}.iommu_id"))
135 .with_context(|| format!("BTI with ID {id} at index {i} is missing \"iommu_id\""))?;
136 let name = get_string(dict, &format!("btis.{i}.name"));
137 list.push(Bti { id, name, iommu_id });
138 }
139 Ok(list)
140}
141
142pub fn smc_list(dict: &fdr::Dictionary) -> Vec<Smc> {
143 let mut list = Vec::new();
144 for i in 0.. {
145 let prefix = format!("smcs.{}", i);
146 if let Some(service_call_num_base) =
147 get_uint32(dict, &format!("{}.service_call_num_base", prefix))
148 {
149 let count = get_uint32(dict, &format!("{}.count", prefix)).unwrap_or(0);
150 let exclusive = get_bool(dict, &format!("{}.exclusive", prefix)).unwrap_or(false);
151 let name = get_string(dict, &format!("{}.name", prefix));
152 list.push(Smc { service_call_num_base, count, exclusive, name });
153 } else {
154 break;
155 }
156 }
157 list
158}
159
160pub fn boot_metadata_list(dict: &fdr::Dictionary) -> Vec<BootMetadata> {
161 let mut list = Vec::new();
162 for i in 0.. {
163 let prefix = format!("boot_metadata.{}", i);
164 if let Some(zbi_type) = get_uint32(dict, &format!("{}.zbi_type", prefix)) {
165 let zbi_extra = get_uint32(dict, &format!("{}.zbi_extra", prefix));
166 list.push(BootMetadata { zbi_type, zbi_extra });
167 } else {
168 break;
169 }
170 }
171 list
172}
173
174pub fn pdev_constraints<'a>(
175 config: &'a BoardConfig,
176 node_name: &str,
177) -> Option<&'a fdr::Dictionary> {
178 config
179 .aggregates
180 .as_ref()?
181 .iter()
182 .find(|agg| {
183 agg.provider.as_deref() == Some("pdev")
184 && agg.service.as_deref() == Some("fuchsia.hardware.platform.device.Service")
185 })
186 .and_then(|agg| {
187 agg.resources
188 .as_ref()?
189 .iter()
190 .find(|res| res.node.as_deref() == Some(node_name))
191 .and_then(|res| res.constraint.as_ref())
192 })
193}
194
195pub fn is_node_force_enabled(dev_name: &str, enabled_nodes: &[String]) -> bool {
196 enabled_nodes.iter().any(|n| {
197 n == dev_name
198 || n.trim_start_matches('/').replace('@', "-") == dev_name
199 || n.rsplit('/').next().unwrap_or(n).replace('@', "-") == dev_name
200 })
201}
202
203pub fn is_device_disabled(dev: &Device, enabled_nodes: &[String]) -> bool {
204 if !dev.disabled.unwrap_or(false) {
205 return false;
206 }
207 let name = dev.name.as_deref().unwrap_or("");
208 !is_node_force_enabled(name, enabled_nodes)
209}
210
211#[cfg(target_os = "fuchsia")]
212pub use board_structured_config::Config as StructuredConfig;
213
214#[cfg(target_os = "fuchsia")]
215pub mod parser;
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_is_device_disabled_and_runtime_override() {
223 let dev_enabled = Device { name: Some("dev_a".to_string()), ..Default::default() };
224 let dev_explicit_enabled =
225 Device { name: Some("dev_b".to_string()), disabled: Some(false), ..Default::default() };
226 let dev_disabled = Device {
227 name: Some("pcie-c500000".to_string()),
228 disabled: Some(true),
229 ..Default::default()
230 };
231
232 assert!(!is_device_disabled(&dev_enabled, &[]));
233 assert!(!is_device_disabled(&dev_explicit_enabled, &[]));
234 assert!(is_device_disabled(&dev_disabled, &[]));
235
236 assert!(!is_device_disabled(&dev_disabled, &["pcie-c500000".to_string()]));
238
239 assert!(!is_device_disabled(&dev_disabled, &["/pcie@c500000".to_string()]));
241
242 assert!(is_device_disabled(&dev_disabled, &["other-node".to_string()]));
244 }
245
246 #[test]
247 fn test_bti_list() {
248 let dict = fdr::Dictionary {
249 entries: Some(vec![
250 fdr::DictionaryEntry {
251 key: "btis.0.id".to_string(),
252 value: fdr::DictionaryValue::Int64(1),
253 },
254 fdr::DictionaryEntry {
255 key: "btis.0.iommu_id".to_string(),
256 value: fdr::DictionaryValue::Int64(10),
257 },
258 fdr::DictionaryEntry {
259 key: "btis.0.name".to_string(),
260 value: fdr::DictionaryValue::Str("dma_bti".to_string()),
261 },
262 fdr::DictionaryEntry {
263 key: "btis.1.id".to_string(),
264 value: fdr::DictionaryValue::Int64(2),
265 },
266 fdr::DictionaryEntry {
267 key: "btis.1.iommu_id".to_string(),
268 value: fdr::DictionaryValue::Int64(0),
269 },
270 ]),
271 ..Default::default()
272 };
273
274 let btis = bti_list(&dict).unwrap();
275 assert_eq!(btis.len(), 2);
276 assert_eq!(btis[0].id, 1);
277 assert_eq!(btis[0].name.as_deref(), Some("dma_bti"));
278 assert_eq!(btis[0].iommu_id, 10);
279 assert_eq!(btis[1].id, 2);
280 assert_eq!(btis[1].name, None);
281 assert_eq!(btis[1].iommu_id, 0);
282
283 let dict_missing_iommu = fdr::Dictionary {
284 entries: Some(vec![fdr::DictionaryEntry {
285 key: "btis.0.id".to_string(),
286 value: fdr::DictionaryValue::Int64(1),
287 }]),
288 ..Default::default()
289 };
290 assert!(bti_list(&dict_missing_iommu).is_err());
291
292 let dict_missing_id = fdr::Dictionary {
293 entries: Some(vec![
294 fdr::DictionaryEntry {
295 key: "btis.0.name".to_string(),
296 value: fdr::DictionaryValue::Str("dma_bti".to_string()),
297 },
298 fdr::DictionaryEntry {
299 key: "btis.0.iommu_id".to_string(),
300 value: fdr::DictionaryValue::Int64(10),
301 },
302 ]),
303 ..Default::default()
304 };
305 assert!(bti_list(&dict_missing_id).is_err());
306 }
307}