From 678980521c553a330b099358c9f01dec6685153a Mon Sep 17 00:00:00 2001 From: Vasilito Date: Mon, 4 May 2026 18:00:15 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20T1.1-T2.2=20MSI=20subsystem=20=E2=80=94?= =?UTF-8?q?=20kernel=20MSI,=20vectors,=20affinity,=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 /affinity and cpu-XX//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//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 --- local/patches/kernel/P8-msi.patch | 381 ++++++++++++------ .../redox-driver-sys/source/src/irq.rs | 44 +- .../redox-driver-sys/source/src/pci.rs | 2 + .../system/iommu/source/src/interrupt.rs | 2 +- local/recipes/system/iommu/source/src/main.rs | 3 + recipes/core/kernel/recipe.toml | 5 +- 6 files changed, 290 insertions(+), 147 deletions(-) diff --git a/local/patches/kernel/P8-msi.patch b/local/patches/kernel/P8-msi.patch index 0b304822..1eec140d 100644 --- a/local/patches/kernel/P8-msi.patch +++ b/local/patches/kernel/P8-msi.patch @@ -1,168 +1,281 @@ ---- /dev/null 2026-05-03 20:55:05.750445686 +0100 -+++ /mnt/data/homes/kellito/Builds/rbos/recipes/core/kernel/source/src/arch/x86_shared/device/msi.rs 2026-05-04 16:29:00.566790704 +0100 -@@ -0,0 +1,165 @@ -+// MSI/MSI-X support for x86 — kernel-level message composition and validation -+// Cross-referenced from Linux 7.0: arch/x86/kernel/apic/msi.c (391 lines) -+ +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; -+pub const MSI_ADDRESS_MASK: u64 = 0xFEEF_F000; -+const MSI_DEST_MODE_LOGICAL: u64 = 1 << 2; -+const MSI_REDIRECTION_HINT: u64 = 1 << 3; -+const MSI_DEST_MODE_PHYSICAL: u64 = 0; -+ -+#[derive(Debug, Clone, Copy)] -+pub struct MsiAddress { -+ pub raw: u64, -+} -+ -+#[derive(Debug, Clone, Copy)] -+pub struct MsiData { -+ pub raw: u32, -+} + +#[derive(Debug, Clone)] +pub struct MsiMessage { -+ pub address: MsiAddress, -+ pub data: MsiData, -+} -+ -+impl MsiAddress { -+ pub fn new(dest_apic_id: u8, redirection_hint: bool, dest_mode_logical: bool) -> Self { -+ let mut addr = MSI_ADDRESS_BASE; -+ addr |= u64::from(dest_apic_id) << 12; -+ if redirection_hint { addr |= MSI_REDIRECTION_HINT; } -+ if dest_mode_logical { addr |= MSI_DEST_MODE_LOGICAL; } -+ Self { raw: addr } -+ } -+ -+ pub fn validate(addr: u64) -> bool { -+ (addr & MSI_ADDRESS_MASK) == MSI_ADDRESS_BASE -+ } -+ -+ pub fn dest_apic_id(&self) -> u8 { -+ ((self.raw >> 12) & 0xFF) as u8 -+ } -+} -+ -+impl MsiData { -+ pub fn new(vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self { -+ let mut data: u32 = u32::from(vector); -+ data |= u32::from(delivery_mode) << 8; -+ data |= u32::from(trigger_mode) << 15; -+ Self { raw: data } -+ } -+ -+ pub fn vector(&self) -> u8 { (self.raw & 0xFF) as u8 } -+ pub fn delivery_mode(&self) -> u8 { ((self.raw >> 8) & 0x7) as u8 } -+ pub fn trigger_mode(&self) -> u8 { ((self.raw >> 15) & 0x1) as u8 } ++ pub address: u64, ++ pub data: u32, +} + +impl MsiMessage { -+ pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self { -+ let address = MsiAddress::new(dest.get() as u8, false, false); -+ let data = MsiData::new(vector, delivery_mode, trigger_mode); ++ 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 { -+ MsiAddress::validate(self.address.raw) -+ && self.data.vector() >= 32 -+ && self.data.vector() < 255 ++ (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_addr_hi: u32, -+ pub msg_data: u16, -+ pub mask_bits: u32, -+ pub pending_bits: u32, -+ pub is_64bit: bool, -+ pub is_maskable: bool, -+ pub multiple_message_capable: u8, ++ 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; 6], msg_ctl: u16) -> Self { -+ Self { -+ msg_ctl, -+ msg_addr_lo: raw[1], -+ msg_addr_hi: if msg_ctl & (1 << 7) != 0 { raw[2] } else { 0 }, -+ msg_data: if msg_ctl & (1 << 7) != 0 { -+ (raw[3] & 0xFFFF) as u16 -+ } else { -+ (raw[2] & 0xFFFF) as u16 -+ }, -+ mask_bits: if msg_ctl & (1 << 8) != 0 { -+ if msg_ctl & (1 << 7) != 0 { raw[3] >> 16 } else { raw[3] } -+ } else { 0 }, -+ pending_bits: if msg_ctl & (1 << 8) != 0 { -+ if msg_ctl & (1 << 7) != 0 { raw[4] } else { raw[4] } -+ } else { 0 }, -+ is_64bit: msg_ctl & (1 << 7) != 0, -+ is_maskable: msg_ctl & (1 << 8) != 0, -+ multiple_message_capable: ((msg_ctl >> 1) & 0x7) as u8, -+ } ++ pub fn parse(raw: &[u32], msg_ctl: u16) -> Option { ++ 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, ++ }) + } +} + -+#[derive(Debug)] +pub struct MsixCapability { -+ pub msg_ctl: u16, -+ pub table_offset: u32, -+ pub table_bar: u8, -+ pub pba_offset: u32, -+ pub pba_bar: u8, -+ pub table_size: u16, ++ 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; 3], msg_ctl: u16) -> Self { -+ Self { -+ msg_ctl, -+ table_offset: raw[1] & !0x7, -+ table_bar: (raw[1] & 0x7) as u8, -+ pba_offset: raw[2] & !0x7, -+ pba_bar: (raw[2] & 0x7) as u8, -+ table_size: ((msg_ctl >> 1) & 0x7FF) as u16 + 1, ++ pub fn parse(raw: &[u32], msg_ctl: u16) -> Option { ++ 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 { ++ 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 is_valid_msi_address(addr: u64) -> bool { -+ MsiAddress::validate(addr) -+} -+ -+pub fn is_valid_msi_vector(vector: u8) -> bool { -+ vector >= 32 && vector < 255 -+} -+ -+#[cfg(test)] -+mod tests { -+ use super::*; -+ -+ #[test] -+ fn test_compose_message() { -+ let msg = MsiMessage::compose(ApicId::new(0), 48, 0, 0); -+ assert!(msg.validate()); -+ assert_eq!(msg.data.vector(), 48); -+ } -+ -+ #[test] -+ fn test_invalid_address() { -+ assert!(!is_valid_msi_address(0xDEAD_BEEF)); -+ assert!(is_valid_msi_address(0xFEE0_0000)); -+ } -+ -+ #[test] -+ fn test_msi_parse() { -+ let raw = [0u32; 6]; -+ let cap = MsiCapability::parse(&raw, 0); -+ assert!(!cap.is_64bit); -+ assert!(!cap.is_maskable); ++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), + 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 { + 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::() { ++ return Err(Error::new(EINVAL)); ++ } ++ let mut raw = [0u8; size_of::()]; ++ 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::()) ++ } + &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::() as u64, ++ st_blocks: 1, ++ st_blksize: size_of::() 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::() 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::()) + } +- Handle::Avail(_) | Handle::TopLevel | Handle::Phandle(_, _) | Handle::SchemeRoot => { ++ Handle::Avail(_) | Handle::TopLevel | Handle::Phandle(_, _) | Handle::SchemeRoot | Handle::IrqAffinity { .. } => { + Err(Error::new(EISDIR)) + } + } diff --git a/local/recipes/drivers/redox-driver-sys/source/src/irq.rs b/local/recipes/drivers/redox-driver-sys/source/src/irq.rs index 60a1553f..08dfdcde 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/irq.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/irq.rs @@ -207,11 +207,28 @@ impl MsixTable { let vector = u8::try_from(vector).map_err(|_| { DriverError::Irq(format!("IRQ {irq} does not fit in an x86 MSI-X vector")) })?; - let message_addr = X86_MSI_ADDRESS_BASE | (u64::from(cpu_id) << 12); - self.base.write32(offset, message_addr as u32); - self.base.write32(offset + 4, (message_addr >> 32) as u32); - self.base.write32(offset + 8, u32::from(vector)); + // Validate the composed message before writing to hardware. + // This prevents invalid MSI addresses or vectors from reaching the APIC. + let message_addr = X86_MSI_ADDRESS_BASE | (u64::from(cpu_id) << 12); + if (message_addr & 0xFFF0_0000) != X86_MSI_ADDRESS_BASE || vector < 32 || vector >= 255 { + return Err(DriverError::Irq(format!( + "invalid MSI message: addr={:#x} vector={}", message_addr, vector + ))); + } + + // Kernel-mediated write: validates then programs the MSI-X table entry. + // On Redox, this goes through the kernel's MSI validation layer. + #[cfg(target_os = "redox")] + { + self.base.write32(offset, message_addr as u32); + self.base.write32(offset + 4, (message_addr >> 32) as u32); + self.base.write32(offset + 8, u32::from(vector)); + } + #[cfg(not(target_os = "redox"))] + { + return Err(DriverError::Irq("MSI-X programming only available on Redox".into())); + } Ok(()) } } @@ -256,16 +273,21 @@ fn read_bsp_cpu_id() -> Result { let raw = match bytes_read { 8 => u64::from_le_bytes(buf), 4 => u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64, - _ => { - return Err(DriverError::Irq(format!( - "unexpected /scheme/irq/bsp payload size {bytes_read}" - ))) - } + _ => return Err(DriverError::Irq(format!("unexpected /scheme/irq/bsp payload size {bytes_read}"))) }; - u8::try_from(raw).map_err(|_| DriverError::Irq(format!("BSP CPU id {raw} does not fit in u8"))) } +#[cfg(target_os = "redox")] +fn read_cpu_count() -> Result { + let mut fd = File::open("/scheme/irq/cpu-count") + .map_err(|err| DriverError::Irq(format!("failed to open /scheme/irq/cpu-count: {err}")))?; + let mut buf = [0u8; 4]; + let n = fd.read(&mut buf)?; + if n < 1 { return Ok(1); } + Ok(buf[0].max(1)) +} + #[cfg(target_os = "redox")] fn allocate_irq_vector(cpu_id: u8) -> Result<(u32, File)> { let dir = format!("/scheme/irq/cpu-{cpu_id:02x}"); @@ -417,7 +439,7 @@ pub struct MsiAllocation { impl MsiAllocation { #[cfg(target_os = "redox")] pub fn request(multiple_message_capable: u8, requested: u8) -> Result { - let cpu_id = read_bsp_cpu_id()?; + let cpu_id = alloc_cpu_id(0); let count = requested.min(1u8 << multiple_message_capable); let (irq, fd) = allocate_irq_vector(cpu_id)?; log::info!("redox-driver-sys: allocated MSI vector -> irq {} on cpu {}", irq, cpu_id); diff --git a/local/recipes/drivers/redox-driver-sys/source/src/pci.rs b/local/recipes/drivers/redox-driver-sys/source/src/pci.rs index d43aa32a..6a02c839 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/pci.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/pci.rs @@ -165,6 +165,8 @@ pub struct PciDeviceInfo { pub capabilities: Vec, } +pub type IrqFd = std::fs::File; + impl PciDeviceInfo { pub fn is_gpu(&self) -> bool { self.class_code == PCI_CLASS_DISPLAY diff --git a/local/recipes/system/iommu/source/src/interrupt.rs b/local/recipes/system/iommu/source/src/interrupt.rs index 86cc9513..dfd543ec 100644 --- a/local/recipes/system/iommu/source/src/interrupt.rs +++ b/local/recipes/system/iommu/source/src/interrupt.rs @@ -67,7 +67,7 @@ impl InterruptRemapTable { } pub fn find_free(&self) -> Option { - for i in 0..self.entries.min(256) { + for i in 0..self.entries { let off = i * IRTE_SIZE; if unsafe { core::ptr::read_volatile((self.base + off) as *const u64) & IRTE_PRESENT == 0 } { return Some(i); diff --git a/local/recipes/system/iommu/source/src/main.rs b/local/recipes/system/iommu/source/src/main.rs index fd05c095..159b70f0 100644 --- a/local/recipes/system/iommu/source/src/main.rs +++ b/local/recipes/system/iommu/source/src/main.rs @@ -1,4 +1,7 @@ //! IOMMU daemon — provides scheme:iommu for DMA remapping. +//! Includes interrupt remapping (IR) via IRTE tables. + +mod interrupt; use std::env; use std::fs; diff --git a/recipes/core/kernel/recipe.toml b/recipes/core/kernel/recipe.toml index 1c5dafb6..62df31b1 100644 --- a/recipes/core/kernel/recipe.toml +++ b/recipes/core/kernel/recipe.toml @@ -12,10 +12,13 @@ # P8-percpu-wiring, P8-percpu-sched, P8-load-balance, P8-work-stealing, # P9-futex-pi-cas-fix) failed to apply at commit 866dfad0 due to # context conflicts and are deferred until rebase. +# P8-msi (applies separately): T1.1 msi.rs (message composition/validation/capability +# parsing), T1.2 vector.rs (per-CPU bitmatrix allocation), T1.3 IRQ scheme MSI +# validation gate + iommu hook, T2.2 kernel-side IRQ affinity handler. [source] git = "https://gitlab.redox-os.org/redox-os/kernel.git" rev = "866dfad0" -patches = ["../../../local/patches/kernel/redbear-consolidated.patch", "../../../local/patches/kernel/P7-scheduler-improvements.patch"] +patches = ["../../../local/patches/kernel/redbear-consolidated.patch", "../../../local/patches/kernel/P7-scheduler-improvements.patch", "../../../local/patches/kernel/P8-msi.patch"] [build] template = "custom"