1#[cfg(any(target_os = "android", target_os = "linux"))]
6use std::collections::BTreeMap;
7use std::fs::File;
8use std::io::Read;
9
10use cros_fdt::apply_overlay;
11use cros_fdt::Error;
12use cros_fdt::Fdt;
13#[cfg(any(target_os = "android", target_os = "linux"))]
14use cros_fdt::Path;
15use cros_fdt::Result;
16#[cfg(any(target_os = "android", target_os = "linux"))]
17use devices::IommuDevType;
18use vm_memory::GuestAddress;
19use vm_memory::GuestMemory;
20use vm_memory::MemoryRegionInformation;
21use vm_memory::MemoryRegionPurpose;
22
23#[cfg(any(target_os = "android", target_os = "linux"))]
24use crate::sys::linux::PlatformBusResources;
25
26pub struct DtbOverlay {
28 pub file: File,
30 #[cfg(any(target_os = "android", target_os = "linux"))]
32 pub do_filter: bool,
33}
34
35#[cfg(not(any(target_os = "android", target_os = "linux")))]
37pub fn apply_device_tree_overlays(fdt: &mut Fdt, overlays: Vec<DtbOverlay>) -> Result<()> {
38 for mut dtbo in overlays {
39 let mut buffer = Vec::new();
40 dtbo.file
41 .read_to_end(&mut buffer)
42 .map_err(Error::FdtIoError)?;
43 let overlay = Fdt::from_blob(buffer.as_slice())?;
44 apply_overlay::<&str>(fdt, overlay, [])?;
45 }
46 Ok(())
47}
48
49#[cfg(any(target_os = "android", target_os = "linux"))]
50fn get_iommu_phandle(
51 iommu_type: IommuDevType,
52 id: Option<u32>,
53 phandles: &BTreeMap<&str, u32>,
54) -> Result<u32> {
55 match iommu_type {
56 IommuDevType::NoIommu | IommuDevType::VirtioIommu | IommuDevType::CoIommu => None,
57 IommuDevType::PkvmPviommu => {
58 if let Some(id) = id {
59 phandles.get(format!("pviommu{id}").as_str()).copied()
60 } else {
61 None
62 }
63 }
64 }
65 .ok_or_else(|| Error::MissingIommuPhandle(format!("{iommu_type:?}"), id))
66}
67
68#[cfg(any(target_os = "android", target_os = "linux"))]
71fn update_device_nodes(
72 node_path: Path,
73 fdt: &mut Fdt,
74 resources: &PlatformBusResources,
75 phandles: &BTreeMap<&str, u32>,
76) -> Result<()> {
77 const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
78
79 let node = fdt.get_node_mut(node_path).ok_or_else(|| {
80 Error::InvalidPath(format!(
81 "cannot find FDT node for dt-symbol {}",
82 &resources.dt_symbol
83 ))
84 })?;
85 let reg_val: Vec<u64> = resources
86 .regions
87 .iter()
88 .flat_map(|(a, s)| [*a, *s].into_iter())
89 .collect();
90 let irq_val: Vec<u32> = resources
91 .irqs
92 .iter()
93 .flat_map(|(n, f)| [GIC_FDT_IRQ_TYPE_SPI, *n, *f].into_iter())
94 .collect();
95 if !reg_val.is_empty() {
96 node.set_prop("reg", reg_val)?;
97 }
98 if !irq_val.is_empty() {
99 node.set_prop("interrupts", irq_val)?;
100 }
101
102 if !resources.iommus.is_empty() {
103 let mut iommus_val = Vec::new();
104 for (t, id, vsids) in &resources.iommus {
105 let phandle = get_iommu_phandle(*t, *id, phandles)?;
106 iommus_val.push(phandle);
107 iommus_val.extend_from_slice(vsids);
108 }
109 node.set_prop("iommus", iommus_val)?;
110 }
111
112 Ok(())
113}
114
115#[cfg(any(target_os = "android", target_os = "linux"))]
123pub fn apply_device_tree_overlays(
124 fdt: &mut Fdt,
125 overlays: Vec<DtbOverlay>,
126 mut devices: Vec<PlatformBusResources>,
127 phandles: &BTreeMap<&str, u32>,
128) -> Result<()> {
129 for mut dtbo in overlays {
130 let mut buffer = Vec::new();
131 dtbo.file
132 .read_to_end(&mut buffer)
133 .map_err(Error::FdtIoError)?;
134 let mut overlay = Fdt::from_blob(buffer.as_slice())?;
135
136 let mut node_paths = vec![];
138 let devs_in_overlay;
139 (devs_in_overlay, devices) = devices.into_iter().partition(|r| {
140 if let Ok(path) = overlay.symbol_to_path(&r.dt_symbol) {
141 node_paths.push(path);
142 true
143 } else {
144 false
145 }
146 });
147
148 for (path, res) in node_paths.into_iter().zip(&devs_in_overlay) {
150 update_device_nodes(path, &mut overlay, res, phandles)?;
151 }
152
153 if !dtbo.do_filter {
155 apply_overlay::<&str>(fdt, overlay, [])?;
156 } else if !devs_in_overlay.is_empty() {
157 apply_overlay(fdt, overlay, devs_in_overlay.iter().map(|r| &r.dt_symbol))?;
158 }
159 }
160
161 if devices.is_empty() {
162 Ok(())
163 } else {
164 Err(Error::ApplyOverlayError(format!(
165 "labels {:#?} not found in overlay files",
166 devices.iter().map(|r| &r.dt_symbol).collect::<Vec<_>>()
167 )))
168 }
169}
170
171pub fn create_memory_node(fdt: &mut Fdt, guest_mem: &GuestMemory) -> Result<()> {
173 let mut mem_reg_prop = Vec::new();
174 let mut previous_memory_region_end = None;
175 let mut regions: Vec<MemoryRegionInformation> = guest_mem
176 .regions()
177 .filter(|region| match region.options.purpose {
178 MemoryRegionPurpose::Bios => false,
179 MemoryRegionPurpose::GuestMemoryRegion => true,
180 MemoryRegionPurpose::ProtectedFirmwareRegion => true,
181 MemoryRegionPurpose::ReservedMemory => true,
182 #[cfg(target_arch = "aarch64")]
183 MemoryRegionPurpose::StaticSwiotlbRegion => true,
184 })
185 .collect();
186 regions.sort_by(|a, b| a.guest_addr.cmp(&b.guest_addr));
187 for region in regions {
188 if let Some(previous_end) = previous_memory_region_end {
190 if region.guest_addr == previous_end {
191 *mem_reg_prop.last_mut().unwrap() += region.size as u64;
192 previous_memory_region_end =
193 Some(previous_end.checked_add(region.size as u64).unwrap());
194 continue;
195 }
196 assert!(region.guest_addr > previous_end, "Memory regions overlap");
197 }
198
199 mem_reg_prop.push(region.guest_addr.offset());
200 mem_reg_prop.push(region.size as u64);
201 previous_memory_region_end =
202 Some(region.guest_addr.checked_add(region.size as u64).unwrap());
203 }
204
205 let memory_node = fdt.root_mut().subnode_mut("memory")?;
206 memory_node.set_prop("device_type", "memory")?;
207 memory_node.set_prop("reg", mem_reg_prop)?;
208 Ok(())
209}
210
211pub struct ReservedMemoryRegion<'a> {
212 pub name: &'a str,
213 pub address: Option<GuestAddress>,
214 pub size: u64,
215 pub phandle: Option<u32>,
216 pub compatible: Option<&'a str>,
217 pub alignment: Option<u64>,
218 pub no_map: bool,
219}
220
221pub fn create_reserved_memory_node(
223 fdt: &mut Fdt,
224 reserved_regions: &[ReservedMemoryRegion],
225) -> Result<()> {
226 if reserved_regions.is_empty() {
227 return Ok(());
228 }
229
230 let resv_memory_node = fdt.root_mut().subnode_mut("reserved-memory")?;
231 resv_memory_node.set_prop("#address-cells", 0x2u32)?;
232 resv_memory_node.set_prop("#size-cells", 0x2u32)?;
233 resv_memory_node.set_prop("ranges", ())?;
234
235 for region in reserved_regions {
236 let child_node = if let Some(resv_addr) = region.address {
237 let node =
238 resv_memory_node.subnode_mut(&format!("{}@{:x}", region.name, resv_addr.0))?;
239 node.set_prop("reg", &[resv_addr.0, region.size])?;
240 node
241 } else {
242 let node = resv_memory_node.subnode_mut(region.name)?;
243 node.set_prop("size", region.size)?;
244 node
245 };
246
247 if let Some(phandle) = region.phandle {
248 child_node.set_prop("phandle", phandle)?;
249 }
250 if let Some(compatible) = region.compatible {
251 child_node.set_prop("compatible", compatible)?;
252 }
253 if let Some(alignment) = region.alignment {
254 child_node.set_prop("alignment", alignment)?;
255 }
256 if region.no_map {
257 child_node.set_prop("no-map", ())?;
258 }
259 }
260
261 Ok(())
262}
263
264pub fn reserved_memory_regions_from_guest_mem(
267 guest_mem: &GuestMemory,
268) -> Vec<ReservedMemoryRegion> {
269 guest_mem
270 .regions()
271 .filter(|region| region.options.purpose == MemoryRegionPurpose::ReservedMemory)
272 .map(|region| ReservedMemoryRegion {
273 address: Some(region.guest_addr),
274 size: region.size.try_into().unwrap(),
275 name: "reserved",
276 phandle: None,
277 compatible: None,
278 alignment: None,
279 no_map: true,
280 })
281 .collect()
282}