Avoid Rust references of IRT entries

This commit is contained in:
Ruihan Li 2025-05-09 11:39:19 +08:00 committed by Junyang Zhang
parent 189daa2e09
commit d4afe3a035
3 changed files with 87 additions and 110 deletions

View File

@ -2,23 +2,17 @@
mod table; mod table;
use alloc::sync::Arc; use core::fmt::Debug;
use core::{fmt::Debug, mem::size_of};
use log::{info, warn}; use log::{info, warn};
use spin::Once; use spin::Once;
pub(super) use table::IntRemappingTable; pub(super) use table::IntRemappingTable;
use table::IrtEntry;
use crate::{ use crate::arch::iommu::registers::{ExtendedCapabilityFlags, IOMMU_REGS};
arch::iommu::registers::{ExtendedCapabilityFlags, IOMMU_REGS},
prelude::Vaddr,
sync::{LocalIrqDisabled, SpinLock},
};
pub struct IrtEntryHandle { pub struct IrtEntryHandle {
index: u16, index: u16,
entry_ref: Option<&'static mut IrtEntry>, table: &'static IntRemappingTable,
} }
impl IrtEntryHandle { impl IrtEntryHandle {
@ -26,32 +20,9 @@ impl IrtEntryHandle {
self.index self.index
} }
#[expect(unused)] pub fn enable(&self, vector: u32) {
pub fn irt_entry(&self) -> Option<&IrtEntry> { self.table
self.entry_ref.as_deref() .set_entry(self.index, table::IrtEntry::new_enabled(vector));
}
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::<IrtEntry>()) as *mut IrtEntry),
),
}
} }
} }
@ -59,8 +30,7 @@ impl Debug for IrtEntryHandle {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IrtEntryHandle") f.debug_struct("IrtEntryHandle")
.field("index", &self.index) .field("index", &self.index)
.field("entry_ref", &self.entry_ref) .finish_non_exhaustive()
.finish()
} }
} }
@ -68,7 +38,7 @@ pub fn has_interrupt_remapping() -> bool {
REMAPPING_TABLE.get().is_some() REMAPPING_TABLE.get().is_some()
} }
pub fn alloc_irt_entry() -> Option<Arc<SpinLock<IrtEntryHandle, LocalIrqDisabled>>> { pub fn alloc_irt_entry() -> Option<IrtEntryHandle> {
let page_table = REMAPPING_TABLE.get()?; let page_table = REMAPPING_TABLE.get()?;
page_table.alloc() page_table.alloc()
} }

View File

