mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
Support handling multiple IOMMU faults
This commit is contained in:
parent
baee45a60f
commit
fa1cfc03f2
@ -4,25 +4,25 @@ use alloc::vec::Vec;
|
|||||||
use core::{fmt::Debug, ptr::NonNull};
|
use core::{fmt::Debug, ptr::NonNull};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use log::info;
|
use log::{error, info};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
use volatile::{
|
use volatile::{access::ReadWrite, VolatileRef};
|
||||||
access::{ReadOnly, ReadWrite},
|
|
||||||
VolatileRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::registers::Capability;
|
use super::registers::Capability;
|
||||||
use crate::trap::{IrqLine, TrapFrame};
|
use crate::{
|
||||||
|
sync::{LocalIrqDisabled, SpinLock},
|
||||||
|
trap::{IrqLine, TrapFrame},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FaultEventRegisters {
|
pub struct FaultEventRegisters {
|
||||||
status: VolatileRef<'static, u32, ReadOnly>,
|
status: VolatileRef<'static, u32, ReadWrite>,
|
||||||
/// bit31: Interrupt Mask; bit30: Interrupt Pending.
|
/// bit31: Interrupt Mask; bit30: Interrupt Pending.
|
||||||
_control: VolatileRef<'static, u32, ReadWrite>,
|
_control: VolatileRef<'static, u32, ReadWrite>,
|
||||||
_data: VolatileRef<'static, u32, ReadWrite>,
|
_data: VolatileRef<'static, u32, ReadWrite>,
|
||||||
_address: VolatileRef<'static, u32, ReadWrite>,
|
_address: VolatileRef<'static, u32, ReadWrite>,
|
||||||
_upper_address: VolatileRef<'static, u32, ReadWrite>,
|
_upper_address: VolatileRef<'static, u32, ReadWrite>,
|
||||||
recordings: Vec<VolatileRef<'static, u128, ReadOnly>>,
|
recordings: Vec<VolatileRef<'static, u128, ReadWrite>>,
|
||||||
|
|
||||||
_fault_irq: IrqLine,
|
_fault_irq: IrqLine,
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ impl FaultEventRegisters {
|
|||||||
// capability
|
// capability
|
||||||
VolatileRef::new_read_only(base.add(0x08).cast::<u64>()),
|
VolatileRef::new_read_only(base.add(0x08).cast::<u64>()),
|
||||||
// status
|
// status
|
||||||
VolatileRef::new_read_only(base.add(0x34).cast::<u32>()),
|
VolatileRef::new(base.add(0x34).cast::<u32>()),
|
||||||
// control
|
// control
|
||||||
VolatileRef::new(base.add(0x38).cast::<u32>()),
|
VolatileRef::new(base.add(0x38).cast::<u32>()),
|
||||||
// data
|
// data
|
||||||
@ -68,14 +68,12 @@ impl FaultEventRegisters {
|
|||||||
// SAFETY: The safety is upheld by the caller and the correctness of the capability
|
// SAFETY: The safety is upheld by the caller and the correctness of the capability
|
||||||
// value.
|
// value.
|
||||||
recordings.push(unsafe {
|
recordings.push(unsafe {
|
||||||
VolatileRef::new_read_only(
|
VolatileRef::new(base_register_vaddr.add(offset).add(i * 16).cast::<u128>())
|
||||||
base_register_vaddr.add(offset).add(i * 16).cast::<u128>(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fault_irq = IrqLine::alloc().unwrap();
|
let mut fault_irq = IrqLine::alloc().unwrap();
|
||||||
fault_irq.on_active(iommu_page_fault_handler);
|
fault_irq.on_active(iommu_fault_handler);
|
||||||
|
|
||||||
// Set page fault interrupt vector and address
|
// Set page fault interrupt vector and address
|
||||||
data.as_mut_ptr().write(fault_irq.num() as u32);
|
data.as_mut_ptr().write(fault_irq.num() as u32);
|
||||||
@ -101,6 +99,10 @@ impl FaultRecording {
|
|||||||
self.0 & (1 << 127) != 0
|
self.0 & (1 << 127) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_fault(&mut self) {
|
||||||
|
self.0 &= !(1 << 127);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request_type(&self) -> FaultRequestType {
|
pub fn request_type(&self) -> FaultRequestType {
|
||||||
// bit 126 and bit 92
|
// bit 126 and bit 92
|
||||||
let t1 = ((self.0 & (1 << 126)) >> 125) as u8;
|
let t1 = ((self.0 & (1 << 126)) >> 125) as u8;
|
||||||
@ -222,7 +224,8 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) static FAULT_EVENT_REGS: Once<FaultEventRegisters> = Once::new();
|
pub(super) static FAULT_EVENT_REGS: Once<SpinLock<FaultEventRegisters, LocalIrqDisabled>> =
|
||||||
|
Once::new();
|
||||||
|
|
||||||
/// Initializes the fault reporting function.
|
/// Initializes the fault reporting function.
|
||||||
///
|
///
|
||||||
@ -230,12 +233,56 @@ pub(super) static FAULT_EVENT_REGS: Once<FaultEventRegisters> = Once::new();
|
|||||||
///
|
///
|
||||||
/// User must ensure the base_register_vaddr is read from DRHD
|
/// User must ensure the base_register_vaddr is read from DRHD
|
||||||
pub(super) unsafe fn init(base_register_vaddr: NonNull<u8>) {
|
pub(super) unsafe fn init(base_register_vaddr: NonNull<u8>) {
|
||||||
FAULT_EVENT_REGS.call_once(|| FaultEventRegisters::new(base_register_vaddr));
|
FAULT_EVENT_REGS.call_once(|| SpinLock::new(FaultEventRegisters::new(base_register_vaddr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iommu_page_fault_handler(_frame: &TrapFrame) {
|
fn iommu_fault_handler(_frame: &TrapFrame) {
|
||||||
let fault_event = FAULT_EVENT_REGS.get().unwrap();
|
let mut fault_event_regs = FAULT_EVENT_REGS.get().unwrap().lock();
|
||||||
let index = (fault_event.status().bits & FaultStatus::FRI.bits) >> 8;
|
|
||||||
let recording = FaultRecording(fault_event.recordings[index as usize].as_ptr().read());
|
primary_fault_handler(&mut fault_event_regs);
|
||||||
info!("Catch iommu page fault, recording:{:x?}", recording)
|
|
||||||
|
let fault_status = fault_event_regs.status();
|
||||||
|
if fault_status.intersects(FaultStatus::IQE | FaultStatus::ICE | FaultStatus::ITE) {
|
||||||
|
panic!(
|
||||||
|
"Catch IOMMU invalidation error. Fault status: {:x?}",
|
||||||
|
fault_status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn primary_fault_handler(fault_event_regs: &mut FaultEventRegisters) {
|
||||||
|
let mut fault_status = fault_event_regs.status();
|
||||||
|
if !fault_status.contains(FaultStatus::PPF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_index = ((fault_event_regs.status().bits & FaultStatus::FRI.bits) >> 8) as usize;
|
||||||
|
let mut fault_iter = fault_event_regs.recordings.iter_mut();
|
||||||
|
fault_iter.advance_by(start_index).unwrap();
|
||||||
|
for raw_recording in fault_iter {
|
||||||
|
let raw_recording = raw_recording.as_mut_ptr();
|
||||||
|
let mut recording = FaultRecording(raw_recording.read());
|
||||||
|
if !recording.is_fault() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report
|
||||||
|
error!(
|
||||||
|
"Catch iommu page fault, doing nothing. recording:{:x?}",
|
||||||
|
recording
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear Fault field
|
||||||
|
recording.clear_fault();
|
||||||
|
raw_recording.write(recording.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if fault_status.contains(FaultStatus::PFO) {
|
||||||
|
info!("Primary fault overflow detected.");
|
||||||
|
fault_status.remove(FaultStatus::PFO);
|
||||||
|
fault_event_regs
|
||||||
|
.status
|
||||||
|
.as_mut_ptr()
|
||||||
|
.write(fault_status.bits);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#![feature(ptr_sub_ptr)]
|
#![feature(ptr_sub_ptr)]
|
||||||
#![feature(sync_unsafe_cell)]
|
#![feature(sync_unsafe_cell)]
|
||||||
#![feature(trait_upcasting)]
|
#![feature(trait_upcasting)]
|
||||||
|
#![feature(iter_advance_by)]
|
||||||
// The `generic_const_exprs` feature is incomplete however required for the page table
|
// The `generic_const_exprs` feature is incomplete however required for the page table
|
||||||
// const generic implementation. We are using this feature in a conservative manner.
|
// const generic implementation. We are using this feature in a conservative manner.
|
||||||
#![expect(incomplete_features)]
|
#![expect(incomplete_features)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user