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