mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Extract IOMMU register operations
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
63b42bff73
commit
0bf3595964
@ -12,7 +12,7 @@ use spin::Once;
|
|||||||
use trapframe::TrapFrame;
|
use trapframe::TrapFrame;
|
||||||
use volatile::{access::ReadWrite, Volatile};
|
use volatile::{access::ReadWrite, Volatile};
|
||||||
|
|
||||||
use super::remapping::Capability;
|
use super::registers::Capability;
|
||||||
use crate::{mm::Vaddr, trap::IrqLine};
|
use crate::{mm::Vaddr, trap::IrqLine};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -37,10 +37,13 @@ impl FaultEventRegisters {
|
|||||||
///
|
///
|
||||||
/// User must ensure the base_register_vaddr is read from DRHD
|
/// User must ensure the base_register_vaddr is read from DRHD
|
||||||
unsafe fn new(base_register_vaddr: Vaddr) -> Self {
|
unsafe fn new(base_register_vaddr: Vaddr) -> Self {
|
||||||
let capability = Volatile::new_read_only(&*((base_register_vaddr + 0x08) as *const u64));
|
let capability_reg =
|
||||||
let length = ((capability.read() & Capability::NFR.bits()) >> 40) + 1;
|
Volatile::new_read_only(&*((base_register_vaddr + 0x08) as *const u64));
|
||||||
|
let capability = Capability::new(capability_reg.read());
|
||||||
|
|
||||||
|
let length = capability.fault_recording_number() + 1;
|
||||||
let mut recordings = Vec::with_capacity(length as usize);
|
let mut recordings = Vec::with_capacity(length as usize);
|
||||||
let offset = (capability.read() & 0x3_ff00_0000) >> 24;
|
let offset = capability.fault_recording_register_offset();
|
||||||
for i in 0..length {
|
for i in 0..length {
|
||||||
recordings.push(Volatile::new(
|
recordings.push(Volatile::new(
|
||||||
&mut *((base_register_vaddr + 16 * (offset + i) as usize) as *mut u128),
|
&mut *((base_register_vaddr + 16 * (offset + i) as usize) as *mut u128),
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
mod context_table;
|
mod context_table;
|
||||||
mod fault;
|
mod fault;
|
||||||
mod remapping;
|
mod registers;
|
||||||
mod second_stage;
|
mod second_stage;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -65,6 +65,8 @@ pub(crate) fn unmap(daddr: Daddr) -> Result<(), IommuError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init() -> Result<(), IommuError> {
|
pub(crate) fn init() -> Result<(), IommuError> {
|
||||||
|
registers::init()?;
|
||||||
|
|
||||||
let mut root_table = RootTable::new();
|
let mut root_table = RootTable::new();
|
||||||
// For all PCI Device, use the same page table.
|
// For all PCI Device, use the same page table.
|
||||||
let page_table = PageTable::<DeviceMode, PageTableEntry, PagingConsts>::empty();
|
let page_table = PageTable::<DeviceMode, PageTableEntry, PagingConsts>::empty();
|
||||||
|
171
ostd/src/arch/x86/iommu/registers/mod.rs
Normal file
171
ostd/src/arch/x86/iommu/registers/mod.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Registers and their definition used by IOMMU.
|
||||||
|
|
||||||
|
mod capability;
|
||||||
|
mod command;
|
||||||
|
mod extended_cap;
|
||||||
|
mod status;
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
pub use capability::Capability;
|
||||||
|
use command::GlobalCommand;
|
||||||
|
use extended_cap::ExtendedCapability;
|
||||||
|
use log::debug;
|
||||||
|
use spin::Once;
|
||||||
|
use status::GlobalStatus;
|
||||||
|
use volatile::{
|
||||||
|
access::{ReadOnly, ReadWrite, WriteOnly},
|
||||||
|
Volatile,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{dma_remapping::context_table::RootTable, IommuError};
|
||||||
|
use crate::{
|
||||||
|
arch::{
|
||||||
|
iommu::fault,
|
||||||
|
x86::kernel::acpi::dmar::{Dmar, Remapping},
|
||||||
|
},
|
||||||
|
mm::paddr_to_vaddr,
|
||||||
|
sync::SpinLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct IommuVersion {
|
||||||
|
major: u8,
|
||||||
|
minor: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IommuVersion {
|
||||||
|
/// Major version number
|
||||||
|
pub fn major(&self) -> u8 {
|
||||||
|
self.major
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minor version number
|
||||||
|
pub fn minor(&self) -> u8 {
|
||||||
|
self.minor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Important registers used by IOMMU.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IommuRegisters {
|
||||||
|
version: Volatile<&'static u32, ReadOnly>,
|
||||||
|
capability: Volatile<&'static u64, ReadOnly>,
|
||||||
|
extended_capability: Volatile<&'static u64, ReadOnly>,
|
||||||
|
global_command: Volatile<&'static mut u32, WriteOnly>,
|
||||||
|
global_status: Volatile<&'static u32, ReadOnly>,
|
||||||
|
root_table_address: Volatile<&'static mut u64, ReadWrite>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
context_command: Volatile<&'static mut u64, ReadWrite>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IommuRegisters {
|
||||||
|
/// Version of IOMMU
|
||||||
|
pub fn version(&self) -> IommuVersion {
|
||||||
|
let version = self.version.read();
|
||||||
|
IommuVersion {
|
||||||
|
major: version.get_bits(4..8) as u8,
|
||||||
|
minor: version.get_bits(0..4) as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Capability of IOMMU
|
||||||
|
pub fn capability(&self) -> Capability {
|
||||||
|
Capability::new(self.capability.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extended Capability of IOMMU
|
||||||
|
pub fn extended_capability(&self) -> ExtendedCapability {
|
||||||
|
ExtendedCapability::new(self.extended_capability.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global Status of IOMMU
|
||||||
|
pub fn global_status(&self) -> GlobalStatus {
|
||||||
|
GlobalStatus::from_bits_truncate(self.global_status.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable DMA remapping with static RootTable
|
||||||
|
pub(super) fn enable_dma_remapping(&mut self, root_table: &'static SpinLock<RootTable>) {
|
||||||
|
// Set root table address
|
||||||
|
self.root_table_address
|
||||||
|
.write(root_table.lock().paddr() as u64);
|
||||||
|
self.write_global_command(GlobalCommand::SRTP, true);
|
||||||
|
while !self.global_status().contains(GlobalStatus::RTPS) {}
|
||||||
|
|
||||||
|
// Enable DMA remapping
|
||||||
|
self.write_global_command(GlobalCommand::TE, true);
|
||||||
|
while !self.global_status().contains(GlobalStatus::TES) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write value to the global command register. This function will not wait until the command
|
||||||
|
/// is serviced. User need to check the global status register.
|
||||||
|
fn write_global_command(&mut self, command: GlobalCommand, enable: bool) {
|
||||||
|
const ONE_SHOT_STATUS_MASK: u32 = 0x96FF_FFFF;
|
||||||
|
let status = self.global_status.read() & ONE_SHOT_STATUS_MASK;
|
||||||
|
if enable {
|
||||||
|
self.global_command.write(status | command.bits());
|
||||||
|
} else {
|
||||||
|
self.global_command.write(status & !command.bits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance from base address
|
||||||
|
fn new() -> Option<Self> {
|
||||||
|
let dmar = Dmar::new()?;
|
||||||
|
|
||||||
|
debug!("DMAR:{:#x?}", dmar);
|
||||||
|
let base_address = {
|
||||||
|
let mut addr = 0;
|
||||||
|
for remapping in dmar.remapping_iter() {
|
||||||
|
if let Remapping::Drhd(drhd) = remapping {
|
||||||
|
addr = drhd.register_base_addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if addr == 0 {
|
||||||
|
panic!("There should be a DRHD structure in the DMAR table");
|
||||||
|
}
|
||||||
|
addr
|
||||||
|
};
|
||||||
|
|
||||||
|
let vaddr: usize = paddr_to_vaddr(base_address as usize);
|
||||||
|
// SAFETY: All offsets and sizes are strictly adhered to in the manual, and the base address is obtained from Drhd.
|
||||||
|
let iommu_regs = unsafe {
|
||||||
|
fault::init(vaddr);
|
||||||
|
let version = Volatile::new_read_only(&*(vaddr as *const u32));
|
||||||
|
let capability = Volatile::new_read_only(&*((vaddr + 0x08) as *const u64));
|
||||||
|
let extended_capability: Volatile<&u64, ReadOnly> =
|
||||||
|
Volatile::new_read_only(&*((vaddr + 0x10) as *const u64));
|
||||||
|
let global_command = Volatile::new_write_only(&mut *((vaddr + 0x18) as *mut u32));
|
||||||
|
let global_status = Volatile::new_read_only(&*((vaddr + 0x1C) as *const u32));
|
||||||
|
let root_table_address = Volatile::new(&mut *((vaddr + 0x20) as *mut u64));
|
||||||
|
let context_command = Volatile::new(&mut *((vaddr + 0x28) as *mut u64));
|
||||||
|
Self {
|
||||||
|
version,
|
||||||
|
capability,
|
||||||
|
extended_capability,
|
||||||
|
global_command,
|
||||||
|
global_status,
|
||||||
|
root_table_address,
|
||||||
|
context_command,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("IOMMU registers:{:#x?}", iommu_regs);
|
||||||
|
debug!("IOMMU capability:{:#x?}", iommu_regs.capability());
|
||||||
|
debug!(
|
||||||
|
"IOMMU extend capability:{:#x?}",
|
||||||
|
iommu_regs.extended_capability()
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(iommu_regs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) static IOMMU_REGS: Once<SpinLock<IommuRegisters>> = Once::new();
|
||||||
|
|
||||||
|
pub(super) fn init() -> Result<(), IommuError> {
|
||||||
|
let iommu_regs = IommuRegisters::new().ok_or(IommuError::NoIommu)?;
|
||||||
|
IOMMU_REGS.call_once(|| SpinLock::new(iommu_regs));
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,187 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use log::debug;
|
|
||||||
use spin::Once;
|
|
||||||
use volatile::{
|
|
||||||
access::{ReadOnly, ReadWrite, WriteOnly},
|
|
||||||
Volatile,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{context_table::RootTable, IommuError};
|
|
||||||
use crate::{
|
|
||||||
arch::{
|
|
||||||
iommu::fault,
|
|
||||||
x86::kernel::acpi::{
|
|
||||||
dmar::{Dmar, Remapping},
|
|
||||||
ACPI_TABLES,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mm::paddr_to_vaddr,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RemappingRegisters {
|
|
||||||
version: Volatile<&'static u32, ReadOnly>,
|
|
||||||
capability: Volatile<&'static u64, ReadOnly>,
|
|
||||||
extended_capability: Volatile<&'static u64, ReadOnly>,
|
|
||||||
global_command: Volatile<&'static mut u32, WriteOnly>,
|
|
||||||
global_status: Volatile<&'static u32, ReadOnly>,
|
|
||||||
root_table_address: Volatile<&'static mut u64, ReadWrite>,
|
|
||||||
context_command: Volatile<&'static mut u64, ReadWrite>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RemappingRegisters {
|
|
||||||
pub fn capability(&self) -> Capability {
|
|
||||||
Capability::from_bits_truncate(self.capability.read())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a instance from base address
|
|
||||||
fn new(root_table: &RootTable) -> Option<Self> {
|
|
||||||
let dmar = Dmar::new()?;
|
|
||||||
let acpi_table_lock = ACPI_TABLES.get().unwrap().lock();
|
|
||||||
|
|
||||||
debug!("DMAR:{:#x?}", dmar);
|
|
||||||
let base_address = {
|
|
||||||
let mut addr = 0;
|
|
||||||
for remapping in dmar.remapping_iter() {
|
|
||||||
if let Remapping::Drhd(drhd) = remapping {
|
|
||||||
addr = drhd.register_base_addr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if addr == 0 {
|
|
||||||
panic!("There should be a DRHD structure in the DMAR table");
|
|
||||||
}
|
|
||||||
addr
|
|
||||||
};
|
|
||||||
|
|
||||||
let vaddr: usize = paddr_to_vaddr(base_address as usize);
|
|
||||||
// SAFETY: All offsets and sizes are strictly adhered to in the manual, and the base address is obtained from Drhd.
|
|
||||||
let mut remapping_reg = unsafe {
|
|
||||||
fault::init(vaddr);
|
|
||||||
let version = Volatile::new_read_only(&*(vaddr as *const u32));
|
|
||||||
let capability = Volatile::new_read_only(&*((vaddr + 0x08) as *const u64));
|
|
||||||
let extended_capability: Volatile<&u64, ReadOnly> =
|
|
||||||
Volatile::new_read_only(&*((vaddr + 0x10) as *const u64));
|
|
||||||
let global_command = Volatile::new_write_only(&mut *((vaddr + 0x18) as *mut u32));
|
|
||||||
let global_status = Volatile::new_read_only(&*((vaddr + 0x1C) as *const u32));
|
|
||||||
let root_table_address = Volatile::new(&mut *((vaddr + 0x20) as *mut u64));
|
|
||||||
let context_command = Volatile::new(&mut *((vaddr + 0x28) as *mut u64));
|
|
||||||
Self {
|
|
||||||
version,
|
|
||||||
capability,
|
|
||||||
extended_capability,
|
|
||||||
global_command,
|
|
||||||
global_status,
|
|
||||||
root_table_address,
|
|
||||||
context_command,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// write remapping register
|
|
||||||
remapping_reg
|
|
||||||
.root_table_address
|
|
||||||
.write(root_table.paddr() as u64);
|
|
||||||
// start writing
|
|
||||||
remapping_reg.global_command.write(0x4000_0000);
|
|
||||||
// wait until complete
|
|
||||||
while remapping_reg.global_status.read() & 0x4000_0000 == 0 {}
|
|
||||||
|
|
||||||
// enable iommu
|
|
||||||
remapping_reg.global_command.write(0x8000_0000);
|
|
||||||
|
|
||||||
debug!("IOMMU registers:{:#x?}", remapping_reg);
|
|
||||||
|
|
||||||
Some(remapping_reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct Capability : u64{
|
|
||||||
/// Number of domain support.
|
|
||||||
///
|
|
||||||
/// ```norun
|
|
||||||
/// 0 => 4-bit domain-ids with support for up to 16 domains.
|
|
||||||
/// 1 => 6-bit domain-ids with support for up to 64 domains.
|
|
||||||
/// 2 => 8-bit domain-ids with support for up to 256 domains.
|
|
||||||
/// 3 => 10-bit domain-ids with support for up to 1024 domains.
|
|
||||||
/// 4 => 12-bit domain-ids with support for up to 4K domains.
|
|
||||||
/// 5 => 14-bit domain-ids with support for up to 16K domains.
|
|
||||||
/// 6 => 16-bit domain-ids with support for up to 64K domains.
|
|
||||||
/// 7 => Reserved.
|
|
||||||
/// ```
|
|
||||||
const ND = 0x7;
|
|
||||||
/// Required Write-Buffer Flushing.
|
|
||||||
const RWBF = 1 << 4;
|
|
||||||
/// Protected Low-Memory Region
|
|
||||||
const PLMR = 1 << 5;
|
|
||||||
/// Protected High-Memory Region
|
|
||||||
const PHMR = 1 << 6;
|
|
||||||
/// Caching Mode
|
|
||||||
const CM = 1 << 7;
|
|
||||||
/// Supported Adjusted Guest Address Widths.
|
|
||||||
/// ```norun
|
|
||||||
/// 0/4 => Reserved
|
|
||||||
/// 1 => 39-bit AGAW (3-level page-table)
|
|
||||||
/// 2 => 48-bit AGAW (4-level page-table)
|
|
||||||
/// 3 => 57-bit AGAW (5-level page-table)
|
|
||||||
/// ```
|
|
||||||
const SAGAW = 0x1F << 8;
|
|
||||||
/// Maximum Guest Address Width.
|
|
||||||
/// The maximum guest physical address width supported by second-stage translation in remapping hardware.
|
|
||||||
/// MGAW is computed as (N+1), where N is the valued reported in this field.
|
|
||||||
const MGAW = 0x3F << 16;
|
|
||||||
/// Zero Length Read. Whether the remapping hardware unit supports zero length
|
|
||||||
/// DMA read requests to write-only pages.
|
|
||||||
const ZLR = 1 << 22;
|
|
||||||
/// Fault-recording Register offset, specifies the offset of the first fault recording register
|
|
||||||
/// relative to the register base address of this remapping hardware unit.
|
|
||||||
///
|
|
||||||
/// If the register base address is X, and the value reported in this field
|
|
||||||
/// is Y, the address for the first fault recording register is calculated as X+(16*Y).
|
|
||||||
const FRO = 0x3FF << 24;
|
|
||||||
/// Second Stage Large Page Support.
|
|
||||||
/// ```norun
|
|
||||||
/// 2/3 => Reserved
|
|
||||||
/// 0 => 21-bit offset to page frame(2MB)
|
|
||||||
/// 1 => 30-bit offset to page frame(1GB)
|
|
||||||
/// ```
|
|
||||||
const SSLPS = 0xF << 34;
|
|
||||||
/// Page Selective Invalidation. Whether hardware supports page-selective invalidation for IOTLB.
|
|
||||||
const PSI = 1 << 39;
|
|
||||||
/// Number of Fault-recording Registers. Number of fault recording registers is computed as N+1.
|
|
||||||
const NFR = 0xFF << 40;
|
|
||||||
/// Maximum Address Mask Value, indicates the maximum supported value for the
|
|
||||||
/// Address Mask (AM) field in the Invalidation Address register
|
|
||||||
/// (IVA_REG), and IOTLB Invalidation Descriptor (iotlb_inv_dsc) used
|
|
||||||
/// for invalidations of second-stage translation.
|
|
||||||
const MAMV = 0x3F << 48;
|
|
||||||
/// Write Draining.
|
|
||||||
const DWD = 1 << 54;
|
|
||||||
/// Read Draining.
|
|
||||||
const DRD = 1 << 55;
|
|
||||||
/// First Stage 1-GByte Page Support.
|
|
||||||
const FS1GP = 1 << 56;
|
|
||||||
/// Posted Interrupts Support.
|
|
||||||
const PI = 1 << 59;
|
|
||||||
/// First Stage 5-level Paging Support.
|
|
||||||
const FS5LP = 1 << 60;
|
|
||||||
/// Enhanced Command Support.
|
|
||||||
const ECMDS = 1 << 61;
|
|
||||||
/// Enhanced Set Interrupt Remap Table Pointer Support.
|
|
||||||
const ESIRTPS = 1 << 62;
|
|
||||||
/// Enhanced Set Root Table Pointer Support.
|
|
||||||
const ESRTPS = 1 << 63;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static REMAPPING_REGS: Once<RemappingRegisters> = Once::new();
|
|
||||||
|
|
||||||
pub(super) fn init(root_table: &RootTable) -> Result<(), IommuError> {
|
|
||||||
let remapping_regs = RemappingRegisters::new(root_table).ok_or(IommuError::NoIommu)?;
|
|
||||||
REMAPPING_REGS.call_once(|| remapping_regs);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Reference in New Issue
Block a user