xref: /aosp_15_r20/external/crosvm/arch/src/fdt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #[cfg(any(target_os = "android", target_os = "linux"))]
6 use std::collections::BTreeMap;
7 use std::fs::File;
8 use std::io::Read;
9 
10 use cros_fdt::apply_overlay;
11 use cros_fdt::Error;
12 use cros_fdt::Fdt;
13 #[cfg(any(target_os = "android", target_os = "linux"))]
14 use cros_fdt::Path;
15 use cros_fdt::Result;
16 #[cfg(any(target_os = "android", target_os = "linux"))]
17 use devices::IommuDevType;
18 
19 #[cfg(any(target_os = "android", target_os = "linux"))]
20 use crate::sys::linux::PlatformBusResources;
21 
22 /// Device tree overlay file
23 pub struct DtbOverlay {
24     /// Device tree overlay file to apply
25     pub file: File,
26     /// Whether to filter out nodes that do not belong to assigned VFIO devices.
27     #[cfg(any(target_os = "android", target_os = "linux"))]
28     pub do_filter: bool,
29 }
30 
31 /// Apply multiple device tree overlays to the base FDT.
32 #[cfg(not(any(target_os = "android", target_os = "linux")))]
apply_device_tree_overlays(fdt: &mut Fdt, overlays: Vec<DtbOverlay>) -> Result<()>33 pub fn apply_device_tree_overlays(fdt: &mut Fdt, overlays: Vec<DtbOverlay>) -> Result<()> {
34     for mut dtbo in overlays {
35         let mut buffer = Vec::new();
36         dtbo.file
37             .read_to_end(&mut buffer)
38             .map_err(Error::FdtIoError)?;
39         let overlay = Fdt::from_blob(buffer.as_slice())?;
40         apply_overlay::<&str>(fdt, overlay, [])?;
41     }
42     Ok(())
43 }
44 
45 #[cfg(any(target_os = "android", target_os = "linux"))]
get_iommu_phandle( iommu_type: IommuDevType, id: Option<u32>, phandles: &BTreeMap<&str, u32>, ) -> Result<u32>46 fn get_iommu_phandle(
47     iommu_type: IommuDevType,
48     id: Option<u32>,
49     phandles: &BTreeMap<&str, u32>,
50 ) -> Result<u32> {
51     match iommu_type {
52         IommuDevType::NoIommu | IommuDevType::VirtioIommu | IommuDevType::CoIommu => None,
53         IommuDevType::PkvmPviommu => {
54             if let Some(id) = id {
55                 phandles.get(format!("pviommu{id}").as_str()).copied()
56             } else {
57                 None
58             }
59         }
60     }
61     .ok_or_else(|| Error::MissingIommuPhandle(format!("{iommu_type:?}"), id))
62 }
63 
64 // Find the device node at given path and update its `reg` and `interrupts` properties using
65 // its platform resources.
66 #[cfg(any(target_os = "android", target_os = "linux"))]
update_device_nodes( node_path: Path, fdt: &mut Fdt, resources: &PlatformBusResources, phandles: &BTreeMap<&str, u32>, ) -> Result<()>67 fn update_device_nodes(
68     node_path: Path,
69     fdt: &mut Fdt,
70     resources: &PlatformBusResources,
71     phandles: &BTreeMap<&str, u32>,
72 ) -> Result<()> {
73     const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
74 
75     let node = fdt.get_node_mut(node_path).ok_or_else(|| {
76         Error::InvalidPath(format!(
77             "cannot find FDT node for dt-symbol {}",
78             &resources.dt_symbol
79         ))
80     })?;
81     let reg_val: Vec<u64> = resources
82         .regions
83         .iter()
84         .flat_map(|(a, s)| [*a, *s].into_iter())
85         .collect();
86     let irq_val: Vec<u32> = resources
87         .irqs
88         .iter()
89         .flat_map(|(n, f)| [GIC_FDT_IRQ_TYPE_SPI, *n, *f].into_iter())
90         .collect();
91     if !reg_val.is_empty() {
92         node.set_prop("reg", reg_val)?;
93     }
94     if !irq_val.is_empty() {
95         node.set_prop("interrupts", irq_val)?;
96     }
97 
98     if !resources.iommus.is_empty() {
99         let mut iommus_val = Vec::new();
100         for (t, id, vsids) in &resources.iommus {
101             let phandle = get_iommu_phandle(*t, *id, phandles)?;
102             iommus_val.push(phandle);
103             iommus_val.extend_from_slice(vsids);
104         }
105         node.set_prop("iommus", iommus_val)?;
106     }
107 
108     Ok(())
109 }
110 
111 /// Apply multiple device tree overlays to the base FDT.
112 ///
113 /// # Arguments
114 ///
115 /// * `fdt` - The base FDT
116 /// * `overlays` - A vector of overlay files to apply
117 /// * `devices` - A vector of device resource descriptors to amend the overlay nodes with
118 #[cfg(any(target_os = "android", target_os = "linux"))]
apply_device_tree_overlays( fdt: &mut Fdt, overlays: Vec<DtbOverlay>, mut devices: Vec<PlatformBusResources>, phandles: &BTreeMap<&str, u32>, ) -> Result<()>119 pub fn apply_device_tree_overlays(
120     fdt: &mut Fdt,
121     overlays: Vec<DtbOverlay>,
122     mut devices: Vec<PlatformBusResources>,
123     phandles: &BTreeMap<&str, u32>,
124 ) -> Result<()> {
125     for mut dtbo in overlays {
126         let mut buffer = Vec::new();
127         dtbo.file
128             .read_to_end(&mut buffer)
129             .map_err(Error::FdtIoError)?;
130         let mut overlay = Fdt::from_blob(buffer.as_slice())?;
131 
132         // Find device node paths corresponding to the resources.
133         let mut node_paths = vec![];
134         let devs_in_overlay;
135         (devs_in_overlay, devices) = devices.into_iter().partition(|r| {
136             if let Ok(path) = overlay.symbol_to_path(&r.dt_symbol) {
137                 node_paths.push(path);
138                 true
139             } else {
140                 false
141             }
142         });
143 
144         // Update device nodes found in this overlay, and then apply the overlay.
145         for (path, res) in node_paths.into_iter().zip(&devs_in_overlay) {
146             update_device_nodes(path, &mut overlay, res, phandles)?;
147         }
148 
149         // Unfiltered DTBOs applied as whole.
150         if !dtbo.do_filter {
151             apply_overlay::<&str>(fdt, overlay, [])?;
152         } else if !devs_in_overlay.is_empty() {
153             apply_overlay(fdt, overlay, devs_in_overlay.iter().map(|r| &r.dt_symbol))?;
154         }
155     }
156 
157     if devices.is_empty() {
158         Ok(())
159     } else {
160         Err(Error::ApplyOverlayError(format!(
161             "labels {:#?} not found in overlay files",
162             devices.iter().map(|r| &r.dt_symbol).collect::<Vec<_>>()
163         )))
164     }
165 }
166