Merge branch 'riscv' into 'master'
Remap PCI IRQs as instructed in FDT See merge request redox-os/drivers!206
This commit is contained in:
+71
-12
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user