#[cfg(any(target_os = "android", target_os = "linux"))]
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use cros_fdt::apply_overlay;
use cros_fdt::Error;
use cros_fdt::Fdt;
#[cfg(any(target_os = "android", target_os = "linux"))]
use cros_fdt::Path;
use cros_fdt::Result;
#[cfg(any(target_os = "android", target_os = "linux"))]
use devices::IommuDevType;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::sys::linux::PlatformBusResources;
pub struct DtbOverlay {
pub file: File,
#[cfg(any(target_os = "android", target_os = "linux"))]
pub do_filter: bool,
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
pub fn apply_device_tree_overlays(fdt: &mut Fdt, overlays: Vec<DtbOverlay>) -> Result<()> {
for mut dtbo in overlays {
let mut buffer = Vec::new();
dtbo.file
.read_to_end(&mut buffer)
.map_err(Error::FdtIoError)?;
let overlay = Fdt::from_blob(buffer.as_slice())?;
apply_overlay::<&str>(fdt, overlay, [])?;
}
Ok(())
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn get_iommu_phandle(
iommu_type: IommuDevType,
id: Option<u32>,
phandles: &BTreeMap<&str, u32>,
) -> Result<u32> {
match iommu_type {
IommuDevType::NoIommu | IommuDevType::VirtioIommu | IommuDevType::CoIommu => None,
IommuDevType::PkvmPviommu => {
if let Some(id) = id {
phandles.get(format!("pviommu{id}").as_str()).copied()
} else {
None
}
}
}
.ok_or_else(|| Error::MissingIommuPhandle(format!("{iommu_type:?}"), id))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn update_device_nodes(
node_path: Path,
fdt: &mut Fdt,
resources: &PlatformBusResources,
phandles: &BTreeMap<&str, u32>,
) -> Result<()> {
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
let node = fdt.get_node_mut(node_path).ok_or_else(|| {
Error::InvalidPath(format!(
"cannot find FDT node for dt-symbol {}",
&resources.dt_symbol
))
})?;
let reg_val: Vec<u64> = resources
.regions
.iter()
.flat_map(|(a, s)| [*a, *s].into_iter())
.collect();
let irq_val: Vec<u32> = resources
.irqs
.iter()
.flat_map(|(n, f)| [GIC_FDT_IRQ_TYPE_SPI, *n, *f].into_iter())
.collect();
if !reg_val.is_empty() {
node.set_prop("reg", reg_val)?;
}
if !irq_val.is_empty() {
node.set_prop("interrupts", irq_val)?;
}
if !resources.iommus.is_empty() {
let mut iommus_val = Vec::new();
for (t, id, vsids) in &resources.iommus {
let phandle = get_iommu_phandle(*t, *id, phandles)?;
iommus_val.push(phandle);
iommus_val.extend_from_slice(vsids);
}
node.set_prop("iommus", iommus_val)?;
}
Ok(())
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn apply_device_tree_overlays(
fdt: &mut Fdt,
overlays: Vec<DtbOverlay>,
mut devices: Vec<PlatformBusResources>,
phandles: &BTreeMap<&str, u32>,
) -> Result<()> {
for mut dtbo in overlays {
let mut buffer = Vec::new();
dtbo.file
.read_to_end(&mut buffer)
.map_err(Error::FdtIoError)?;
let mut overlay = Fdt::from_blob(buffer.as_slice())?;
let mut node_paths = vec![];
let devs_in_overlay;
(devs_in_overlay, devices) = devices.into_iter().partition(|r| {
if let Ok(path) = overlay.symbol_to_path(&r.dt_symbol) {
node_paths.push(path);
true
} else {
false
}
});
for (path, res) in node_paths.into_iter().zip(&devs_in_overlay) {
update_device_nodes(path, &mut overlay, res, phandles)?;
}
if !dtbo.do_filter {
apply_overlay::<&str>(fdt, overlay, [])?;
} else if !devs_in_overlay.is_empty() {
apply_overlay(fdt, overlay, devs_in_overlay.iter().map(|r| &r.dt_symbol))?;
}
}
if devices.is_empty() {
Ok(())
} else {
Err(Error::ApplyOverlayError(format!(
"labels {:#?} not found in overlay files",
devices.iter().map(|r| &r.dt_symbol).collect::<Vec<_>>()
)))
}
}