Share idt.rs between x86 and x86_64

This commit is contained in:
bjorn3
2024-02-25 15:36:30 +01:00
parent f92b4c9608
commit e23f0ed66b
8 changed files with 44 additions and 390 deletions
-364
View File
@@ -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<X86IdtEntry> = 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<Option<HashMap<LogicalCpuId, &'static mut Idt>>> = 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<NonZeroU8> {
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<Item = u8> + '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<X86IdtEntry> = DescriptorTablePointer {
limit: (current_idt.len() * mem::size_of::<IdtEntry>() - 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);
}
}
-5
View File
@@ -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},
};
-3
View File
@@ -13,9 +13,6 @@ pub mod gdt;
#[macro_use]
pub mod interrupt;
/// Interrupt descriptor table
pub mod idt;
/// Paging
pub mod paging;
-5
View File
@@ -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},
};
-3
View File
@@ -22,9 +22,6 @@ pub mod gdt;
#[macro_use]
pub mod interrupt;
/// Interrupt descriptor table
pub mod idt;
/// Miscellaneous processor features
pub mod misc;
@@ -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<Item = u8> + '
(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
+5
View File
@@ -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() {
+3
View File
@@ -7,6 +7,9 @@ pub mod debug;
/// Devices
pub mod device;
/// Interrupt descriptor table
pub mod idt;
/// Interrupt instructions
#[macro_use]
pub mod interrupt;