diff --git a/src/arch/x86/idt.rs b/src/arch/x86/idt.rs deleted file mode 100644 index 665c52f0f8..0000000000 --- a/src/arch/x86/idt.rs +++ /dev/null @@ -1,364 +0,0 @@ -use core::{ - mem, - num::NonZeroU8, - sync::atomic::{AtomicU32, Ordering}, -}; - -use alloc::boxed::Box; -use hashbrown::HashMap; - -use x86::{ - dtables::{self, DescriptorTablePointer}, - segmentation::Descriptor as X86IdtEntry, -}; - -use crate::{interrupt::*, ipi::IpiKind, LogicalCpuId}; - -use spin::RwLock; - -pub static mut INIT_IDTR: DescriptorTablePointer = DescriptorTablePointer { - limit: 0, - base: 0 as *const X86IdtEntry, -}; - -pub type IdtEntries = [IdtEntry; 256]; -pub type IdtReservations = [AtomicU32; 8]; - -#[repr(C)] -pub struct Idt { - entries: IdtEntries, - reservations: IdtReservations, -} -impl Idt { - pub const fn new() -> Self { - Self { - entries: [IdtEntry::new(); 256], - reservations: new_idt_reservations(), - } - } - #[inline] - pub fn is_reserved(&self, index: u8) -> bool { - let byte_index = index / 32; - let bit = index % 32; - - { &self.reservations[usize::from(byte_index)] }.load(Ordering::Acquire) & (1 << bit) != 0 - } - - #[inline] - pub fn set_reserved(&self, index: u8, reserved: bool) { - let byte_index = index / 32; - let bit = index % 32; - - { &self.reservations[usize::from(byte_index)] } - .fetch_or(u32::from(reserved) << bit, Ordering::AcqRel); - } - #[inline] - pub fn is_reserved_mut(&mut self, index: u8) -> bool { - let byte_index = index / 32; - let bit = index % 32; - - *{ &mut self.reservations[usize::from(byte_index)] }.get_mut() & (1 << bit) != 0 - } - - #[inline] - pub fn set_reserved_mut(&mut self, index: u8, reserved: bool) { - let byte_index = index / 32; - let bit = index % 32; - - *{ &mut self.reservations[usize::from(byte_index)] }.get_mut() |= - u32::from(reserved) << bit; - } -} - -static mut INIT_BSP_IDT: Idt = Idt::new(); - -// TODO: VecMap? -pub static IDTS: RwLock>> = RwLock::new(None); - -#[inline] -pub fn is_reserved(cpu_id: LogicalCpuId, index: u8) -> bool { - let byte_index = index / 32; - let bit = index % 32; - - { - &IDTS - .read() - .as_ref() - .unwrap() - .get(&cpu_id) - .unwrap() - .reservations[usize::from(byte_index)] - } - .load(Ordering::Acquire) - & (1 << bit) - != 0 -} - -#[inline] -pub fn set_reserved(cpu_id: LogicalCpuId, index: u8, reserved: bool) { - let byte_index = index / 32; - let bit = index % 32; - - { - &IDTS - .read() - .as_ref() - .unwrap() - .get(&cpu_id) - .unwrap() - .reservations[usize::from(byte_index)] - } - .fetch_or(u32::from(reserved) << bit, Ordering::AcqRel); -} - -pub fn allocate_interrupt() -> Option { - let cpu_id = crate::cpu_id(); - for number in 50..=254 { - if !is_reserved(cpu_id, number) { - set_reserved(cpu_id, number, true); - return Some(unsafe { NonZeroU8::new_unchecked(number) }); - } - } - None -} - -pub fn available_irqs_iter(cpu_id: LogicalCpuId) -> impl Iterator + 'static { - (32..=254).filter(move |&index| !is_reserved(cpu_id, index)) -} - -macro_rules! use_irq( - ( $idt: expr, $number:literal, $func:ident ) => {{ - $idt[$number].set_func($func); - }} -); - -macro_rules! use_default_irqs( - ($idt:expr) => {{ - use crate::interrupt::irq::*; - default_irqs!($idt, use_irq); - }} -); - -pub unsafe fn init() { - dtables::lidt(&INIT_IDTR); -} - -const fn new_idt_reservations() -> [AtomicU32; 8] { - [ - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - AtomicU32::new(0), - ] -} - -/// Initialize the IDT for a -pub unsafe fn init_paging_post_heap(cpu_id: LogicalCpuId) { - let mut idts_guard = IDTS.write(); - let idts_btree = idts_guard.get_or_insert_with(HashMap::new); - - if cpu_id == LogicalCpuId::BSP { - idts_btree.insert(cpu_id, &mut INIT_BSP_IDT); - } else { - let idt = idts_btree - .entry(cpu_id) - .or_insert_with(|| Box::leak(Box::new(Idt::new()))); - init_generic(cpu_id, idt); - } -} - -/// Initializes a fully functional IDT for use before it be moved into the map. This is ONLY called -/// on the BSP, since the kernel heap is ready for the APs. -pub unsafe fn init_paging_bsp() { - init_generic(LogicalCpuId::BSP, &mut INIT_BSP_IDT); -} - -/// Initializes an IDT for any type of processor. -pub unsafe fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt) { - let (current_idt, current_reservations) = (&mut idt.entries, &mut idt.reservations); - - let idtr: DescriptorTablePointer = DescriptorTablePointer { - limit: (current_idt.len() * mem::size_of::() - 1) as u16, - base: current_idt.as_ptr() as *const X86IdtEntry, - }; - - let backup_ist = { - // We give Non-Maskable Interrupts, Double Fault, and Machine Check exceptions separate - // stacks, since these (unless we are going to set up NMI watchdogs like Linux does) are - // considered the most fatal, especially Double Faults which are caused by errors __when - // accessing the system IDT__. If that goes wrong, then kernel memory may be partially - // corrupt, and we want a separate stack. - // - // Note that each CPU has its own "backup interrupt stack". - let index = 1_u8; - - // Allocate 64 KiB of stack space for the backup stack. - const BACKUP_STACK_SIZE: usize = 65536; - assert_eq!(BACKUP_STACK_SIZE % crate::memory::PAGE_SIZE, 0); - let page_count = BACKUP_STACK_SIZE / crate::memory::PAGE_SIZE; - let frames = crate::memory::allocate_frames(page_count) - .expect("failed to allocate pages for backup interrupt stack"); - - use crate::paging::{RmmA, RmmArch}; - - // Physical pages are mapped linearly. So is the linearly mapped virtual memory. - let base_address = RmmA::phys_to_virt(frames.start_address()); - - // Stack always grows downwards. - let address = base_address.data() + BACKUP_STACK_SIZE; - - // Put them in the 1st entry of the IST. - //TODO: x86: crate::gdt::KPCR.tss.0.ist[usize::from(index - 1)] = address as u64; - - index - }; - - // Set up exceptions - current_idt[0].set_func(exception::divide_by_zero); - current_idt[1].set_func(exception::debug); - current_idt[2].set_func(exception::non_maskable); - current_idt[2].set_ist(backup_ist); - current_idt[3].set_func(exception::breakpoint); - current_idt[3].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT); - current_idt[4].set_func(exception::overflow); - current_idt[5].set_func(exception::bound_range); - current_idt[6].set_func(exception::invalid_opcode); - current_idt[7].set_func(exception::device_not_available); - current_idt[8].set_func(exception::double_fault); - current_idt[8].set_ist(backup_ist); - // 9 no longer available - current_idt[10].set_func(exception::invalid_tss); - current_idt[11].set_func(exception::segment_not_present); - current_idt[12].set_func(exception::stack_segment); - current_idt[13].set_func(exception::protection); - current_idt[14].set_func(exception::page); - // 15 reserved - current_idt[16].set_func(exception::fpu_fault); - current_idt[17].set_func(exception::alignment_check); - current_idt[18].set_func(exception::machine_check); - current_idt[18].set_ist(backup_ist); - current_idt[19].set_func(exception::simd); - current_idt[20].set_func(exception::virtualization); - // 21 through 29 reserved - current_idt[30].set_func(exception::security); - // 31 reserved - - // reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions - *current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF; - - if cpu_id == LogicalCpuId::BSP { - // Set up IRQs - current_idt[32].set_func(irq::pit_stack); - current_idt[33].set_func(irq::keyboard); - current_idt[34].set_func(irq::cascade); - current_idt[35].set_func(irq::com2); - current_idt[36].set_func(irq::com1); - current_idt[37].set_func(irq::lpt2); - current_idt[38].set_func(irq::floppy); - current_idt[39].set_func(irq::lpt1); - current_idt[40].set_func(irq::rtc); - current_idt[41].set_func(irq::pci1); - current_idt[42].set_func(irq::pci2); - current_idt[43].set_func(irq::pci3); - current_idt[44].set_func(irq::mouse); - current_idt[45].set_func(irq::fpu); - current_idt[46].set_func(irq::ata1); - current_idt[47].set_func(irq::ata2); - current_idt[48].set_func(irq::lapic_timer); - current_idt[49].set_func(irq::lapic_error); - - // reserve bits 49:32, which are for the standard IRQs, and for the local apic timer and error. - *current_reservations[1].get_mut() |= 0x0003_FFFF; - } else { - // TODO: use_default_irqs! but also the legacy IRQs that are only needed on one CPU - current_idt[49].set_func(irq::lapic_error); - - // reserve bit 49 - *current_reservations[1].get_mut() |= 1 << 17; - } - - use_default_irqs!(current_idt); - - // Set IPI handlers - current_idt[IpiKind::Wakeup as usize].set_func(ipi::wakeup); - current_idt[IpiKind::Switch as usize].set_func(ipi::switch); - current_idt[IpiKind::Tlb as usize].set_func(ipi::tlb); - current_idt[IpiKind::Pit as usize].set_func(ipi::pit); - idt.set_reserved_mut(IpiKind::Wakeup as u8, true); - idt.set_reserved_mut(IpiKind::Switch as u8, true); - idt.set_reserved_mut(IpiKind::Tlb as u8, true); - idt.set_reserved_mut(IpiKind::Pit as u8, true); - let current_idt = &mut idt.entries; - - // Set syscall function - current_idt[0x80].set_func(syscall::syscall); - current_idt[0x80].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT); - idt.set_reserved_mut(0x80, true); - - dtables::lidt(&idtr); -} - -bitflags! { - pub struct IdtFlags: u8 { - const PRESENT = 1 << 7; - const RING_0 = 0 << 5; - const RING_1 = 1 << 5; - const RING_2 = 2 << 5; - const RING_3 = 3 << 5; - const SS = 1 << 4; - const INTERRUPT = 0xE; - const TRAP = 0xF; - } -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(packed)] -pub struct IdtEntry { - offsetl: u16, - selector: u16, - zero: u8, - attribute: u8, - offsetm: u16, -} - -impl IdtEntry { - pub const fn new() -> IdtEntry { - IdtEntry { - offsetl: 0, - selector: 0, - zero: 0, - attribute: 0, - offsetm: 0, - } - } - - pub fn set_flags(&mut self, flags: IdtFlags) { - self.attribute = flags.bits; - } - - pub fn set_ist(&mut self, ist: u8) { - assert_eq!( - ist & 0x07, - ist, - "interrupt stack table must be within 0..=7" - ); - self.zero &= 0xF8; - self.zero |= ist; - } - - pub fn set_offset(&mut self, selector: u16, base: usize) { - self.selector = selector; - self.offsetl = base as u16; - self.offsetm = (base >> 16) as u16; - } - - // A function to set the offset more easily - pub fn set_func(&mut self, func: unsafe extern "C" fn()) { - self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT); - self.set_offset((crate::gdt::GDT_KERNEL_CODE as u16) << 3, func as usize); - } -} diff --git a/src/arch/x86/interrupt/mod.rs b/src/arch/x86/interrupt/mod.rs index 117958332d..430961dd9f 100644 --- a/src/arch/x86/interrupt/mod.rs +++ b/src/arch/x86/interrupt/mod.rs @@ -10,8 +10,3 @@ pub mod irq; pub mod syscall; pub use self::handler::InterruptStack; - -pub use super::{ - device::local_apic::bsp_apic_id, - idt::{available_irqs_iter, is_reserved, set_reserved}, -}; diff --git a/src/arch/x86/mod.rs b/src/arch/x86/mod.rs index 2d107b9111..e672f24819 100644 --- a/src/arch/x86/mod.rs +++ b/src/arch/x86/mod.rs @@ -13,9 +13,6 @@ pub mod gdt; #[macro_use] pub mod interrupt; -/// Interrupt descriptor table -pub mod idt; - /// Paging pub mod paging; diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs index 117958332d..430961dd9f 100644 --- a/src/arch/x86_64/interrupt/mod.rs +++ b/src/arch/x86_64/interrupt/mod.rs @@ -10,8 +10,3 @@ pub mod irq; pub mod syscall; pub use self::handler::InterruptStack; - -pub use super::{ - device::local_apic::bsp_apic_id, - idt::{available_irqs_iter, is_reserved, set_reserved}, -}; diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index bbb67dc73b..6edd2e87d2 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -22,9 +22,6 @@ pub mod gdt; #[macro_use] pub mod interrupt; -/// Interrupt descriptor table -pub mod idt; - /// Miscellaneous processor features pub mod misc; diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_shared/idt.rs similarity index 91% rename from src/arch/x86_64/idt.rs rename to src/arch/x86_shared/idt.rs index 6b90af1735..55af99bbe2 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_shared/idt.rs @@ -12,14 +12,9 @@ use x86::{ segmentation::Descriptor as X86IdtEntry, }; -use crate::{ - interrupt::{ - irq::{__generic_interrupts_end, __generic_interrupts_start}, - *, - }, - ipi::IpiKind, - LogicalCpuId, -}; +#[cfg(target_arch = "x86_64")] +use crate::interrupt::irq::{__generic_interrupts_end, __generic_interrupts_start}; +use crate::{interrupt::*, ipi::IpiKind, LogicalCpuId}; use spin::RwLock; @@ -133,6 +128,21 @@ pub fn available_irqs_iter(cpu_id: LogicalCpuId) -> impl Iterator + ' (32..=254).filter(move |&index| !is_reserved(cpu_id, index)) } +#[cfg(target_arch = "x86")] +macro_rules! use_irq( + ( $idt: expr, $number:literal, $func:ident ) => {{ + $idt[$number].set_func($func); + }} +); + +#[cfg(target_arch = "x86")] +macro_rules! use_default_irqs( + ($idt:expr) => {{ + use crate::interrupt::irq::*; + default_irqs!($idt, use_irq); + }} +); + pub unsafe fn init() { dtables::lidt(&INIT_IDTR); } @@ -206,7 +216,10 @@ pub unsafe fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt) { let address = base_address.data() + BACKUP_STACK_SIZE; // Put them in the 1st entry of the IST. - (*crate::gdt::pcr()).tss.ist[usize::from(index - 1)] = address as u64; + #[cfg(target_arch = "x86_64")] // TODO: x86 + { + (*crate::gdt::pcr()).tss.ist[usize::from(index - 1)] = address as u64; + } index }; @@ -241,11 +254,13 @@ pub unsafe fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt) { current_idt[30].set_func(exception::security); // 31 reserved + #[cfg(target_arch = "x86_64")] assert_eq!( __generic_interrupts_end as usize - __generic_interrupts_start as usize, 224 * 8 ); + #[cfg(target_arch = "x86_64")] for i in 0..224 { current_idt[i + 32].set_func(mem::transmute(__generic_interrupts_start as usize + i * 8)); } @@ -283,6 +298,10 @@ pub unsafe fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt) { // reserve bit 49 *current_reservations[1].get_mut() |= 1 << 17; } + + #[cfg(target_arch = "x86")] + use_default_irqs!(current_idt); + // Set IPI handlers current_idt[IpiKind::Wakeup as usize].set_func(ipi::wakeup); current_idt[IpiKind::Switch as usize].set_func(ipi::switch); @@ -326,7 +345,9 @@ pub struct IdtEntry { zero: u8, attribute: u8, offsetm: u16, + #[cfg(target_arch = "x86_64")] offseth: u32, + #[cfg(target_arch = "x86_64")] _zero2: u32, } @@ -338,7 +359,9 @@ impl IdtEntry { zero: 0, attribute: 0, offsetm: 0, + #[cfg(target_arch = "x86_64")] offseth: 0, + #[cfg(target_arch = "x86_64")] _zero2: 0, } } @@ -361,7 +384,10 @@ impl IdtEntry { self.selector = selector; self.offsetl = base as u16; self.offsetm = (base >> 16) as u16; - self.offseth = ((base as u64) >> 32) as u32; + #[cfg(target_arch = "x86_64")] + { + self.offseth = ((base as u64) >> 32) as u32; + } } // A function to set the offset more easily diff --git a/src/arch/x86_shared/interrupt/mod.rs b/src/arch/x86_shared/interrupt/mod.rs index 18b803c91c..6a62463b1f 100644 --- a/src/arch/x86_shared/interrupt/mod.rs +++ b/src/arch/x86_shared/interrupt/mod.rs @@ -5,6 +5,11 @@ pub mod trace; pub use self::trace::stack_trace; +pub use super::{ + device::local_apic::bsp_apic_id, + idt::{available_irqs_iter, is_reserved, set_reserved}, +}; + /// Clear interrupts #[inline(always)] pub unsafe fn disable() { diff --git a/src/arch/x86_shared/mod.rs b/src/arch/x86_shared/mod.rs index 9c12ddccbc..f92099d155 100644 --- a/src/arch/x86_shared/mod.rs +++ b/src/arch/x86_shared/mod.rs @@ -7,6 +7,9 @@ pub mod debug; /// Devices pub mod device; +/// Interrupt descriptor table +pub mod idt; + /// Interrupt instructions #[macro_use] pub mod interrupt;