diff --git a/ostd/src/arch/x86/iommu/interrupt_remapping/mod.rs b/ostd/src/arch/x86/iommu/interrupt_remapping/mod.rs index 6bcc6cff..400497cd 100644 --- a/ostd/src/arch/x86/iommu/interrupt_remapping/mod.rs +++ b/ostd/src/arch/x86/iommu/interrupt_remapping/mod.rs @@ -2,23 +2,17 @@ mod table; -use alloc::sync::Arc; -use core::{fmt::Debug, mem::size_of}; +use core::fmt::Debug; use log::{info, warn}; use spin::Once; pub(super) use table::IntRemappingTable; -use table::IrtEntry; -use crate::{ - arch::iommu::registers::{ExtendedCapabilityFlags, IOMMU_REGS}, - prelude::Vaddr, - sync::{LocalIrqDisabled, SpinLock}, -}; +use crate::arch::iommu::registers::{ExtendedCapabilityFlags, IOMMU_REGS}; pub struct IrtEntryHandle { index: u16, - entry_ref: Option<&'static mut IrtEntry>, + table: &'static IntRemappingTable, } impl IrtEntryHandle { @@ -26,32 +20,9 @@ impl IrtEntryHandle { self.index } - #[expect(unused)] - pub fn irt_entry(&self) -> Option<&IrtEntry> { - self.entry_ref.as_deref() - } - - pub fn irt_entry_mut(&mut self) -> Option<&mut IrtEntry> { - self.entry_ref.as_deref_mut() - } - - /// Set entry reference to None. - pub(self) fn set_none(&mut self) { - self.entry_ref = None; - } - - /// Creates a handle based on index and the interrupt remapping table base virtual address. - /// - /// # Safety - /// - /// User must ensure the target address is **always** valid and point to `IrtEntry`. - pub(self) unsafe fn new(table_vaddr: Vaddr, index: u16) -> Self { - Self { - index, - entry_ref: Some( - &mut *((table_vaddr + index as usize * size_of::()) as *mut IrtEntry), - ), - } + pub fn enable(&self, vector: u32) { + self.table + .set_entry(self.index, table::IrtEntry::new_enabled(vector)); } } @@ -59,8 +30,7 @@ impl Debug for IrtEntryHandle { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("IrtEntryHandle") .field("index", &self.index) - .field("entry_ref", &self.entry_ref) - .finish() + .finish_non_exhaustive() } } @@ -68,7 +38,7 @@ pub fn has_interrupt_remapping() -> bool { REMAPPING_TABLE.get().is_some() } -pub fn alloc_irt_entry() -> Option>> { +pub fn alloc_irt_entry() -> Option { let page_table = REMAPPING_TABLE.get()?; page_table.alloc() } diff --git a/ostd/src/arch/x86/iommu/interrupt_remapping/table.rs b/ostd/src/arch/x86/iommu/interrupt_remapping/table.rs index 370f7de7..ca07f1d9 100644 --- a/ostd/src/arch/x86/iommu/interrupt_remapping/table.rs +++ b/ostd/src/arch/x86/iommu/interrupt_remapping/table.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -use alloc::{sync::Arc, vec::Vec}; use core::{fmt::Debug, mem::size_of}; use bitflags::bitflags; @@ -9,7 +8,7 @@ use int_to_c_enum::TryFromInt; use super::IrtEntryHandle; use crate::{ - mm::{paddr_to_vaddr, FrameAllocOptions, Segment, PAGE_SIZE}, + mm::{FrameAllocOptions, Segment, UntypedMem, PAGE_SIZE}, sync::{LocalIrqDisabled, SpinLock}, }; @@ -20,80 +19,97 @@ enum ExtendedInterruptMode { X2Apic, } +struct IntRemappingMeta; +crate::impl_untyped_frame_meta_for!(IntRemappingMeta); + pub struct IntRemappingTable { - size: u16, + num_entries: u16, extended_interrupt_mode: ExtendedInterruptMode, - frames: Segment<()>, - /// The global allocator for Interrupt remapping entry. + segment: Segment, + /// A lock that prevents concurrent modification of entries. + modification_lock: SpinLock<(), LocalIrqDisabled>, + /// An allocator that allocates indexes to unused entries. allocator: SpinLock, - handles: Vec>>, } impl IntRemappingTable { - pub fn alloc(&self) -> Option>> { - let id = self.allocator.lock().alloc()?; - Some(self.handles.get(id).unwrap().clone()) + pub fn alloc(&'static self) -> Option { + let index = self.allocator.lock().alloc()?; + Some(IrtEntryHandle { + index: index as u16, + table: self, + }) } - /// Creates an Interrupt Remapping Table with one `Segment` (default). + /// Creates an Interrupt Remapping Table with one page. pub(super) fn new() -> Self { - const DEFAULT_PAGES: usize = 1; - let segment = FrameAllocOptions::new() - .alloc_segment(DEFAULT_PAGES) - .unwrap(); - let entry_number = (DEFAULT_PAGES * PAGE_SIZE / size_of::()) as u16; + const NUM_PAGES: usize = 1; - let mut handles = Vec::new(); - let base_vaddr = paddr_to_vaddr(segment.start_paddr()); - for index in 0..entry_number { - // SAFETY: The IrtEntry reference will always valid and will disabled when IntRemappingTable is dropped. - let handle = unsafe { IrtEntryHandle::new(base_vaddr, index) }; - handles.push(Arc::new(SpinLock::new(handle))); - } + let segment = FrameAllocOptions::new() + .alloc_segment_with(NUM_PAGES, |_| IntRemappingMeta) + .unwrap(); + let num_entries = (NUM_PAGES * PAGE_SIZE / size_of::()) + .try_into() + .unwrap(); Self { - size: entry_number, + num_entries, extended_interrupt_mode: ExtendedInterruptMode::X2Apic, - frames: segment, - allocator: SpinLock::new(IdAlloc::with_capacity(entry_number as usize)), - handles, + segment, + modification_lock: SpinLock::new(()), + allocator: SpinLock::new(IdAlloc::with_capacity(num_entries as usize)), } } + /// Sets the entry in the Interrupt Remapping Table. + pub(super) fn set_entry(&self, index: u16, entry: IrtEntry) { + let _guard = self.modification_lock.lock(); + + let [lower, upper] = entry.as_raw_u64(); + + let offset = (index as usize) * size_of::(); + // Write a zero as the lower bits first to clear the present bit. + self.segment + .writer() + .skip(offset) + .write_once(&0u64) + .unwrap(); + // Write the upper bits first (which keeps the present bit unset) + self.segment + .writer() + .skip(offset + size_of::()) + .write_once(&upper) + .unwrap(); + self.segment + .writer() + .skip(offset) + .write_once(&lower) + .unwrap(); + } + /// Encodes the value written into the Interrupt Remapping Table Register. pub(crate) fn encode(&self) -> u64 { - let mut encoded = self.frames.start_paddr() as u64; + let mut encoded = self.segment.start_paddr() as u64; - match self.extended_interrupt_mode { - ExtendedInterruptMode::XApic => {} - ExtendedInterruptMode::X2Apic => encoded |= 1 << 11, - } + // Bit Range 11 - Extended Interrupt Mode Enable (EIME) + encoded |= match self.extended_interrupt_mode { + ExtendedInterruptMode::XApic => 0, + ExtendedInterruptMode::X2Apic => 1 << 11, + }; - // entry_number = 2^(size+1) - if self.size == 1 { - panic!("Wrong entry number"); - } - let mut size = 0; - let mut tmp = self.size >> 1; - while (tmp & 0b1) == 0 { - tmp >>= 1; - size += 1; - } - encoded += size; + // Bit Range 3:0 - Size (S) + encoded |= { + assert!(self.num_entries.is_power_of_two()); + // num_entries = 2^(size+1) + let size = self.num_entries.trailing_zeros().checked_sub(1).unwrap(); + assert!(size <= 15); + size as u64 + }; encoded } } -impl Drop for IntRemappingTable { - fn drop(&mut self) { - for handle in self.handles.iter_mut() { - let mut handle = handle.lock(); - handle.set_none(); - } - } -} - /// The type of validation that must be performed by the interrupt-remapping hardware. #[derive(Debug, TryFromInt)] #[repr(u32)] @@ -146,20 +162,15 @@ enum DeliveryMode { pub struct IrtEntry(u128); impl IrtEntry { - #[expect(unused)] - pub const fn new(value: u128) -> Self { - Self(value) - } - - #[expect(unused)] - pub fn clear(&mut self) { - self.0 = 0 - } - - /// Enables this entry with no validation, + /// Creates an enabled entry with no validation, + /// /// DST = 0, IM = 0, DLM = 0, TM = 0, RH = 0, DM = 0, FPD = 1, P = 1 - pub fn enable_default(&mut self, vector: u32) { - self.0 = 0b11 | ((vector as u128) << 16); + pub(super) fn new_enabled(vector: u32) -> Self { + Self(0b11 | ((vector as u128) << 16)) + } + + fn as_raw_u64(&self) -> [u64; 2] { + [self.0 as u64, (self.0 >> 64) as u64] } pub fn source_validation_type(&self) -> SourceValidationType { diff --git a/ostd/src/arch/x86/irq.rs b/ostd/src/arch/x86/irq.rs index de9c707e..672d405b 100644 --- a/ostd/src/arch/x86/irq.rs +++ b/ostd/src/arch/x86/irq.rs @@ -13,7 +13,7 @@ use x86_64::registers::rflags::{self, RFlags}; use super::iommu::{alloc_irt_entry, has_interrupt_remapping, IrtEntryHandle}; use crate::{ cpu::PinCurrentCpu, - sync::{LocalIrqDisabled, Mutex, PreemptDisabled, RwLock, RwLockReadGuard, SpinLock}, + sync::{Mutex, PreemptDisabled, RwLock, RwLockReadGuard, SpinLock}, trap::TrapFrame, }; @@ -88,7 +88,7 @@ impl Debug for CallbackElement { pub(crate) struct IrqLine { pub(crate) irq_num: u8, pub(crate) callback_list: RwLock>, - bind_remapping_entry: Once>>, + bind_remapping_entry: Once, } impl IrqLine { @@ -105,11 +105,7 @@ impl IrqLine { let handle = alloc_irt_entry(); if let Some(handle) = handle { // Enable the IRT entry - handle - .lock() - .irt_entry_mut() - .unwrap() - .enable_default(irq_num as u32); + handle.enable(irq_num as u32); irq.bind_remapping_entry.call_once(|| handle); } } @@ -121,7 +117,7 @@ impl IrqLine { /// This method will return `None` if interrupt remapping is disabled or /// not supported by the architecture. pub fn remapping_index(&self) -> Option { - Some(self.bind_remapping_entry.get()?.lock().index()) + Some(self.bind_remapping_entry.get()?.index()) } /// Gets the IRQ number.