1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
mod context;
mod gdt;
mod idt;
#[cfg(feature = "uspace")]
mod syscall;
#[cfg(target_os = "none")]
mod trap;
use core::arch::asm;
use memory_addr::{MemoryAddr, PhysAddr, VirtAddr};
use x86::{controlregs, msr, tlb};
use x86_64::instructions::interrupts;
pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame};
pub use self::gdt::{init_gdt, tss_get_rsp0, tss_set_rsp0, GdtStruct};
pub use self::idt::{init_idt, IdtStruct};
#[cfg(feature = "uspace")]
pub use self::{context::UspaceContext, syscall::init_syscall};
/// Allows the current CPU to respond to interrupts.
#[inline]
pub fn enable_irqs() {
#[cfg(target_os = "none")]
interrupts::enable()
}
/// Makes the current CPU to ignore interrupts.
#[inline]
pub fn disable_irqs() {
#[cfg(target_os = "none")]
interrupts::disable()
}
/// Returns whether the current CPU is allowed to respond to interrupts.
#[inline]
pub fn irqs_enabled() -> bool {
interrupts::are_enabled()
}
/// Relaxes the current CPU and waits for interrupts.
///
/// It must be called with interrupts enabled, otherwise it will never return.
#[inline]
pub fn wait_for_irqs() {
if cfg!(target_os = "none") {
unsafe { asm!("hlt") }
} else {
core::hint::spin_loop()
}
}
/// Halt the current CPU.
#[inline]
pub fn halt() {
disable_irqs();
wait_for_irqs(); // should never return
}
/// Reads the register that stores the current page table root.
///
/// Returns the physical address of the page table root.
#[inline]
pub fn read_page_table_root() -> PhysAddr {
pa!(unsafe { controlregs::cr3() } as usize).align_down_4k()
}
/// Writes the register to update the current page table root.
///
/// # Safety
///
/// This function is unsafe as it changes the virtual memory address space.
pub unsafe fn write_page_table_root(root_paddr: PhysAddr) {
let old_root = read_page_table_root();
trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr);
if old_root != root_paddr {
controlregs::cr3_write(root_paddr.as_usize() as _)
}
}
/// Flushes the TLB.
///
/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB
/// entry that maps the given virtual address.
#[inline]
pub fn flush_tlb(vaddr: Option<VirtAddr>) {
if let Some(vaddr) = vaddr {
unsafe { tlb::flush(vaddr.into()) }
} else {
unsafe { tlb::flush_all() }
}
}
/// Reads the thread pointer of the current CPU.
///
/// It is used to implement TLS (Thread Local Storage).
#[inline]
pub fn read_thread_pointer() -> usize {
unsafe { msr::rdmsr(msr::IA32_FS_BASE) as usize }
}
/// Writes the thread pointer of the current CPU.
///
/// It is used to implement TLS (Thread Local Storage).
///
/// # Safety
///
/// This function is unsafe as it changes the CPU states.
#[inline]
pub unsafe fn write_thread_pointer(fs_base: usize) {
unsafe { msr::wrmsr(msr::IA32_FS_BASE, fs_base as u64) }
}
/// Initializes CPU states on the current CPU.
///
/// In detail, it initializes the GDT, IDT on x86_64 platforms. If the `uspace`
/// feature is enabled, it also initializes relevant model-specific registers
/// to enable the `syscall` instruction.
pub fn cpu_init() {
init_gdt();
init_idt();
#[cfg(feature = "uspace")]
init_syscall();
}