axhal/arch/x86_64/
mod.rs

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