diff --git a/src/arch/x86/device/ioapic.rs b/src/arch/x86/device/ioapic.rs deleted file mode 100644 index 8b56a07e6d..0000000000 --- a/src/arch/x86/device/ioapic.rs +++ /dev/null @@ -1,441 +0,0 @@ -use core::{fmt, ptr}; - -use alloc::vec::Vec; -use spin::Mutex; - -#[cfg(feature = "acpi")] -use crate::acpi::madt::{self, Madt, MadtEntry, MadtIntSrcOverride, MadtIoApic}; - -use crate::{ - arch::interrupt::irq, - memory::Frame, - paging::{ - entry::EntryFlags, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch, - VirtualAddress, - }, -}; - -use super::{super::cpuid::cpuid, pic}; - -pub struct IoApicRegs { - pointer: *const u32, -} -impl IoApicRegs { - fn ioregsel(&self) -> *const u32 { - self.pointer - } - fn iowin(&self) -> *const u32 { - // offset 0x10 - unsafe { self.pointer.offset(4) } - } - fn write_ioregsel(&mut self, value: u32) { - unsafe { ptr::write_volatile::(self.ioregsel() as *mut u32, value) } - } - fn read_iowin(&self) -> u32 { - unsafe { ptr::read_volatile::(self.iowin()) } - } - fn write_iowin(&mut self, value: u32) { - unsafe { ptr::write_volatile::(self.iowin() as *mut u32, value) } - } - fn read_reg(&mut self, reg: u8) -> u32 { - self.write_ioregsel(reg.into()); - self.read_iowin() - } - fn write_reg(&mut self, reg: u8, value: u32) { - self.write_ioregsel(reg.into()); - self.write_iowin(value); - } - pub fn read_ioapicid(&mut self) -> u32 { - self.read_reg(0x00) - } - pub fn write_ioapicid(&mut self, value: u32) { - self.write_reg(0x00, value); - } - pub fn read_ioapicver(&mut self) -> u32 { - self.read_reg(0x01) - } - pub fn read_ioapicarb(&mut self) -> u32 { - self.read_reg(0x02) - } - pub fn read_ioredtbl(&mut self, idx: u8) -> u64 { - assert!(idx < 24); - let lo = self.read_reg(0x10 + idx * 2); - let hi = self.read_reg(0x10 + idx * 2 + 1); - - u64::from(lo) | (u64::from(hi) << 32) - } - pub fn write_ioredtbl(&mut self, idx: u8, value: u64) { - assert!(idx < 24); - - let lo = value as u32; - let hi = (value >> 32) as u32; - - self.write_reg(0x10 + idx * 2, lo); - self.write_reg(0x10 + idx * 2 + 1, hi); - } - - pub fn max_redirection_table_entries(&mut self) -> u8 { - let ver = self.read_ioapicver(); - ((ver & 0x00FF_0000) >> 16) as u8 - } - pub fn id(&mut self) -> u8 { - let id_reg = self.read_ioapicid(); - ((id_reg & 0x0F00_0000) >> 24) as u8 - } -} -pub struct IoApic { - regs: Mutex, - gsi_start: u32, - count: u8, -} -impl IoApic { - pub fn new(regs_base: *const u32, gsi_start: u32) -> Self { - let mut regs = IoApicRegs { pointer: regs_base }; - let count = regs.max_redirection_table_entries(); - - Self { - regs: Mutex::new(regs), - gsi_start, - count, - } - } - /// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode). - pub fn map(&self, idx: u8, info: MapInfo) { - self.regs.lock().write_ioredtbl(idx, info.as_raw()) - } - pub fn set_mask(&self, gsi: u32, mask: bool) { - let idx = (gsi - self.gsi_start) as u8; - let mut guard = self.regs.lock(); - - let mut reg = guard.read_ioredtbl(idx); - reg &= !(1 << 16); - reg |= u64::from(mask) << 16; - guard.write_ioredtbl(idx, reg); - } -} -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub enum ApicTriggerMode { - Edge = 0, - Level = 1, -} -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub enum ApicPolarity { - ActiveHigh = 0, - ActiveLow = 1, -} -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub enum DestinationMode { - Physical = 0, - Logical = 1, -} -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub enum DeliveryMode { - Fixed = 0b000, - LowestPriority = 0b001, - Smi = 0b010, - Nmi = 0b100, - Init = 0b101, - ExtInt = 0b111, -} - -#[derive(Clone, Copy, Debug)] -pub struct MapInfo { - pub dest: u8, - pub mask: bool, - pub trigger_mode: ApicTriggerMode, - pub polarity: ApicPolarity, - pub dest_mode: DestinationMode, - pub delivery_mode: DeliveryMode, - pub vector: u8, -} - -impl MapInfo { - pub fn as_raw(&self) -> u64 { - assert!(self.vector >= 0x20); - assert!(self.vector <= 0xFE); - - // TODO: Check for reserved fields. - - (u64::from(self.dest) << 56) - | (u64::from(self.mask) << 16) - | ((self.trigger_mode as u64) << 15) - | ((self.polarity as u64) << 13) - | ((self.dest_mode as u64) << 11) - | ((self.delivery_mode as u64) << 8) - | u64::from(self.vector) - } -} - -impl fmt::Debug for IoApic { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - struct RedirTable<'a>(&'a Mutex); - - impl<'a> fmt::Debug for RedirTable<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut guard = self.0.lock(); - - let count = guard.max_redirection_table_entries(); - f.debug_list() - .entries((0..count).map(|i| guard.read_ioredtbl(i))) - .finish() - } - } - - f.debug_struct("IoApic") - .field("redir_table", &RedirTable(&self.regs)) - .field("gsi_start", &self.gsi_start) - .field("count", &self.count) - .finish() - } -} - -#[derive(Clone, Copy, Debug)] -pub enum TriggerMode { - ConformsToSpecs, - Edge, - Level, -} - -#[derive(Clone, Copy, Debug)] -pub enum Polarity { - ConformsToSpecs, - ActiveHigh, - ActiveLow, -} - -#[derive(Clone, Copy, Debug)] -pub struct Override { - bus_irq: u8, - gsi: u32, - - trigger_mode: TriggerMode, - polarity: Polarity, -} - -// static mut because only the AP initializes the I/O Apic, and when that is done, it's solely -// accessed immutably. -static mut IOAPICS: Option> = None; - -// static mut for the same reason as above -static mut SRC_OVERRIDES: Option> = None; - -pub fn ioapics() -> &'static [IoApic] { - unsafe { IOAPICS.as_ref().map_or(&[], |vector| &vector[..]) } -} -pub fn src_overrides() -> &'static [Override] { - unsafe { SRC_OVERRIDES.as_ref().map_or(&[], |vector| &vector[..]) } -} - -#[cfg(feature = "acpi")] -pub unsafe fn handle_ioapic(mapper: &mut KernelMapper, madt_ioapic: &'static MadtIoApic) { - // map the I/O APIC registers - - let frame = Frame::containing_address(PhysicalAddress::new(madt_ioapic.address as usize)); - let page = Page::containing_address(VirtualAddress::new(crate::IOAPIC_OFFSET)); - - assert!(mapper.translate(page.start_address()).is_none()); - - mapper - .get_mut() - .expect("expected KernelMapper not to be locked re-entrant while mapping I/O APIC memory") - .map_phys( - page.start_address(), - frame.start_address(), - PageFlags::new() - .write(true) - .custom_flag(EntryFlags::NO_CACHE.bits(), true), - ) - .expect("failed to map I/O APIC") - .flush(); - - let ioapic_registers = page.start_address().data() as *const u32; - let ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base); - - assert_eq!( - ioapic.regs.lock().id(), - madt_ioapic.id, - "mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC" - ); - - IOAPICS.get_or_insert_with(Vec::new).push(ioapic); -} -#[cfg(feature = "acpi")] -pub unsafe fn handle_src_override(src_override: &'static MadtIntSrcOverride) { - let flags = src_override.flags; - - let polarity_raw = (flags & 0x0003) as u8; - let trigger_mode_raw = ((flags & 0x000C) >> 2) as u8; - - let polarity = match polarity_raw { - 0b00 => Polarity::ConformsToSpecs, - 0b01 => Polarity::ActiveHigh, - 0b10 => return, // reserved - 0b11 => Polarity::ActiveLow, - - _ => unreachable!(), - }; - - let trigger_mode = match trigger_mode_raw { - 0b00 => TriggerMode::ConformsToSpecs, - 0b01 => TriggerMode::Edge, - 0b10 => return, // reserved - 0b11 => TriggerMode::Level, - _ => unreachable!(), - }; - - let over = Override { - bus_irq: src_override.irq_source, - gsi: src_override.gsi_base, - polarity, - trigger_mode, - }; - SRC_OVERRIDES.get_or_insert_with(Vec::new).push(over); -} - -pub unsafe fn init(active_table: &mut KernelMapper) { - let bsp_apic_id = cpuid() - .unwrap() - .get_feature_info() - .unwrap() - .initial_local_apic_id(); // TODO: remove unwraps - - // search the madt for all IOAPICs. - #[cfg(feature = "acpi")] - { - let madt: &'static Madt = match madt::MADT.as_ref() { - Some(m) => m, - // TODO: Parse MP tables too. - None => return, - }; - if madt.flags & madt::FLAG_PCAT != 0 { - pic::disable(); - } - - // find all I/O APICs (usually one). - - for entry in madt.iter() { - match entry { - MadtEntry::IoApic(ioapic) => handle_ioapic(active_table, ioapic), - MadtEntry::IntSrcOverride(src_override) => handle_src_override(src_override), - _ => (), - } - } - } - println!( - "I/O APICs: {:?}, overrides: {:?}", - ioapics(), - src_overrides() - ); - - // map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it - // wouldn't have been disabled due to this I/O APIC) - for legacy_irq in 0..=15 { - let (gsi, trigger_mode, polarity) = match get_override(legacy_irq) { - Some(over) => (over.gsi, over.trigger_mode, over.polarity), - None => { - if src_overrides() - .iter() - .any(|over| over.gsi == u32::from(legacy_irq) && over.bus_irq != legacy_irq) - && !src_overrides() - .iter() - .any(|over| over.bus_irq == legacy_irq) - { - // there's an IRQ conflict, making this legacy IRQ inaccessible. - continue; - } - ( - legacy_irq.into(), - TriggerMode::ConformsToSpecs, - Polarity::ConformsToSpecs, - ) - } - }; - let apic = match find_ioapic(gsi) { - Some(ioapic) => ioapic, - None => { - println!("Unable to find a suitable APIC for legacy IRQ {} (GSI {}). It will not be mapped.", legacy_irq, gsi); - continue; - } - }; - let redir_tbl_index = (gsi - apic.gsi_start) as u8; - - let map_info = MapInfo { - // only send to the BSP - dest: bsp_apic_id, - dest_mode: DestinationMode::Physical, - delivery_mode: DeliveryMode::Fixed, - mask: false, - polarity: match polarity { - Polarity::ActiveHigh => ApicPolarity::ActiveHigh, - Polarity::ActiveLow => ApicPolarity::ActiveLow, - Polarity::ConformsToSpecs => ApicPolarity::ActiveHigh, - }, - trigger_mode: match trigger_mode { - TriggerMode::Edge => ApicTriggerMode::Edge, - TriggerMode::Level => ApicTriggerMode::Level, - TriggerMode::ConformsToSpecs => ApicTriggerMode::Edge, - }, - vector: 32 + legacy_irq, - }; - apic.map(redir_tbl_index, map_info); - } - println!( - "I/O APICs: {:?}, overrides: {:?}", - ioapics(), - src_overrides() - ); - irq::set_irq_method(irq::IrqMethod::Apic); - - // tell the firmware that we're using APIC rather than the default 8259 PIC. - - // FIXME: With ACPI moved to userspace, we should instead allow userspace to check whether the - // IOAPIC has been initialized, and then subsequently let some ACPI driver call the AML from - // userspace. - - /*#[cfg(feature = "acpi")] - { - let method = { - let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); - if let Some(value) = namespace_guard.as_ref().unwrap().get("\\_PIC") { - value.get_as_method().ok() - } else { - None - } - }; - if let Some(m) = method { - m.execute("\\_PIC".into(), vec!(crate::acpi::aml::AmlValue::Integer(1))); - } - }*/ -} -fn get_override(irq: u8) -> Option<&'static Override> { - src_overrides().iter().find(|over| over.bus_irq == irq) -} -fn resolve(irq: u8) -> u32 { - get_override(irq).map_or(u32::from(irq), |over| over.gsi) -} -fn find_ioapic(gsi: u32) -> Option<&'static IoApic> { - ioapics() - .iter() - .find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count)) -} - -pub unsafe fn mask(irq: u8) { - let gsi = resolve(irq); - let apic = match find_ioapic(gsi) { - Some(a) => a, - None => return, - }; - apic.set_mask(gsi, true); -} -pub unsafe fn unmask(irq: u8) { - let gsi = resolve(irq); - let apic = match find_ioapic(gsi) { - Some(a) => a, - None => return, - }; - apic.set_mask(gsi, false); -} diff --git a/src/arch/x86/device/mod.rs b/src/arch/x86/device/mod.rs index f9f2a4efcf..f09efe0f92 100644 --- a/src/arch/x86/device/mod.rs +++ b/src/arch/x86/device/mod.rs @@ -1,6 +1,4 @@ pub use crate::arch::x86_shared::device::*; -pub mod ioapic; -pub mod local_apic; use crate::paging::KernelMapper; diff --git a/src/arch/x86_64/device/local_apic.rs b/src/arch/x86_64/device/local_apic.rs deleted file mode 100644 index b44a0b563d..0000000000 --- a/src/arch/x86_64/device/local_apic.rs +++ /dev/null @@ -1,251 +0,0 @@ -use core::{ - ptr::{read_volatile, write_volatile}, - sync::atomic::{self, AtomicU64}, -}; -use x86::msr::*; - -use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch}; - -use super::super::cpuid::cpuid; - -pub static mut LOCAL_APIC: LocalApic = LocalApic { - address: 0, - x2: false, -}; - -pub unsafe fn init(active_table: &mut KernelMapper) { - LOCAL_APIC.init(active_table); -} - -pub unsafe fn init_ap() { - LOCAL_APIC.init_ap(); -} - -/// Local APIC -pub struct LocalApic { - pub address: usize, - pub x2: bool, -} - -#[derive(Debug)] -struct NoFreqInfo; - -static BSP_APIC_ID: AtomicU64 = AtomicU64::new(0xFFFF_FFFF_FFFF_FFFF); - -#[no_mangle] -pub fn bsp_apic_id() -> Option { - let value = BSP_APIC_ID.load(atomic::Ordering::SeqCst); - if value <= u64::from(u32::max_value()) { - Some(value as u32) - } else { - None - } -} - -impl LocalApic { - unsafe fn init(&mut self, mapper: &mut KernelMapper) { - let mapper = mapper - .get_mut() - .expect("expected KernelMapper not to be locked re-entrant while initializing LAPIC"); - - let physaddr = PhysicalAddress::new(rdmsr(IA32_APIC_BASE) as usize & 0xFFFF_0000); - let virtaddr = RmmA::phys_to_virt(physaddr); - - self.address = virtaddr.data(); - self.x2 = cpuid().map_or(false, |cpuid| { - cpuid - .get_feature_info() - .map_or(false, |feature_info| feature_info.has_x2apic()) - }); - - if !self.x2 { - log::info!("Detected xAPIC at {:#x}", physaddr.data()); - if let Some((_entry, _, flush)) = mapper.unmap_phys(virtaddr, true) { - // Unmap xAPIC page if already mapped - flush.flush(); - } - mapper - .map_phys(virtaddr, physaddr, PageFlags::new().write(true)) - .expect("failed to map local APIC memory") - .flush(); - } else { - log::info!("Detected x2APIC"); - } - - self.init_ap(); - BSP_APIC_ID.store(u64::from(self.id()), atomic::Ordering::SeqCst); - } - - unsafe fn init_ap(&mut self) { - if self.x2 { - wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | 1 << 10); - wrmsr(IA32_X2APIC_SIVR, 0x100); - } else { - self.write(0xF0, 0x100); - } - self.setup_error_int(); - - //self.setup_timer(); - } - - unsafe fn read(&self, reg: u32) -> u32 { - read_volatile((self.address + reg as usize) as *const u32) - } - - unsafe fn write(&mut self, reg: u32, value: u32) { - write_volatile((self.address + reg as usize) as *mut u32, value); - } - - pub fn id(&self) -> u32 { - if self.x2 { - unsafe { rdmsr(IA32_X2APIC_APICID) as u32 } - } else { - unsafe { self.read(0x20) } - } - } - - pub fn version(&self) -> u32 { - if self.x2 { - unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 } - } else { - unsafe { self.read(0x30) } - } - } - - pub fn icr(&self) -> u64 { - if self.x2 { - unsafe { rdmsr(IA32_X2APIC_ICR) } - } else { - unsafe { (self.read(0x310) as u64) << 32 | self.read(0x300) as u64 } - } - } - - pub fn set_icr(&mut self, value: u64) { - if self.x2 { - unsafe { - wrmsr(IA32_X2APIC_ICR, value); - } - } else { - unsafe { - const PENDING: u32 = 1 << 12; - while self.read(0x300) & PENDING == PENDING { - core::hint::spin_loop(); - } - self.write(0x310, (value >> 32) as u32); - self.write(0x300, value as u32); - while self.read(0x300) & PENDING == PENDING { - core::hint::spin_loop(); - } - } - } - } - - pub fn ipi(&mut self, apic_id: usize) { - let mut icr = 0x4040; - if self.x2 { - icr |= (apic_id as u64) << 32; - } else { - icr |= (apic_id as u64) << 56; - } - self.set_icr(icr); - } - // Not used just yet, but allows triggering an NMI to another processor. - pub fn ipi_nmi(&mut self, apic_id: u32) { - let shift = if self.x2 { 32 } else { 56 }; - self.set_icr((u64::from(apic_id) << shift) | (1 << 14) | (0b100 << 8)); - } - - pub unsafe fn eoi(&mut self) { - if self.x2 { - wrmsr(IA32_X2APIC_EOI, 0); - } else { - self.write(0xB0, 0); - } - } - /// Reads the Error Status Register. - pub unsafe fn esr(&mut self) -> u32 { - if self.x2 { - // update the ESR to the current state of the local apic. - wrmsr(IA32_X2APIC_ESR, 0); - // read the updated value - rdmsr(IA32_X2APIC_ESR) as u32 - } else { - self.write(0x280, 0); - self.read(0x280) - } - } - pub unsafe fn lvt_timer(&mut self) -> u32 { - if self.x2 { - rdmsr(IA32_X2APIC_LVT_TIMER) as u32 - } else { - self.read(0x320) - } - } - pub unsafe fn set_lvt_timer(&mut self, value: u32) { - if self.x2 { - wrmsr(IA32_X2APIC_LVT_TIMER, u64::from(value)); - } else { - self.write(0x320, value); - } - } - pub unsafe fn init_count(&mut self) -> u32 { - if self.x2 { - rdmsr(IA32_X2APIC_INIT_COUNT) as u32 - } else { - self.read(0x380) - } - } - pub unsafe fn set_init_count(&mut self, initial_count: u32) { - if self.x2 { - wrmsr(IA32_X2APIC_INIT_COUNT, u64::from(initial_count)); - } else { - self.write(0x380, initial_count); - } - } - pub unsafe fn cur_count(&mut self) -> u32 { - if self.x2 { - rdmsr(IA32_X2APIC_CUR_COUNT) as u32 - } else { - self.read(0x390) - } - } - pub unsafe fn div_conf(&mut self) -> u32 { - if self.x2 { - rdmsr(IA32_X2APIC_DIV_CONF) as u32 - } else { - self.read(0x3E0) - } - } - pub unsafe fn set_div_conf(&mut self, div_conf: u32) { - if self.x2 { - wrmsr(IA32_X2APIC_DIV_CONF, u64::from(div_conf)); - } else { - self.write(0x3E0, div_conf); - } - } - pub unsafe fn lvt_error(&mut self) -> u32 { - if self.x2 { - rdmsr(IA32_X2APIC_LVT_ERROR) as u32 - } else { - self.read(0x370) - } - } - pub unsafe fn set_lvt_error(&mut self, lvt_error: u32) { - if self.x2 { - wrmsr(IA32_X2APIC_LVT_ERROR, u64::from(lvt_error)); - } else { - self.write(0x370, lvt_error); - } - } - unsafe fn setup_error_int(&mut self) { - let vector = 49u32; - self.set_lvt_error(vector); - } -} - -#[repr(u8)] -pub enum LvtTimerMode { - OneShot = 0b00, - Periodic = 0b01, - TscDeadline = 0b10, -} diff --git a/src/arch/x86_64/device/mod.rs b/src/arch/x86_64/device/mod.rs index 44cf1e87ce..3be0c37435 100644 --- a/src/arch/x86_64/device/mod.rs +++ b/src/arch/x86_64/device/mod.rs @@ -1,6 +1,4 @@ pub use crate::arch::x86_shared::device::*; -pub mod ioapic; -pub mod local_apic; use crate::paging::KernelMapper; diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_shared/device/ioapic.rs similarity index 99% rename from src/arch/x86_64/device/ioapic.rs rename to src/arch/x86_shared/device/ioapic.rs index 61f12c1172..6a88b338c7 100644 --- a/src/arch/x86_64/device/ioapic.rs +++ b/src/arch/x86_shared/device/ioapic.rs @@ -12,7 +12,8 @@ use crate::{ paging::{entry::EntryFlags, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch}, }; -use super::{super::cpuid::cpuid, pic}; +use crate::arch::cpuid::cpuid; +use super::pic; pub struct IoApicRegs { pointer: *const u32, diff --git a/src/arch/x86/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs similarity index 97% rename from src/arch/x86/device/local_apic.rs rename to src/arch/x86_shared/device/local_apic.rs index 9821f91cfa..dc116bd135 100644 --- a/src/arch/x86/device/local_apic.rs +++ b/src/arch/x86_shared/device/local_apic.rs @@ -4,9 +4,9 @@ use core::{ }; use x86::msr::*; -use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch, VirtualAddress}; +use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch}; -use super::super::cpuid::cpuid; +use crate::arch::cpuid::cpuid; pub static mut LOCAL_APIC: LocalApic = LocalApic { address: 0, @@ -36,7 +36,7 @@ static BSP_APIC_ID: AtomicU32 = AtomicU32::new(u32::max_value()); pub fn bsp_apic_id() -> Option { let value = BSP_APIC_ID.load(atomic::Ordering::SeqCst); if value < u32::max_value() { - Some(value as u32) + Some(value) } else { None } @@ -49,7 +49,7 @@ impl LocalApic { .expect("expected KernelMapper not to be locked re-entrant while initializing LAPIC"); let physaddr = PhysicalAddress::new(rdmsr(IA32_APIC_BASE) as usize & 0xFFFF_0000); - let virtaddr = VirtualAddress::new(crate::LAPIC_OFFSET); + let virtaddr = RmmA::phys_to_virt(physaddr); self.address = virtaddr.data(); self.x2 = cpuid().map_or(false, |cpuid| { diff --git a/src/arch/x86_shared/device/mod.rs b/src/arch/x86_shared/device/mod.rs index e709e7e5fb..f96947fc33 100644 --- a/src/arch/x86_shared/device/mod.rs +++ b/src/arch/x86_shared/device/mod.rs @@ -1,6 +1,8 @@ pub mod cpu; #[cfg(feature = "acpi")] pub mod hpet; +pub mod ioapic; +pub mod local_apic; pub mod pic; pub mod pit; pub mod rtc;