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