ax_cpu/x86_64/asm.rs
1//! Wrapper functions for assembly instructions.
2
3use core::arch::asm;
4
5use ax_memory_addr::{MemoryAddr, PhysAddr, VirtAddr};
6use x86::{controlregs, msr, tlb};
7use x86_64::instructions::interrupts;
8
9/// Allows the current CPU to respond to interrupts.
10#[inline]
11pub fn enable_irqs() {
12 #[cfg(not(target_os = "none"))]
13 {
14 warn!("enable_irqs: not implemented");
15 }
16 #[cfg(target_os = "none")]
17 interrupts::enable()
18}
19
20/// Makes the current CPU to ignore interrupts.
21#[inline]
22pub fn disable_irqs() {
23 #[cfg(not(target_os = "none"))]
24 {
25 warn!("disable_irqs: not implemented");
26 }
27 #[cfg(target_os = "none")]
28 interrupts::disable()
29}
30
31/// Returns whether the current CPU is allowed to respond to interrupts.
32#[inline]
33pub fn irqs_enabled() -> bool {
34 interrupts::are_enabled()
35}
36
37/// Relaxes the current CPU and waits for interrupts.
38///
39/// It must be called with interrupts enabled, otherwise it will never return.
40#[inline]
41pub fn wait_for_irqs() {
42 if cfg!(target_os = "none") {
43 unsafe { asm!("hlt") }
44 } else {
45 core::hint::spin_loop()
46 }
47}
48
49/// Halt the current CPU.
50#[inline]
51pub fn halt() {
52 disable_irqs();
53 wait_for_irqs(); // should never return
54}
55
56/// Reads the current page table root register for user space (`CR3`).
57///
58/// x86_64 does not have a separate page table root register for user and
59/// kernel space, so this operation is the same as [`read_kernel_page_table`].
60///
61/// Returns the physical address of the page table root.
62#[inline]
63pub fn read_user_page_table() -> PhysAddr {
64 pa!(unsafe { controlregs::cr3() } as usize).align_down_4k()
65}
66
67/// Reads the current page table root register for kernel space (`CR3`).
68///
69/// x86_64 does not have a separate page table root register for user and
70/// kernel space, so this operation is the same as [`read_user_page_table`].
71///
72/// Returns the physical address of the page table root.
73#[inline]
74pub fn read_kernel_page_table() -> PhysAddr {
75 read_user_page_table()
76}
77
78/// Writes the register to update the current page table root for user space
79/// (`CR3`).
80///
81/// x86_64 does not have a separate page table root register for user
82/// and kernel space, so this operation is the same as [`write_kernel_page_table`].
83///
84/// Note that the TLB will be **flushed** after this operation.
85///
86/// # Safety
87///
88/// This function is unsafe as it changes the virtual memory address space.
89#[inline]
90pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
91 unsafe { controlregs::cr3_write(root_paddr.as_usize() as _) }
92}
93
94/// Writes the register to update the current page table root for kernel space
95/// (`CR3`).
96///
97/// x86_64 does not have a separate page table root register for user
98/// and kernel space, so this operation is the same as [`write_user_page_table`].
99///
100/// Note that the TLB will be **flushed** after this operation.
101///
102/// # Safety
103///
104/// This function is unsafe as it changes the virtual memory address space.
105#[inline]
106pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
107 unsafe { write_user_page_table(root_paddr) }
108}
109
110/// Flushes the TLB.
111///
112/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB
113/// entry that maps the given virtual address.
114#[inline]
115pub fn flush_tlb(vaddr: Option<VirtAddr>) {
116 if let Some(vaddr) = vaddr {
117 unsafe { tlb::flush(vaddr.into()) }
118 } else {
119 unsafe { tlb::flush_all() }
120 }
121}
122
123/// Reads the thread pointer of the current CPU (`FS_BASE`).
124///
125/// It is used to implement TLS (Thread Local Storage).
126#[inline]
127pub fn read_thread_pointer() -> usize {
128 unsafe { msr::rdmsr(msr::IA32_FS_BASE) as usize }
129}
130
131/// Writes the thread pointer of the current CPU (`FS_BASE`).
132///
133/// It is used to implement TLS (Thread Local Storage).
134///
135/// # Safety
136///
137/// This function is unsafe as it changes the CPU states.
138#[inline]
139pub unsafe fn write_thread_pointer(fs_base: usize) {
140 unsafe { msr::wrmsr(msr::IA32_FS_BASE, fs_base as u64) }
141}
142
143#[cfg(feature = "uspace")]
144core::arch::global_asm!(include_str!("user_copy.S"));
145
146#[cfg(feature = "uspace")]
147unsafe extern "C" {
148 /// Copies data from source to destination, where addresses may be in user
149 /// space. Equivalent to memcpy.
150 ///
151 /// # Safety
152 /// This function is unsafe because it performs raw memory operations.
153 ///
154 /// # Returns
155 /// Returns the number of bytes not copied. This means 0 indicates success,
156 /// while a value > 0 indicates failure.
157 pub fn user_copy(dst: *mut u8, src: *const u8, size: usize) -> usize;
158}