From 0bf359596494966a553d37d2a5cb1e7885ec0d5e Mon Sep 17 00:00:00 2001 From: Yuke Peng Date: Thu, 18 Jul 2024 20:48:56 +0800 Subject: [PATCH] Extract IOMMU register operations --- ostd/src/arch/x86/iommu/fault.rs | 11 +- ostd/src/arch/x86/iommu/mod.rs | 4 +- ostd/src/arch/x86/iommu/registers/mod.rs | 171 +++++++++++++++++++++ ostd/src/arch/x86/iommu/remapping.rs | 187 ----------------------- 4 files changed, 181 insertions(+), 192 deletions(-) create mode 100644 ostd/src/arch/x86/iommu/registers/mod.rs delete mode 100644 ostd/src/arch/x86/iommu/remapping.rs diff --git a/ostd/src/arch/x86/iommu/fault.rs b/ostd/src/arch/x86/iommu/fault.rs index 94f884da7..daea41f7f 100644 --- a/ostd/src/arch/x86/iommu/fault.rs +++ b/ostd/src/arch/x86/iommu/fault.rs @@ -12,7 +12,7 @@ use spin::Once; use trapframe::TrapFrame; use volatile::{access::ReadWrite, Volatile}; -use super::remapping::Capability; +use super::registers::Capability; use crate::{mm::Vaddr, trap::IrqLine}; #[derive(Debug)] @@ -37,10 +37,13 @@ impl FaultEventRegisters { /// /// User must ensure the base_register_vaddr is read from DRHD unsafe fn new(base_register_vaddr: Vaddr) -> Self { - let capability = Volatile::new_read_only(&*((base_register_vaddr + 0x08) as *const u64)); - let length = ((capability.read() & Capability::NFR.bits()) >> 40) + 1; + let capability_reg = + 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 offset = (capability.read() & 0x3_ff00_0000) >> 24; + let offset = capability.fault_recording_register_offset(); for i in 0..length { recordings.push(Volatile::new( &mut *((base_register_vaddr + 16 * (offset + i) as usize) as *mut u128), diff --git a/ostd/src/arch/x86/iommu/mod.rs b/ostd/src/arch/x86/iommu/mod.rs index cd771084e..752b4c696 100644 --- a/ostd/src/arch/x86/iommu/mod.rs +++ b/ostd/src/arch/x86/iommu/mod.rs @@ -4,7 +4,7 @@ mod context_table; mod fault; -mod remapping; +mod registers; mod second_stage; use log::info; @@ -65,6 +65,8 @@ pub(crate) fn unmap(daddr: Daddr) -> Result<(), IommuError> { } pub(crate) fn init() -> Result<(), IommuError> { + registers::init()?; + let mut root_table = RootTable::new(); // For all PCI Device, use the same page table. let page_table = PageTable::::empty(); diff --git a/ostd/src/arch/x86/iommu/registers/mod.rs b/ostd/src/arch/x86/iommu/registers/mod.rs new file mode 100644 index 000000000..8a8f6f847 --- /dev/null +++ b/ostd/src/arch/x86/iommu/registers/mod.rs @@ -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) { + // 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 { + 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> = 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(()) +} diff --git a/ostd/src/arch/x86/iommu/remapping.rs b/ostd/src/arch/x86/iommu/remapping.rs deleted file mode 100644 index 46925089e..000000000 --- a/ostd/src/arch/x86/iommu/remapping.rs +++ /dev/null @@ -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 { - 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 = 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(()) -}