@ -1,6 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use alloc::{sync::Arc, vec::Vec};
use core::{fmt::Debug, mem::size_of}; use core::{fmt::Debug, mem::size_of};
use bitflags::bitflags; use bitflags::bitflags;
@ -9,7 +8,7 @@ use int_to_c_enum::TryFromInt;
use super::IrtEntryHandle; use super::IrtEntryHandle;
use crate::{ use crate::{
mm::{paddr_to_vaddr, FrameAllocOptions, Segment, PAGE_SIZE}, mm::{FrameAllocOptions, Segment, UntypedMem, PAGE_SIZE},
sync::{LocalIrqDisabled, SpinLock}, sync::{LocalIrqDisabled, SpinLock},
}; };
@ -20,80 +19,97 @@ enum ExtendedInterruptMode {
X2Apic, X2Apic,
} }
struct IntRemappingMeta;
crate::impl_untyped_frame_meta_for!(IntRemappingMeta);
pub struct IntRemappingTable { pub struct IntRemappingTable {
size: u16, num_entries: u16,
extended_interrupt_mode: ExtendedInterruptMode, extended_interrupt_mode: ExtendedInterruptMode,
frames: Segment<()>, segment: Segment<IntRemappingMeta>,
/// The global allocator for Interrupt remapping entry. /// A lock that prevents concurrent modification of entries.
modification_lock: SpinLock<(), LocalIrqDisabled>,
/// An allocator that allocates indexes to unused entries.
allocator: SpinLock<IdAlloc, LocalIrqDisabled>, allocator: SpinLock<IdAlloc, LocalIrqDisabled>,
handles: Vec<Arc<SpinLock<IrtEntryHandle, LocalIrqDisabled>>>,
} }
impl IntRemappingTable { impl IntRemappingTable {
pub fn alloc(&self) -> Option<Arc<SpinLock<IrtEntryHandle, LocalIrqDisabled>>> { pub fn alloc(&'static self) -> Option<IrtEntryHandle> {
let id = self.allocator.lock().alloc()?; let index = self.allocator.lock().alloc()?;
Some(self.handles.get(id).unwrap().clone()) 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 { pub(super) fn new() -> Self {
const DEFAULT_PAGES: usize = 1; const NUM_PAGES: usize = 1;
let segment = FrameAllocOptions::new()
.alloc_segment(DEFAULT_PAGES)
.unwrap();
let entry_number = (DEFAULT_PAGES * PAGE_SIZE / size_of::<u128>()) as u16;
let mut handles = Vec::new(); let segment = FrameAllocOptions::new()
let base_vaddr = paddr_to_vaddr(segment.start_paddr()); .alloc_segment_with(NUM_PAGES, |_| IntRemappingMeta)
for index in 0..entry_number { .unwrap();
// SAFETY: The IrtEntry reference will always valid and will disabled when IntRemappingTable is dropped. let num_entries = (NUM_PAGES * PAGE_SIZE / size_of::<u128>())
let handle = unsafe { IrtEntryHandle::new(base_vaddr, index) }; .try_into()
handles.push(Arc::new(SpinLock::new(handle))); .unwrap();
}
Self { Self {
size: entry_number, num_entries,
extended_interrupt_mode: ExtendedInterruptMode::X2Apic, extended_interrupt_mode: ExtendedInterruptMode::X2Apic,
frames: segment, segment,
allocator: SpinLock::new(IdAlloc::with_capacity(entry_number as usize)), modification_lock: SpinLock::new(()),
handles, 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::<u128>();
// 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::<u64>())
.write_once(&upper)
.unwrap();
self.segment
.writer()
.skip(offset)
.write_once(&lower)
.unwrap();
}
/// Encodes the value written into the Interrupt Remapping Table Register. /// Encodes the value written into the Interrupt Remapping Table Register.
pub(crate) fn encode(&self) -> u64 { 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 { // Bit Range 11 - Extended Interrupt Mode Enable (EIME)
ExtendedInterruptMode::XApic => {} encoded |= match self.extended_interrupt_mode {
ExtendedInterruptMode::X2Apic => encoded |= 1 << 11, ExtendedInterruptMode::XApic => 0,
} ExtendedInterruptMode::X2Apic => 1 << 11,
};
// entry_number = 2^(size+1) // Bit Range 3:0 - Size (S)
if self.size == 1 { encoded |= {
panic!("Wrong entry number"); assert!(self.num_entries.is_power_of_two());
} // num_entries = 2^(size+1)
let mut size = 0; let size = self.num_entries.trailing_zeros().checked_sub(1).unwrap();
let mut tmp = self.size >> 1; assert!(size <= 15);
while (tmp & 0b1) == 0 { size as u64
tmp >>= 1; };
size += 1;
}
encoded += size;
encoded 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. /// The type of validation that must be performed by the interrupt-remapping hardware.
#[derive(Debug, TryFromInt)] #[derive(Debug, TryFromInt)]
#[repr(u32)] #[repr(u32)]
@ -146,20 +162,15 @@ enum DeliveryMode {
pub struct IrtEntry(u128); pub struct IrtEntry(u128);
impl IrtEntry { impl IrtEntry {
#[expect(unused)] /// Creates an enabled entry with no validation,
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,
/// DST = 0, IM = 0, DLM = 0, TM = 0, RH = 0, DM = 0, FPD = 1, P = 1 /// DST = 0, IM = 0, DLM = 0, TM = 0, RH = 0, DM = 0, FPD = 1, P = 1
pub fn enable_default(&mut self, vector: u32) { pub(super) fn new_enabled(vector: u32) -> Self {
self.0 = 0b11 | ((vector as u128) << 16); 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 { pub fn source_validation_type(&self) -> SourceValidationType {

View File

@ -13,7 +13,7 @@ use x86_64::registers::rflags::{self, RFlags};
use super::iommu::{alloc_irt_entry, has_interrupt_remapping, IrtEntryHandle}; use super::iommu::{alloc_irt_entry, has_interrupt_remapping, IrtEntryHandle};
use crate::{ use crate::{
cpu::PinCurrentCpu, cpu::PinCurrentCpu,
sync::{LocalIrqDisabled, Mutex, PreemptDisabled, RwLock, RwLockReadGuard, SpinLock}, sync::{Mutex, PreemptDisabled, RwLock, RwLockReadGuard, SpinLock},
trap::TrapFrame, trap::TrapFrame,
}; };
@ -88,7 +88,7 @@ impl Debug for CallbackElement {
pub(crate) struct IrqLine { pub(crate) struct IrqLine {
pub(crate) irq_num: u8, pub(crate) irq_num: u8,
pub(crate) callback_list: RwLock<Vec<CallbackElement>>, pub(crate) callback_list: RwLock<Vec<CallbackElement>>,
bind_remapping_entry: Once<Arc<SpinLock<IrtEntryHandle, LocalIrqDisabled>>>, bind_remapping_entry: Once<IrtEntryHandle>,
} }
impl IrqLine { impl IrqLine {
@ -105,11 +105,7 @@ impl IrqLine {
let handle = alloc_irt_entry(); let handle = alloc_irt_entry();
if let Some(handle) = handle { if let Some(handle) = handle {
// Enable the IRT entry // Enable the IRT entry
handle handle.enable(irq_num as u32);
.lock()
.irt_entry_mut()
.unwrap()
.enable_default(irq_num as u32);
irq.bind_remapping_entry.call_once(|| handle); irq.bind_remapping_entry.call_once(|| handle);
} }
} }
@ -121,7 +117,7 @@ impl IrqLine {
/// This method will return `None` if interrupt remapping is disabled or /// This method will return `None` if interrupt remapping is disabled or
/// not supported by the architecture. /// not supported by the architecture.
pub fn remapping_index(&self) -> Option<u16> { pub fn remapping_index(&self) -> Option<u16> {
Some(self.bind_remapping_entry.get()?.lock().index()) Some(self.bind_remapping_entry.get()?.index())
} }
/// Gets the IRQ number. /// Gets the IRQ number.