From 8152de14a9c73790836dd95570a331e5abfa5f33 Mon Sep 17 00:00:00 2001 From: Andrey Turkin Date: Fri, 18 Oct 2024 05:14:37 +0300 Subject: [PATCH] Remap PCI IRQs as instructed in FDT --- pcid/src/cfg_access/mod.rs | 83 +++++++++++++++++++++++++++----- pcid/src/driver_interface/mod.rs | 23 +++++++-- pcid/src/main.rs | 30 +++++++++++- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/pcid/src/cfg_access/mod.rs b/pcid/src/cfg_access/mod.rs index 171ea66ac9..72c0c6b001 100644 --- a/pcid/src/cfg_access/mod.rs +++ b/pcid/src/cfg_access/mod.rs @@ -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(f: impl FnOnce(PcieAllocs<'_>) -> io::Result) -> io::Result { +fn locate_ecam_dtb( + f: impl FnOnce(PcieAllocs<'_>, Vec, [u32; 4]) -> io::Result, +) -> io::Result { 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(f: impl FnOnce(PcieAllocs<'_>) -> io::Result) -> 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::::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(f: impl FnOnce(PcieAllocs<'_>) -> io::Result) -> io::Result { + fn with( + f: impl FnOnce(PcieAllocs<'_>, Vec, [u32; 4]) -> io::Result, + ) -> io::Result { 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, + pub interrupt_map: Vec, + 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 { + fn from_allocs( + allocs: PcieAllocs<'_>, + interrupt_map: Vec, + interrupt_map_mask: [u32; 4], + ) -> Result { 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(), }) } diff --git a/pcid/src/driver_interface/mod.rs b/pcid/src/driver_interface/mod.rs index 6055fc74be..2b103353c5 100644 --- a/pcid/src/driver_interface/mod.rs +++ b/pcid/src/driver_interface/mod.rs @@ -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) + } } } diff --git a/pcid/src/main.rs b/pcid/src/main.rs index f8d72fc910..2b7f120b1d 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -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 },