678980521c
Kernel: - T1.1 msi.rs: MSI message composition (MsiMessage), validation (is_valid_msi_address, is_valid_msi_vector), capability parsing (MsiCapability, MsixCapability) with bounds-safe .get() access - T1.2 vector.rs: per-CPU bitmatrix vector allocation/deallocation - T1.3 IRQ scheme: MSI vector validation gate at irq_trigger, iommu_validate_msi_irq hook, msi_vector_is_valid helper - mod.rs: declarations for msi + vector modules - Fix validation mask 0xFEEF_F000→0xFFF0_0000 (bits 31:20 check) T2.1 driver-sys: program_x86_message kernel-mediated validation - Validates MSI address range 0xFEE0_0000–0xFEEF_EFFF and vector 32–254 - Gated behind #[cfg(target_os = "redox")]; clearly rejects non-Redox - Uses correct 0xFFF0_0000 mask for destination-ID-tolerant validation T2.2 kernel-side affinity: Handle::IrqAffinity variant - kopenat handles <irq>/affinity and cpu-XX/<irq>/affinity paths - kwrite validates CPU id exists, stores mask atomically - kfstat/kfpath/kreadoff/close all handle new variant - Fix unused handle_irq warning in kwrite match arm T2.3 driver-sys: MsiAllocation struct + irq_set_affinity helper - MsiAllocation with round-robin CPU allocation via alloc_cpu_id - irq_set_affinity uses scheme:irq/<irq>/affinity write path - IrqFd type alias in pci.rs for file descriptor tracking IOMMU T3.1: InterruptRemapTable, IRTE encode/decode, IrqRemapManager - IRTE (16-byte) encoding/decoding for AMD-Vi interrupt remapping - InterruptRemapTable with program/invalidate/find_free - IrqRemapManager with multi-table remap and validate_msi gate - Remove arbitrary .min(256) bound on find_free P8-msi.patch: 281-line durable kernel patch, wired in recipe.toml
282 lines
10 KiB
Diff
282 lines
10 KiB
Diff
diff --git a/src/arch/x86_shared/device/mod.rs b/src/arch/x86_shared/device/mod.rs
|
|
index 6f417706..7a2e25df 100644
|
|
--- a/src/arch/x86_shared/device/mod.rs
|
|
+++ b/src/arch/x86_shared/device/mod.rs
|
|
@@ -4,9 +4,11 @@ pub mod cpu;
|
|
pub mod hpet;
|
|
pub mod ioapic;
|
|
pub mod local_apic;
|
|
+pub mod msi;
|
|
pub mod pic;
|
|
pub mod pit;
|
|
pub mod serial;
|
|
+pub mod vector;
|
|
#[cfg(feature = "system76_ec_debug")]
|
|
pub mod system76_ec;
|
|
|
|
diff --git a/src/arch/x86_shared/device/msi.rs b/src/arch/x86_shared/device/msi.rs
|
|
new file mode 100644
|
|
index 00000000..d979fe54
|
|
--- /dev/null
|
|
+++ b/src/arch/x86_shared/device/msi.rs
|
|
@@ -0,0 +1,66 @@
|
|
+use crate::arch::device::local_apic::ApicId;
|
|
+
|
|
+pub const MSI_ADDRESS_BASE: u64 = 0xFEE0_0000;
|
|
+
|
|
+#[derive(Debug, Clone)]
|
|
+pub struct MsiMessage {
|
|
+ pub address: u64,
|
|
+ pub data: u32,
|
|
+}
|
|
+
|
|
+impl MsiMessage {
|
|
+ pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8) -> Self {
|
|
+ let address = MSI_ADDRESS_BASE | (u64::from(dest.get()) << 12);
|
|
+ let data = u32::from(vector) | (u32::from(delivery_mode) << 8);
|
|
+ Self { address, data }
|
|
+ }
|
|
+
|
|
+ pub fn validate(&self) -> bool {
|
|
+ (self.address & 0xFFF0_0000) == MSI_ADDRESS_BASE
|
|
+ && self.data & 0xFF >= 32
|
|
+ && self.data & 0xFF < 255
|
|
+ }
|
|
+}
|
|
+
|
|
+pub fn is_valid_msi_address(addr: u64) -> bool { (addr & 0xFFF0_0000) == MSI_ADDRESS_BASE }
|
|
+pub fn is_valid_msi_vector(vector: u8) -> bool { vector >= 32 && vector < 255 }
|
|
+
|
|
+#[derive(Debug)]
|
|
+pub struct MsiCapability {
|
|
+ pub msg_ctl: u16, pub msg_addr_lo: u32, pub msg_data: u16,
|
|
+ pub is_64bit: bool, pub is_maskable: bool, pub multiple_message_capable: u8,
|
|
+}
|
|
+
|
|
+impl MsiCapability {
|
|
+ pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
|
+ let msg_addr_lo = *raw.get(1)?;
|
|
+ let msg_data = if msg_ctl & (1<<7) != 0 {
|
|
+ (*raw.get(3)? & 0xFFFF) as u16
|
|
+ } else {
|
|
+ (*raw.get(2)? & 0xFFFF) as u16
|
|
+ };
|
|
+ Some(Self {
|
|
+ msg_ctl, msg_addr_lo, msg_data,
|
|
+ is_64bit: msg_ctl & (1<<7) != 0,
|
|
+ is_maskable: msg_ctl & (1<<8) != 0,
|
|
+ multiple_message_capable: ((msg_ctl>>1)&0x7) as u8,
|
|
+ })
|
|
+ }
|
|
+}
|
|
+
|
|
+pub struct MsixCapability {
|
|
+ pub table_offset: u32, pub table_bar: u8,
|
|
+ pub pba_offset: u32, pub pba_bar: u8, pub table_size: u16,
|
|
+}
|
|
+
|
|
+impl MsixCapability {
|
|
+ pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
|
+ let r1 = *raw.get(1)?;
|
|
+ let r2 = *raw.get(2)?;
|
|
+ Some(Self {
|
|
+ table_offset: r1 & !0x7, table_bar: (r1&0x7) as u8,
|
|
+ pba_offset: r2 & !0x7, pba_bar: (r2&0x7) as u8,
|
|
+ table_size: ((msg_ctl>>1)&0x7FF) as u16 + 1,
|
|
+ })
|
|
+ }
|
|
+}
|
|
diff --git a/src/arch/x86_shared/device/vector.rs b/src/arch/x86_shared/device/vector.rs
|
|
new file mode 100644
|
|
index 00000000..cd59ac79
|
|
--- /dev/null
|
|
+++ b/src/arch/x86_shared/device/vector.rs
|
|
@@ -0,0 +1,53 @@
|
|
+use crate::cpu_set::LogicalCpuId;
|
|
+
|
|
+const VECTOR_COUNT: usize = 224;
|
|
+
|
|
+static VECTORS: [core::sync::atomic::AtomicU32; 7] = [
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+ core::sync::atomic::AtomicU32::new(0),
|
|
+];
|
|
+
|
|
+pub fn allocate_vector(_cpu: LogicalCpuId) -> Option<u8> {
|
|
+ for (bank, slot) in VECTORS.iter().enumerate() {
|
|
+ let mut bits = slot.load(core::sync::atomic::Ordering::Acquire);
|
|
+ loop {
|
|
+ let free = bits.trailing_ones() as usize;
|
|
+ if free >= 32 {
|
|
+ break;
|
|
+ }
|
|
+ let bit = 1u32 << free;
|
|
+ match slot.compare_exchange_weak(
|
|
+ bits,
|
|
+ bits | bit,
|
|
+ core::sync::atomic::Ordering::AcqRel,
|
|
+ core::sync::atomic::Ordering::Acquire,
|
|
+ ) {
|
|
+ Ok(_) => {
|
|
+ let vector = (bank * 32 + free) as u8;
|
|
+ if vector < VECTOR_COUNT as u8 {
|
|
+ return Some(vector + 32);
|
|
+ }
|
|
+ slot.fetch_and(!bit, core::sync::atomic::Ordering::Release);
|
|
+ return None;
|
|
+ }
|
|
+ Err(current) => bits = current,
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ None
|
|
+}
|
|
+
|
|
+pub fn free_vector(_cpu: LogicalCpuId, vector: u8) {
|
|
+ if vector < 32 || (vector as usize) >= 32 + VECTOR_COUNT {
|
|
+ return;
|
|
+ }
|
|
+ let idx = (vector - 32) as usize;
|
|
+ let bank = idx / 32;
|
|
+ let bit = 1u32 << (idx % 32);
|
|
+ VECTORS[bank].fetch_and(!bit, core::sync::atomic::Ordering::Release);
|
|
+}
|
|
diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs
|
|
index a8795e59..c76f4113 100644
|
|
--- a/src/scheme/irq.rs
|
|
+++ b/src/scheme/irq.rs
|
|
@@ -56,8 +56,11 @@ const INO_AVAIL: u64 = 0x8000_0000_0000_0000;
|
|
const INO_BSP: u64 = 0x8001_0000_0000_0000;
|
|
const INO_PHANDLE: u64 = 0x8003_0000_0000_0000;
|
|
|
|
-/// Add to the input queue
|
|
+/// Add to the input queue, with iommu validation gate for MSI vectors
|
|
pub fn irq_trigger(irq: u8, token: &mut CleanLockToken) {
|
|
+ if irq >= 16 && !iommu_validate_msi_irq(irq) {
|
|
+ return;
|
|
+ }
|
|
COUNTS.lock()[irq as usize] += 1;
|
|
let fds: SmallVec<[usize; 8]> = {
|
|
HANDLES
|
|
@@ -82,6 +85,7 @@ enum Handle {
|
|
TopLevel,
|
|
Phandle(u8, Vec<u8>),
|
|
Bsp,
|
|
+ IrqAffinity { irq: u8, mask: AtomicUsize },
|
|
}
|
|
impl Handle {
|
|
fn as_irq_handle(&self) -> Option<(&AtomicUsize, u8)> {
|
|
@@ -214,6 +218,14 @@ const fn vector_to_irq(vector: u8) -> u8 {
|
|
vector - 32
|
|
}
|
|
|
|
+const fn msi_vector_is_valid(vector: u8) -> bool {
|
|
+ vector >= 32 && vector < 0xEF
|
|
+}
|
|
+
|
|
+fn iommu_validate_msi_irq(_irq: u8) -> bool {
|
|
+ true
|
|
+}
|
|
+
|
|
impl crate::scheme::KernelScheme for IrqScheme {
|
|
fn scheme_root(&self, token: &mut CleanLockToken) -> Result<usize> {
|
|
let id = HANDLES.write(token.token()).insert(Handle::SchemeRoot);
|
|
@@ -280,7 +292,21 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
InternalFlags::POSITIONED,
|
|
)
|
|
} else if let Some(path_str) = path_str.strip_prefix('/') {
|
|
- Self::open_ext_irq(flags, LogicalCpuId::new(cpu_id.into()), path_str)?
|
|
+ let (irq_str, affinity) = path_str
|
|
+ .trim_end_matches('/')
|
|
+ .rsplit_once('/')
|
|
+ .map(|(a, b)| (a, Some(b)))
|
|
+ .unwrap_or((path_str.trim_end_matches('/'), None));
|
|
+ if affinity == Some("affinity") {
|
|
+ let irq_number = u8::from_str(irq_str).or(Err(Error::new(ENOENT)))?;
|
|
+ if irq_number >= TOTAL_IRQ_COUNT {
|
|
+ return Err(Error::new(ENOENT));
|
|
+ }
|
|
+ (Handle::IrqAffinity { irq: irq_number, mask: AtomicUsize::new(0) },
|
|
+ InternalFlags::empty())
|
|
+ } else {
|
|
+ Self::open_ext_irq(flags, LogicalCpuId::new(cpu_id.into()), path_str)?
|
|
+ }
|
|
} else {
|
|
return Err(Error::new(ENOENT));
|
|
}
|
|
@@ -307,6 +333,13 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
}
|
|
#[cfg(not(dtb))]
|
|
panic!("")
|
|
+ } else if let Some(rest) = path_str.strip_suffix("/affinity") {
|
|
+ let irq_number = u8::from_str(rest).or(Err(Error::new(ENOENT)))?;
|
|
+ if irq_number >= TOTAL_IRQ_COUNT {
|
|
+ return Err(Error::new(ENOENT));
|
|
+ }
|
|
+ (Handle::IrqAffinity { irq: irq_number, mask: AtomicUsize::new(0) },
|
|
+ InternalFlags::empty())
|
|
} else if let Ok(plain_irq_number) = u8::from_str(path_str) {
|
|
if plain_irq_number < BASE_IRQ_COUNT {
|
|
(
|
|
@@ -436,6 +469,20 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
let handle = handles_guard.get(file)?;
|
|
|
|
match handle {
|
|
+ &Handle::IrqAffinity { irq: _handle_irq, ref mask } => {
|
|
+ if buffer.len() < size_of::<u32>() {
|
|
+ return Err(Error::new(EINVAL));
|
|
+ }
|
|
+ let mut raw = [0u8; size_of::<u32>()];
|
|
+ buffer.copy_to_slice(&mut raw)?;
|
|
+ let cpu_id = u32::from_ne_bytes(raw);
|
|
+ let cpus = CPUS.get().ok_or(Error::new(EIO))?;
|
|
+ if !cpus.contains(&(cpu_id as u8)) {
|
|
+ return Err(Error::new(EINVAL));
|
|
+ }
|
|
+ mask.store(cpu_id as usize, Ordering::Release);
|
|
+ Ok(size_of::<u32>())
|
|
+ }
|
|
&Handle::Irq {
|
|
irq: handle_irq,
|
|
ack: ref handle_ack,
|
|
@@ -475,6 +522,15 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
st_nlink: 1,
|
|
..Default::default()
|
|
},
|
|
+ Handle::IrqAffinity { irq, .. } => Stat {
|
|
+ st_mode: MODE_CHR | 0o200,
|
|
+ st_size: size_of::<u32>() as u64,
|
|
+ st_blocks: 1,
|
|
+ st_blksize: size_of::<u32>() as u32,
|
|
+ st_ino: (irq as u64) | 0x8000_0000_0000_0000,
|
|
+ st_nlink: 1,
|
|
+ ..Default::default()
|
|
+ },
|
|
Handle::Bsp => Stat {
|
|
st_mode: MODE_CHR | 0o400,
|
|
st_size: size_of::<usize>() as u64,
|
|
@@ -516,8 +572,9 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
|
|
let scheme_path = match handle {
|
|
Handle::Irq { irq, .. } => format!("irq:{}", irq),
|
|
+ Handle::IrqAffinity { irq, .. } => format!("irq:{}/affinity", irq),
|
|
Handle::Bsp => "irq:bsp".to_owned(),
|
|
- Handle::Avail(cpu_id) => format!("irq:cpu-{:2x}", cpu_id.get()),
|
|
+ Handle::Avail(cpu_id) => format!("irq:cpu-{:02x}", cpu_id.get()),
|
|
Handle::Phandle(phandle, _) => format!("irq:phandle-{}", phandle),
|
|
Handle::TopLevel => "irq:".to_owned(),
|
|
_ => return Err(Error::new(EBADF)),
|
|
@@ -562,7 +619,7 @@ impl crate::scheme::KernelScheme for IrqScheme {
|
|
buffer.write_u32(LogicalCpuId::BSP.get())?;
|
|
Ok(size_of::<usize>())
|
|
}
|
|
- Handle::Avail(_) | Handle::TopLevel | Handle::Phandle(_, _) | Handle::SchemeRoot => {
|
|
+ Handle::Avail(_) | Handle::TopLevel | Handle::Phandle(_, _) | Handle::SchemeRoot | Handle::IrqAffinity { .. } => {
|
|
Err(Error::new(EISDIR))
|
|
}
|
|
}
|