Files
RedBear-OS/src/arch/x86_64/idt.rs
T

331 lines
11 KiB
Rust

use core::num::NonZeroU8;
use core::sync::atomic::{AtomicU64, Ordering};
use core::mem;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use x86::segmentation::Descriptor as X86IdtEntry;
use x86::dtables::{self, DescriptorTablePointer};
use crate::interrupt::*;
use crate::ipi::IpiKind;
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 = [AtomicU64; 4];
#[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 / 64;
let bit = index % 64;
{ &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 / 64;
let bit = index % 64;
{ &self.reservations[usize::from(byte_index)] }.fetch_or(u64::from(reserved) << bit, Ordering::AcqRel);
}
#[inline]
pub fn is_reserved_mut(&mut self, index: u8) -> bool {
let byte_index = index / 64;
let bit = index % 64;
*{ &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 / 64;
let bit = index % 64;
*{ &mut self.reservations[usize::from(byte_index)] }.get_mut() |= u64::from(reserved) << bit;
}
}
static mut INIT_BSP_IDT: Idt = Idt::new();
// TODO: VecMap?
pub static IDTS: RwLock<Option<BTreeMap<usize, &'static mut Idt>>> = RwLock::new(None);
#[inline]
pub fn is_reserved(cpu_id: usize, index: u8) -> bool {
let byte_index = index / 64;
let bit = index % 64;
{ &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: usize, index: u8, reserved: bool) {
let byte_index = index / 64;
let bit = index % 64;
{ &IDTS.read().as_ref().unwrap().get(&cpu_id).unwrap().reservations[usize::from(byte_index)] }.fetch_or(u64::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: usize) -> 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() -> [AtomicU64; 4] {
[AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0)]
}
/// Initialize the IDT for a
pub unsafe fn init_paging_post_heap(is_bsp: bool, cpu_id: usize) {
let mut idts_guard = IDTS.write();
let idts_btree = idts_guard.get_or_insert_with(BTreeMap::new);
if is_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(is_bsp, 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(true, &mut INIT_BSP_IDT);
}
/// Initializes an IDT for any type of processor.
pub unsafe fn init_generic(is_bsp: bool, 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.
(*crate::gdt::pcr()).tss.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 is_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[0].get_mut() |= 0x0003_FFFF_0000_0000;
} 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[0].get_mut() |= 1 << 49;
}
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,
offseth: u32,
_zero2: u32
}
impl IdtEntry {
pub const fn new() -> IdtEntry {
IdtEntry {
offsetl: 0,
selector: 0,
zero: 0,
attribute: 0,
offsetm: 0,
offseth: 0,
_zero2: 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;
self.offseth = ((base as u64) >> 32) as u32;
}
// A function to set the offset more easily
pub fn set_func(&mut self, func: unsafe extern 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);
}
}