Remap PCI IRQs as instructed in FDT

This commit is contained in:
Andrey Turkin
2024-10-18 05:14:37 +03:00
parent 67f4ca3d73
commit 8152de14a9
3 changed files with 120 additions and 16 deletions
+71 -12
View File
@@ -10,8 +10,17 @@ use fallback::Pci;
mod fallback;
pub struct InterruptMap {
pub addr: [u32; 3],
pub interrupt: u32,
pub parent_phandle: u32,
pub parent_interrupt: [u32; 3],
}
// https://elinux.org/Device_Tree_Usage has a lot of useful information
fn locate_ecam_dtb<T>(f: impl FnOnce(PcieAllocs<'_>) -> io::Result<T>) -> io::Result<T> {
fn locate_ecam_dtb<T>(
f: impl FnOnce(PcieAllocs<'_>, Vec<InterruptMap>, [u32; 4]) -> io::Result<T>,
) -> io::Result<T> {
let dtb = fs::read("kernel.dtb:")?;
let dt = Fdt::new(&dtb).map_err(|err| {
io::Error::new(
@@ -36,15 +45,53 @@ fn locate_ecam_dtb<T>(f: impl FnOnce(PcieAllocs<'_>) -> io::Result<T>) -> io::Re
let start_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[0..4]).unwrap());
let end_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[4..8]).unwrap());
// FIXME respect the BAR remappings in the ranges property
// address-cells == 3, size-cells == 2, interrupt-cells == 1
let mut interrupt_map_data = node
.property("interrupt-map")
.unwrap()
.value
.chunks_exact(4)
.map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap()));
let mut interrupt_map = Vec::<InterruptMap>::new();
while let Ok([addr1, addr2, addr3, int1, phandle]) = interrupt_map_data.next_chunk::<5>() {
let parent = dt.find_phandle(phandle).unwrap();
let interrupt_cells = parent.interrupt_cells().unwrap();
let parent_interrupt = match interrupt_cells {
1 if let Some(a) = interrupt_map_data.next() => [a, 0, 0],
2 if let Ok([a, b]) = interrupt_map_data.next_chunk::<2>() => [a, b, 0],
2 if let Ok([a, b, c]) = interrupt_map_data.next_chunk::<3>() => [a, b, c],
_ => break,
};
interrupt_map.push(InterruptMap {
addr: [addr1, addr2, addr3],
interrupt: int1,
parent_phandle: phandle,
parent_interrupt,
});
}
f(PcieAllocs(&[PcieAlloc {
base_addr: address,
seg_group_num: 0,
start_bus: start_bus.try_into().unwrap(),
end_bus: end_bus.try_into().unwrap(),
_rsvd: [0; 4],
}]))
let interrupt_map_mask = if let Some(interrupt_mask_node) = node.property("interrupt-map-mask")
{
let mut cells = interrupt_mask_node
.value
.chunks_exact(4)
.map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap()));
cells.next_chunk::<4>().unwrap().to_owned()
} else {
[u32::MAX, u32::MAX, u32::MAX, u32::MAX]
};
f(
PcieAllocs(&[PcieAlloc {
base_addr: address,
seg_group_num: 0,
start_bus: start_bus.try_into().unwrap(),
end_bus: end_bus.try_into().unwrap(),
_rsvd: [0; 4],
}]),
interrupt_map,
interrupt_map_mask,
)
}
pub const MCFG_NAME: [u8; 4] = *b"MCFG";
@@ -83,7 +130,9 @@ unsafe impl plain::Plain for PcieAlloc {}
struct PcieAllocs<'a>(&'a [PcieAlloc]);
impl Mcfg {
fn with<T>(f: impl FnOnce(PcieAllocs<'_>) -> io::Result<T>) -> io::Result<T> {
fn with<T>(
f: impl FnOnce(PcieAllocs<'_>, Vec<InterruptMap>, [u32; 4]) -> io::Result<T>,
) -> io::Result<T> {
let table_dir = fs::read_dir("/scheme/acpi/tables")?;
// TODO: validate/print MCFG?
@@ -103,7 +152,7 @@ impl Mcfg {
match Mcfg::parse(&*bytes) {
Some((mcfg, allocs)) => {
log::info!("MCFG {mcfg:?} ALLOCS {allocs:?}");
return f(allocs);
return f(allocs, Vec::new(), [u32::MAX, u32::MAX, u32::MAX, u32::MAX]);
}
None => {
return Err(io::Error::new(
@@ -147,6 +196,8 @@ impl Mcfg {
pub struct Pcie {
lock: Mutex<()>,
allocs: Vec<Alloc>,
pub interrupt_map: Vec<InterruptMap>,
pub interrupt_map_mask: [u32; 4],
fallback: Pci,
}
struct Alloc {
@@ -179,13 +230,19 @@ impl Pcie {
lock: Mutex::new(()),
allocs: Vec::new(),
fallback: Pci::new(),
interrupt_map: Vec::new(),
interrupt_map_mask: [u32::MAX, u32::MAX, u32::MAX, u32::MAX],
}
}
},
}
}
fn from_allocs(allocs: PcieAllocs<'_>) -> Result<Pcie, io::Error> {
fn from_allocs(
allocs: PcieAllocs<'_>,
interrupt_map: Vec<InterruptMap>,
interrupt_map_mask: [u32; 4],
) -> Result<Pcie, io::Error> {
let mut allocs = allocs
.0
.iter()
@@ -220,6 +277,8 @@ impl Pcie {
Ok(Self {
lock: Mutex::new(()),
allocs,
interrupt_map,
interrupt_map_mask,
fallback: Pci::new(),
})
}
+20 -3
View File
@@ -4,6 +4,7 @@ use std::io::prelude::*;
use std::ptr::NonNull;
use std::{env, io};
use log::info;
use std::os::unix::io::{FromRawFd, RawFd};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -21,19 +22,35 @@ pub mod irq_helpers;
pub mod msi;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct LegacyInterruptLine(#[doc(hidden)] pub u8);
pub struct LegacyInterruptLine {
#[doc(hidden)]
pub irq: u8,
pub phandled: Option<(u32, [u32; 3])>,
}
impl LegacyInterruptLine {
/// Get an IRQ handle for this interrupt line.
pub fn irq_handle(self, driver: &str) -> File {
File::open(format!("/scheme/irq/{}", self.0))
if let Some((phandle, addr)) = self.phandled {
File::create(format!(
"/scheme/irq/phandle-{}/{},{},{}",
phandle, addr[0], addr[1], addr[2]
))
.unwrap_or_else(|err| panic!("{driver}: failed to open IRQ file: {err}"))
} else {
File::open(format!("/scheme/irq/{}", self.irq))
.unwrap_or_else(|err| panic!("{driver}: failed to open IRQ file: {err}"))
}
}
}
impl fmt::Display for LegacyInterruptLine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
if let Some((phandle, addr)) = self.phandled {
write!(f, "(phandle {}, {:?})", phandle, addr)
} else {
write!(f, "{}", self.irq)
}
}
}
+29 -1
View File
@@ -1,4 +1,6 @@
#![feature(non_exhaustive_omitted_patterns_lint)]
#![feature(iter_next_chunk)]
#![feature(if_let_guard)]
use std::fs::{metadata, read_dir, File};
use std::io::prelude::*;
@@ -134,6 +136,32 @@ fn handle_parsed_header(
}
};
let mut phandled: Option<(u32, [u32; 3])> = None;
if legacy_interrupt_enabled {
let pci_address = endpoint_header.header().address();
let dt_address = ((pci_address.bus() as u32) << 16)
| ((pci_address.device() as u32) << 11)
| ((pci_address.function() as u32) << 8);
let addr = [
dt_address & state.pcie.interrupt_map_mask[0],
0u32,
0u32,
interrupt_pin as u32 & state.pcie.interrupt_map_mask[3],
];
let mapping = state
.pcie
.interrupt_map
.iter()
.find(|x| x.addr == addr[0..3] && x.interrupt == addr[3]);
if let Some(mapping) = mapping {
debug!(
"found mapping: addr={:?} => (phandle={} irq={:?})",
addr, mapping.parent_phandle, mapping.parent_interrupt
);
phandled = Some((mapping.parent_phandle, mapping.parent_interrupt));
}
}
let capabilities = if endpoint_header.status(&state.pcie).has_capability_list() {
endpoint_header
.capabilities(&state.pcie)
@@ -154,7 +182,7 @@ fn handle_parsed_header(
bars,
addr: endpoint_header.header().address(),
legacy_interrupt_line: if legacy_interrupt_enabled {
Some(LegacyInterruptLine(irq))
Some(LegacyInterruptLine { irq, phandled })
} else {
None
},