From 8b78f39c6fa39236d8c470795ce4a41e1a00eed3 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 6 Jan 2026 13:38:32 +0900 Subject: [PATCH 1/6] fix(interrupt remap): fix the initialization of interrupt remapping 1. do not use the global status register value for the global command register 2. wait untile enabled after updatating the global command register 3. print more registers for debugging Signed-off-by: Yuuki Takano --- .../src/arch/x86_64/interrupt_remap.rs | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index bb4667ac8..179d51d34 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -1,6 +1,7 @@ //! Interrupt remapping of Vt-d. use acpi::AcpiTables; +use alloc::format; use crate::{ addr::{phy_addr::PhyAddr, virt_addr::VirtAddr, Addr}, @@ -60,6 +61,7 @@ mod registers { } } + mmio_r!(offset 0x10 => pub EXTENDED_CAPABILITY); mmio_w!(offset 0x18 => pub GLOBAL_COMMAND); mmio_r!(offset 0x1c => pub GLOBAL_STATUS); mmio_rw!(offset 0xb8 => pub IRTA); @@ -284,18 +286,53 @@ fn set_irta( registers::IRTA.write(val, vt_d_base.as_usize()); - let mut stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - stat |= registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE; + let stat = registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE; registers::GLOBAL_COMMAND.write(stat, vt_d_base.as_usize()); + // Wait until enabled + for _ in 0..100 { + let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + if stat.contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE) + { + break; + } + } + + if !registers::GLOBAL_STATUS + .read(vt_d_base.as_usize()) + .contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE) + { + log::error!("Failed to enable Vt-d Interrupt Remapping."); + return; + } + + let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + + let mut scope_str = format!(""); + + for scope in drhd.device_scopes() { + let (device_scope, path) = scope; + scope_str = format!( + "{scope_str}\r\nVt-d Device Scope: entry type = {}, length = {}, flags = 0x{:x}, enumeration ID = {}, start bus number = {}, path = {path:x?}", + device_scope.entry_type, + device_scope.length, + device_scope.flags, + device_scope.enumeration_id, + device_scope.start_bus_number, + ); + } + log::info!( - "Vt-d Interrupt Remapping: Segment = {}, Vt-d Base = 0x{:x}, Table PhyAddr = 0x{:x}, enabled = {}, mode = {}", - segment_number, - drhd.register_base_address as usize, - phy_addr.as_usize(), - stat.contains(registers::GlobalCommandStatus::IRE | registers::GlobalCommandStatus::IRTP), - if is_x2apic { "x2APIC" } else { "xAPIC" }, - ) + "Vt-d Interrupt Remapping: Segment = {segment_number}, Vt-d Base = 0x{:x}, Table PhyAddr = 0x{:x}, enabled = {}, mode = {}, extended capability = 0x{:x}, global status = 0x{:x}, IRTA = 0x{:x}, DHRD.flags = 0x{:x}{scope_str}", + drhd.register_base_address as usize, + phy_addr.as_usize(), + stat.contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE), + if is_x2apic { "x2APIC" } else { "xAPIC" }, + registers::EXTENDED_CAPABILITY.read(vt_d_base.as_usize()), + stat.bits(), + registers::IRTA.read(vt_d_base.as_usize()), + drhd.flags + ) } pub fn allocate_remapping_entry( From b3907017c59ffcb8788db4af48938ba46211b603 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 6 Jan 2026 15:28:56 +0900 Subject: [PATCH 2/6] feat(iommu): initialize invalidate queue Signed-off-by: Yuuki Takano --- .../src/arch/x86_64/interrupt_remap.rs | 123 +++++++++++++++--- 1 file changed, 106 insertions(+), 17 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index 179d51d34..06cf8340b 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -2,6 +2,7 @@ use acpi::AcpiTables; use alloc::format; +use alloc::string::String; use crate::{ addr::{phy_addr::PhyAddr, virt_addr::VirtAddr, Addr}, @@ -18,6 +19,30 @@ use super::acpi::AcpiMapper; const TABLE_SIZE: usize = 256 * 4096; // 1MiB const TABLE_ENTRY_NUM: usize = TABLE_SIZE / 16; +/// Error type for Vt-d interrupt remapping initialization. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VtdError { + /// Failed to enable Interrupt Remapping Table Pointer. + IrtpEnableFailed, + /// Failed to enable Interrupt Remapping. + IreEnableFailed, + /// Failed to enable Queued Invalidation. + QieEnableFailed, + /// Failed to allocate DMA memory. + DmaAllocationFailed, +} + +impl core::fmt::Display for VtdError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + VtdError::IrtpEnableFailed => write!(f, "Failed to enable IRTP"), + VtdError::IreEnableFailed => write!(f, "Failed to enable IRE"), + VtdError::QieEnableFailed => write!(f, "Failed to enable QIE"), + VtdError::DmaAllocationFailed => write!(f, "DMA allocation failed"), + } + } +} + #[allow(dead_code)] mod registers { use bitflags::bitflags; @@ -64,6 +89,9 @@ mod registers { mmio_r!(offset 0x10 => pub EXTENDED_CAPABILITY); mmio_w!(offset 0x18 => pub GLOBAL_COMMAND); mmio_r!(offset 0x1c => pub GLOBAL_STATUS); + mmio_rw!(offset 0x80 => pub IQH); // Invalidation Queue Head + mmio_rw!(offset 0x88 => pub IQT); // Invalidation Queue Tail + mmio_rw!(offset 0x90 => pub IQA); // Invalidation Queue Address mmio_rw!(offset 0xb8 => pub IRTA); } @@ -218,11 +246,11 @@ pub unsafe fn init_interrupt_remap( phy_offset: VirtAddr, acpi: &AcpiTables, is_x2apic: bool, -) -> Result<(), &'static str> { +) -> Result<(), VtdError> { let mut remap_table = [None; 32]; if let Ok(dmar) = acpi.find_table::() { - dmar.entries().for_each(|entry| { + for entry in dmar.entries() { if let DmarEntry::Drhd(drhd) = entry { drhd.device_scopes() .find(|(scope, _path)| scope.entry_type == 1); @@ -230,25 +258,25 @@ pub unsafe fn init_interrupt_remap( if let Some((phy_addr, _virt_addr)) = &remap_table[drhd.segment_number as usize] { let segment_number = drhd.segment_number as usize; // Set Interrupt Remapping Table Address Register - set_irta(segment_number, phy_offset, drhd, *phy_addr, is_x2apic); + set_irta(segment_number, phy_offset, drhd, *phy_addr, is_x2apic)?; } else { let segment_number = drhd.segment_number as usize; let pool = DMAPool::<[u8; TABLE_SIZE]>::new(segment_number, TABLE_SIZE / PAGESIZE) - .expect("DMAPool::new() failed."); + .ok_or(VtdError::DmaAllocationFailed)?; let virt_addr = pool.get_virt_addr(); let phy_addr = pool.get_phy_addr(); pool.leak(); // Set Interrupt Remapping Table Address Register - set_irta(segment_number, phy_offset, drhd, phy_addr, is_x2apic); + set_irta(segment_number, phy_offset, drhd, phy_addr, is_x2apic)?; remap_table[segment_number] = Some((phy_addr, virt_addr)); } } - }); + } } // Set INTERRUPT_REMAPPING @@ -277,7 +305,7 @@ fn set_irta( drhd: &DmarDrhd, phy_addr: PhyAddr, is_x2apic: bool, -) { +) -> Result<(), VtdError> { let vt_d_base = phy_offset + drhd.register_base_address as usize; // Extended Interrupt Mode (x2APIC) Enable @@ -298,17 +326,23 @@ fn set_irta( } } - if !registers::GLOBAL_STATUS - .read(vt_d_base.as_usize()) - .contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE) - { + let status = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + if !status.contains(registers::GlobalCommandStatus::IRTP) { + log::error!("Failed to enable Vt-d Interrupt Remapping Table Pointer."); + return Err(VtdError::IrtpEnableFailed); + } + if !status.contains(registers::GlobalCommandStatus::IRE) { log::error!("Failed to enable Vt-d Interrupt Remapping."); - return; + return Err(VtdError::IreEnableFailed); } - let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + // Initialize Invalidation Queue + init_invalidation_queue(segment_number, vt_d_base)?; + + // Re-read status after queue initialization + let status = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - let mut scope_str = format!(""); + let mut scope_str = String::new(); for scope in drhd.device_scopes() { let (device_scope, path) = scope; @@ -326,13 +360,68 @@ fn set_irta( "Vt-d Interrupt Remapping: Segment = {segment_number}, Vt-d Base = 0x{:x}, Table PhyAddr = 0x{:x}, enabled = {}, mode = {}, extended capability = 0x{:x}, global status = 0x{:x}, IRTA = 0x{:x}, DHRD.flags = 0x{:x}{scope_str}", drhd.register_base_address as usize, phy_addr.as_usize(), - stat.contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE), + status.contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE), if is_x2apic { "x2APIC" } else { "xAPIC" }, registers::EXTENDED_CAPABILITY.read(vt_d_base.as_usize()), - stat.bits(), + status.bits(), registers::IRTA.read(vt_d_base.as_usize()), drhd.flags - ) + ); + + Ok(()) +} + +fn init_invalidation_queue(segment_number: usize, vt_d_base: VirtAddr) -> Result<(), VtdError> { + // Reset Invalidation Queue Tail + registers::IQT.write(0, vt_d_base.as_usize()); + + // Allocate a page for the Invalidation Queue + let pool = + DMAPool::<[u8; PAGESIZE]>::new(segment_number, 1).ok_or(VtdError::DmaAllocationFailed)?; + + let phy_addr = pool.get_phy_addr().as_usize() as u64; + + // Set Invalidation Queue Address Register + // Bits [11:0] = Queue Size (0 = 4KB, 1 page) + // Bits [63:12] = Base Address + // Queue size = 0 (4KB, 256 entries) + let iqa_value = phy_addr & !((1 << 12) - 1); + registers::IQA.write(iqa_value, vt_d_base.as_usize()); + + // Leak the pool to keep it allocated + pool.leak(); + + // Enable Queued Invalidation + let stat = registers::GlobalCommandStatus::QIE; + registers::GLOBAL_COMMAND.write(stat, vt_d_base.as_usize()); + + // Wait until Queued Invalidation is enabled + for _ in 0..100 { + let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + if stat.contains(registers::GlobalCommandStatus::QIE) { + break; + } + } + + if !registers::GLOBAL_STATUS + .read(vt_d_base.as_usize()) + .contains(registers::GlobalCommandStatus::QIE) + { + log::error!( + "Failed to enable Vt-d Queued Invalidation for segment {}.", + segment_number + ); + return Err(VtdError::QieEnableFailed); + } + + log::info!( + "Vt-d Queued Invalidation enabled: Segment = {}, Queue PhyAddr = 0x{:x}, IQA = 0x{:x}", + segment_number, + phy_addr, + registers::IQA.read(vt_d_base.as_usize()) + ); + + Ok(()) } pub fn allocate_remapping_entry( From 6e350be0ea309a2c9b5e0ad37f72eb243af193eb Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 6 Jan 2026 15:39:02 +0900 Subject: [PATCH 3/6] fix(iommu): print IQH and IQT of QIE Signed-off-by: Yuuki Takano --- awkernel_lib/src/arch/x86_64/interrupt_remap.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index 06cf8340b..b3f647909 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -415,10 +415,12 @@ fn init_invalidation_queue(segment_number: usize, vt_d_base: VirtAddr) -> Result } log::info!( - "Vt-d Queued Invalidation enabled: Segment = {}, Queue PhyAddr = 0x{:x}, IQA = 0x{:x}", + "Vt-d Queued Invalidation enabled: Segment = {}, Queue PhyAddr = 0x{:x}, IQA = 0x{:x}, IQH = {}, IQT = {}", segment_number, phy_addr, - registers::IQA.read(vt_d_base.as_usize()) + registers::IQA.read(vt_d_base.as_usize()), + registers::IQH.read(vt_d_base.as_usize()), + registers::IQT.read(vt_d_base.as_usize()) ); Ok(()) From 5eca6d2d4425d0734765b78055d458ff2ad2ff81 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 6 Jan 2026 16:41:06 +0900 Subject: [PATCH 4/6] fix(iommu): add update_global_command() Signed-off-by: Yuuki Takano --- .../src/arch/x86_64/interrupt_remap.rs | 146 ++++++++++++------ 1 file changed, 100 insertions(+), 46 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index b3f647909..392472485 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -30,6 +30,8 @@ pub enum VtdError { QieEnableFailed, /// Failed to allocate DMA memory. DmaAllocationFailed, + /// Timeout waiting for command completion. + Timeout, } impl core::fmt::Display for VtdError { @@ -39,6 +41,7 @@ impl core::fmt::Display for VtdError { VtdError::IreEnableFailed => write!(f, "Failed to enable IRE"), VtdError::QieEnableFailed => write!(f, "Failed to enable QIE"), VtdError::DmaAllocationFailed => write!(f, "DMA allocation failed"), + VtdError::Timeout => write!(f, "Timeout waiting for command completion"), } } } @@ -298,6 +301,93 @@ pub unsafe fn init_interrupt_remap( Ok(()) } +/// Update GLOBAL_COMMAND register following Intel VT-d specification. +/// +/// Steps: +/// 1. Read GLOBAL_STATUS register +/// 2. Reset one-shot bits (keeping only persistent bits) +/// 3. Set/clear the target bit +/// 4. Write to GLOBAL_COMMAND register +/// 5. Wait until GLOBAL_STATUS indicates command is serviced +fn update_global_command( + vt_d_base: VirtAddr, + bit_to_set: registers::GlobalCommandStatus, + enable: bool, +) -> Result<(), VtdError> { + // Step 1: Read GLOBAL_STATUS + let status = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + + // Step 2: Reset one-shot bits (preserve only bits 31, 30, 27, 26, 25, 24, 23) + // One-shot bits mask: 0x96FFFFFF (bits that should be preserved) + let persistent_mask = registers::GlobalCommandStatus::from_bits_truncate(0x96FF_FFFF); + let status_persistent = status & persistent_mask; + + // Step 3: Set or clear the target bit + let command = if enable { + status_persistent | bit_to_set + } else { + status_persistent & !bit_to_set + }; + + // Step 4: Write to GLOBAL_COMMAND + registers::GLOBAL_COMMAND.write(command, vt_d_base.as_usize()); + + // Step 5: Wait until GLOBAL_STATUS indicates command is serviced + if wait_toggle_then_set(vt_d_base, bit_to_set, enable).is_err() { + // Timeout: bit not updated as expected + let current = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); + Err( + if bit_to_set.contains(registers::GlobalCommandStatus::IRTP) + && !current.contains(registers::GlobalCommandStatus::IRTP) + { + VtdError::IrtpEnableFailed + } else if bit_to_set.contains(registers::GlobalCommandStatus::IRE) + && !current.contains(registers::GlobalCommandStatus::IRE) + { + VtdError::IreEnableFailed + } else if bit_to_set.contains(registers::GlobalCommandStatus::QIE) + && !current.contains(registers::GlobalCommandStatus::QIE) + { + VtdError::QieEnableFailed + } else { + VtdError::Timeout // Default fallback + }, + ) + } else { + Ok(()) + } +} + +fn wait_toggle_then_set( + vt_d_base: VirtAddr, + status_bit: registers::GlobalCommandStatus, + enable: bool, +) -> Result<(), VtdError> { + if enable { + // wait until 1 + for _ in 0..1000 { + if registers::GLOBAL_STATUS + .read(vt_d_base.as_usize()) + .contains(status_bit) + { + return Ok(()); + } + } + } else { + // wait until 0 + for _ in 0..1000 { + if !registers::GLOBAL_STATUS + .read(vt_d_base.as_usize()) + .contains(status_bit) + { + return Ok(()); + } + } + } + + Err(VtdError::Timeout) +} + /// Set Interrupt Remapping Table Address Register. fn set_irta( segment_number: usize, @@ -314,27 +404,11 @@ fn set_irta( registers::IRTA.write(val, vt_d_base.as_usize()); - let stat = registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE; - registers::GLOBAL_COMMAND.write(stat, vt_d_base.as_usize()); + // Enable IRTP (Interrupt Remapping Table Pointer) + update_global_command(vt_d_base, registers::GlobalCommandStatus::IRTP, true)?; - // Wait until enabled - for _ in 0..100 { - let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - if stat.contains(registers::GlobalCommandStatus::IRTP | registers::GlobalCommandStatus::IRE) - { - break; - } - } - - let status = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - if !status.contains(registers::GlobalCommandStatus::IRTP) { - log::error!("Failed to enable Vt-d Interrupt Remapping Table Pointer."); - return Err(VtdError::IrtpEnableFailed); - } - if !status.contains(registers::GlobalCommandStatus::IRE) { - log::error!("Failed to enable Vt-d Interrupt Remapping."); - return Err(VtdError::IreEnableFailed); - } + // Enable IRE (Interrupt Remapping Enable) + update_global_command(vt_d_base, registers::GlobalCommandStatus::IRE, true)?; // Initialize Invalidation Queue init_invalidation_queue(segment_number, vt_d_base)?; @@ -381,38 +455,18 @@ fn init_invalidation_queue(segment_number: usize, vt_d_base: VirtAddr) -> Result let phy_addr = pool.get_phy_addr().as_usize() as u64; - // Set Invalidation Queue Address Register - // Bits [11:0] = Queue Size (0 = 4KB, 1 page) - // Bits [63:12] = Base Address - // Queue size = 0 (4KB, 256 entries) + // bits[63:12] = Base (4KB aligned) + // bit[11] = DW (128/256) + // bits[2:0] = queue size (0 = 4KB) + // bits[10:3] = Reserved let iqa_value = phy_addr & !((1 << 12) - 1); registers::IQA.write(iqa_value, vt_d_base.as_usize()); // Leak the pool to keep it allocated pool.leak(); - // Enable Queued Invalidation - let stat = registers::GlobalCommandStatus::QIE; - registers::GLOBAL_COMMAND.write(stat, vt_d_base.as_usize()); - - // Wait until Queued Invalidation is enabled - for _ in 0..100 { - let stat = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - if stat.contains(registers::GlobalCommandStatus::QIE) { - break; - } - } - - if !registers::GLOBAL_STATUS - .read(vt_d_base.as_usize()) - .contains(registers::GlobalCommandStatus::QIE) - { - log::error!( - "Failed to enable Vt-d Queued Invalidation for segment {}.", - segment_number - ); - return Err(VtdError::QieEnableFailed); - } + // Enable Queued Invalidation (QIE) + update_global_command(vt_d_base, registers::GlobalCommandStatus::QIE, true)?; log::info!( "Vt-d Queued Invalidation enabled: Segment = {}, Queue PhyAddr = 0x{:x}, IQA = 0x{:x}, IQH = {}, IQT = {}", From 5b2be9499d5641c1a7301e98081c3af8e76738b5 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 6 Jan 2026 16:51:31 +0900 Subject: [PATCH 5/6] fix(iommu): Wait for register-based invalidation to complete Signed-off-by: Yuuki Takano --- .../src/arch/x86_64/interrupt_remap.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index 392472485..05edea17c 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -95,6 +95,7 @@ mod registers { mmio_rw!(offset 0x80 => pub IQH); // Invalidation Queue Head mmio_rw!(offset 0x88 => pub IQT); // Invalidation Queue Tail mmio_rw!(offset 0x90 => pub IQA); // Invalidation Queue Address + mmio_r!(offset 0x9c => pub ICS); // Invalidation Completion Status mmio_rw!(offset 0xb8 => pub IRTA); } @@ -388,6 +389,24 @@ fn wait_toggle_then_set( Err(VtdError::Timeout) } +/// Wait for register-based invalidation to complete. +/// This must be called before enabling Queued Invalidation. +fn wait_register_based_invalidation_complete(vt_d_base: VirtAddr) -> Result<(), VtdError> { + const ICS_IWC: u32 = 1; // Invalidation Wait Descriptor Complete + + // Wait for any pending register-based invalidation to complete + // by checking the ICS register's IWC bit is 0 + for _ in 0..1000 { + let ics = registers::ICS.read(vt_d_base.as_usize()); + if (ics & ICS_IWC) == 0 { + return Ok(()); + } + } + + log::error!("Timeout waiting for register-based invalidation to complete"); + Err(VtdError::Timeout) +} + /// Set Interrupt Remapping Table Address Register. fn set_irta( segment_number: usize, @@ -446,6 +465,10 @@ fn set_irta( } fn init_invalidation_queue(segment_number: usize, vt_d_base: VirtAddr) -> Result<(), VtdError> { + // Wait for any pending register-based invalidation to complete + // before enabling Queued Invalidation + wait_register_based_invalidation_complete(vt_d_base)?; + // Reset Invalidation Queue Tail registers::IQT.write(0, vt_d_base.as_usize()); From e2bee2318a4466ceb4af5d9291a684fa6f49094e Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 13 Jan 2026 11:45:28 +0900 Subject: [PATCH 6/6] fix(vt-d): reflect the review Signed-off-by: Yuuki Takano --- .../src/arch/x86_64/interrupt_remap.rs | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs index 05edea17c..dcf75a29e 100644 --- a/awkernel_lib/src/arch/x86_64/interrupt_remap.rs +++ b/awkernel_lib/src/arch/x86_64/interrupt_remap.rs @@ -24,10 +24,16 @@ const TABLE_ENTRY_NUM: usize = TABLE_SIZE / 16; pub enum VtdError { /// Failed to enable Interrupt Remapping Table Pointer. IrtpEnableFailed, + /// Failed to disable Interrupt Remapping Table Pointer. + IrtpDisableFailed, /// Failed to enable Interrupt Remapping. IreEnableFailed, + /// Failed to disable Interrupt Remapping. + IreDisableFailed, /// Failed to enable Queued Invalidation. QieEnableFailed, + /// Failed to disable Queued Invalidation. + QieDisableFailed, /// Failed to allocate DMA memory. DmaAllocationFailed, /// Timeout waiting for command completion. @@ -38,8 +44,11 @@ impl core::fmt::Display for VtdError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { VtdError::IrtpEnableFailed => write!(f, "Failed to enable IRTP"), + VtdError::IrtpDisableFailed => write!(f, "Failed to disable IRTP"), VtdError::IreEnableFailed => write!(f, "Failed to enable IRE"), + VtdError::IreDisableFailed => write!(f, "Failed to disable IRE"), VtdError::QieEnableFailed => write!(f, "Failed to enable QIE"), + VtdError::QieDisableFailed => write!(f, "Failed to disable QIE"), VtdError::DmaAllocationFailed => write!(f, "DMA allocation failed"), VtdError::Timeout => write!(f, "Timeout waiting for command completion"), } @@ -89,6 +98,8 @@ mod registers { } } + pub const ICS_IWC: u32 = 1; // Invalidation Wait Descriptor Complete + mmio_r!(offset 0x10 => pub EXTENDED_CAPABILITY); mmio_w!(offset 0x18 => pub GLOBAL_COMMAND); mmio_r!(offset 0x1c => pub GLOBAL_STATUS); @@ -261,8 +272,14 @@ pub unsafe fn init_interrupt_remap( if let Some((phy_addr, _virt_addr)) = &remap_table[drhd.segment_number as usize] { let segment_number = drhd.segment_number as usize; - // Set Interrupt Remapping Table Address Register - set_irta(segment_number, phy_offset, drhd, *phy_addr, is_x2apic)?; + // Enable Interrupt Remapping + enable_interrupt_remapping( + segment_number, + phy_offset, + drhd, + *phy_addr, + is_x2apic, + )?; } else { let segment_number = drhd.segment_number as usize; @@ -274,8 +291,14 @@ pub unsafe fn init_interrupt_remap( let phy_addr = pool.get_phy_addr(); pool.leak(); - // Set Interrupt Remapping Table Address Register - set_irta(segment_number, phy_offset, drhd, phy_addr, is_x2apic)?; + // Enable Interrupt Remapping + enable_interrupt_remapping( + segment_number, + phy_offset, + drhd, + phy_addr, + is_x2apic, + )?; remap_table[segment_number] = Some((phy_addr, virt_addr)); } @@ -318,7 +341,7 @@ fn update_global_command( // Step 1: Read GLOBAL_STATUS let status = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); - // Step 2: Reset one-shot bits (preserve only bits 31, 30, 27, 26, 25, 24, 23) + // Step 2: Reset one-shot bits // One-shot bits mask: 0x96FFFFFF (bits that should be preserved) let persistent_mask = registers::GlobalCommandStatus::from_bits_truncate(0x96FF_FFFF); let status_persistent = status & persistent_mask; @@ -339,17 +362,29 @@ fn update_global_command( let current = registers::GLOBAL_STATUS.read(vt_d_base.as_usize()); Err( if bit_to_set.contains(registers::GlobalCommandStatus::IRTP) - && !current.contains(registers::GlobalCommandStatus::IRTP) + && (enable && !current.contains(registers::GlobalCommandStatus::IRTP)) { VtdError::IrtpEnableFailed + } else if bit_to_set.contains(registers::GlobalCommandStatus::IRTP) + && (!enable && current.contains(registers::GlobalCommandStatus::IRTP)) + { + VtdError::IrtpDisableFailed } else if bit_to_set.contains(registers::GlobalCommandStatus::IRE) - && !current.contains(registers::GlobalCommandStatus::IRE) + && (enable && !current.contains(registers::GlobalCommandStatus::IRE)) { VtdError::IreEnableFailed + } else if bit_to_set.contains(registers::GlobalCommandStatus::IRE) + && (!enable && current.contains(registers::GlobalCommandStatus::IRE)) + { + VtdError::IreDisableFailed } else if bit_to_set.contains(registers::GlobalCommandStatus::QIE) - && !current.contains(registers::GlobalCommandStatus::QIE) + && (enable && !current.contains(registers::GlobalCommandStatus::QIE)) { VtdError::QieEnableFailed + } else if bit_to_set.contains(registers::GlobalCommandStatus::QIE) + && (!enable && current.contains(registers::GlobalCommandStatus::QIE)) + { + VtdError::QieDisableFailed } else { VtdError::Timeout // Default fallback }, @@ -392,13 +427,11 @@ fn wait_toggle_then_set( /// Wait for register-based invalidation to complete. /// This must be called before enabling Queued Invalidation. fn wait_register_based_invalidation_complete(vt_d_base: VirtAddr) -> Result<(), VtdError> { - const ICS_IWC: u32 = 1; // Invalidation Wait Descriptor Complete - // Wait for any pending register-based invalidation to complete // by checking the ICS register's IWC bit is 0 for _ in 0..1000 { let ics = registers::ICS.read(vt_d_base.as_usize()); - if (ics & ICS_IWC) == 0 { + if (ics & registers::ICS_IWC) == 0 { return Ok(()); } } @@ -408,7 +441,12 @@ fn wait_register_based_invalidation_complete(vt_d_base: VirtAddr) -> Result<(), } /// Set Interrupt Remapping Table Address Register. -fn set_irta( +/// +/// 1. Set IRTA register +/// 2. Enable IRTP (Interrupt Remapping Table Pointer) +/// 3. Enable IRE (Interrupt Remapping Enable) +/// 4. Initialize Invalidation Queue +fn enable_interrupt_remapping( segment_number: usize, phy_offset: VirtAddr, drhd: &DmarDrhd,