axhal/arch/x86_64/
mod.rs

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