Skip to main content

ax_cpu/loongarch64/
asm.rs

1//! Wrapper functions for assembly instructions.
2
3use core::arch::asm;
4
5use ax_memory_addr::{PhysAddr, VirtAddr};
6use loongArch64::register::{crmd, ecfg, eentry, pgdh, pgdl};
7
8/// Allows the current CPU to respond to interrupts.
9#[inline]
10pub fn enable_irqs() {
11    crmd::set_ie(true)
12}
13
14/// Makes the current CPU to ignore interrupts.
15#[inline]
16pub fn disable_irqs() {
17    crmd::set_ie(false)
18}
19
20/// Returns whether the current CPU is allowed to respond to interrupts.
21#[inline]
22pub fn irqs_enabled() -> bool {
23    crmd::read().ie()
24}
25
26/// Relaxes the current CPU and waits for interrupts.
27///
28/// It must be called with interrupts enabled, otherwise it will never return.
29#[inline]
30pub fn wait_for_irqs() {
31    unsafe { loongArch64::asm::idle() }
32}
33
34/// Halt the current CPU.
35#[inline]
36pub fn halt() {
37    disable_irqs();
38    unsafe { loongArch64::asm::idle() }
39}
40
41/// Reads the current page table root register for user space (`PGDL`).
42///
43/// Returns the physical address of the page table root.
44#[inline]
45pub fn read_user_page_table() -> PhysAddr {
46    PhysAddr::from(pgdl::read().base())
47}
48
49/// Reads the current page table root register for kernel space (`PGDH`).
50///
51/// Returns the physical address of the page table root.
52#[inline]
53pub fn read_kernel_page_table() -> PhysAddr {
54    PhysAddr::from(pgdh::read().base())
55}
56
57/// Writes the register to update the current page table root for user space
58/// (`PGDL`).
59///
60/// Note that the TLB is **NOT** flushed after this operation.
61///
62/// # Safety
63///
64/// This function is unsafe as it changes the virtual memory address space.
65pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
66    pgdl::set_base(root_paddr.as_usize() as _);
67}
68
69/// Writes the register to update the current page table root for kernel space
70/// (`PGDH`).
71///
72/// Note that the TLB is **NOT** flushed after this operation.
73///
74/// # Safety
75///
76/// This function is unsafe as it changes the virtual memory address space.
77pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
78    pgdh::set_base(root_paddr.as_usize());
79}
80
81/// Flushes the entire instruction cache.
82/// See <https://elixir.bootlin.com/linux/v6.6/source/arch/loongarch/mm/cache.c#L38>
83#[inline]
84pub fn flush_icache_all() {
85    unsafe { asm!("ibar 0") };
86}
87
88/// Flushes the TLB.
89///
90/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB
91/// entry that maps the given virtual address.
92#[inline]
93pub fn flush_tlb(vaddr: Option<VirtAddr>) {
94    unsafe {
95        if let Some(vaddr) = vaddr {
96            // <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_dbar>
97            //
98            // Only after all previous load/store access operations are completely
99            // executed, the DBAR 0 instruction can be executed; and only after the
100            // execution of DBAR 0 is completed, all subsequent load/store access
101            // operations can be executed.
102            //
103            // <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_invtlb>
104            //
105            // formats: invtlb op, asid, addr
106            //
107            // op 0x5: Clear all page table entries with G=0 and ASID equal to the
108            // register specified ASID, and VA equal to the register specified VA.
109            //
110            // When the operation indicated by op does not require an ASID, the
111            // general register rj should be set to r0.
112            asm!("dbar 0; invtlb 0x05, $r0, {reg}", reg = in(reg) vaddr.as_usize());
113        } else {
114            // op 0x0: Clear all page table entries
115            asm!("dbar 0; invtlb 0x00, $r0, $r0");
116        }
117    }
118}
119
120/// Writes the Exception Entry Base Address register (`EENTRY`).
121///
122/// It also set the Exception Configuration register (`ECFG`) to `VS=0`.
123///
124/// - ECFG: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#exception-configuration>
125/// - EENTRY: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#exception-entry-base-address>
126///
127/// # Safety
128///
129/// This function is unsafe as it changes the exception handling behavior of the
130/// current CPU.
131#[inline]
132pub unsafe fn write_exception_entry_base(eentry: usize) {
133    ecfg::set_vs(0);
134    eentry::set_eentry(eentry);
135}
136
137/// Writes the Page Walk Controller registers (`PWCL` and `PWCH`).
138///
139/// # Safety
140///
141/// This function is unsafe as it changes the page walk configuration such as
142/// levels and starting bits.
143///
144/// - `PWCL`: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#page-walk-controller-for-lower-half-address-space>
145/// - `PWCH`: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#page-walk-controller-for-higher-half-address-space>
146#[inline]
147pub unsafe fn write_pwc(pwcl: u32, pwch: u32) {
148    unsafe {
149        asm!(
150            include_asm_macros!(),
151            "csrwr {}, LA_CSR_PWCL",
152            "csrwr {}, LA_CSR_PWCH",
153            in(reg) pwcl,
154            in(reg) pwch
155        )
156    }
157}
158
159/// Reads the thread pointer of the current CPU (`$tp`).
160///
161/// It is used to implement TLS (Thread Local Storage).
162#[inline]
163pub fn read_thread_pointer() -> usize {
164    let tp;
165    unsafe { asm!("move {}, $tp", out(reg) tp) };
166    tp
167}
168
169/// Writes the thread pointer of the current CPU (`$tp`).
170///
171/// It is used to implement TLS (Thread Local Storage).
172///
173/// # Safety
174///
175/// This function is unsafe as it changes the CPU states.
176#[inline]
177pub unsafe fn write_thread_pointer(tp: usize) {
178    unsafe { asm!("move $tp, {}", in(reg) tp) }
179}
180
181/// Enables floating-point instructions by setting `EUEN.FPE`.
182///
183/// - `EUEN`: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#extended-component-unit-enable>
184#[inline]
185pub fn enable_fp() {
186    loongArch64::register::euen::set_fpe(true);
187}
188
189/// Enables LSX extension by setting `EUEN.LSX`.
190///
191/// - `EUEN`: <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#extended-component-unit-enable>
192pub fn enable_lsx() {
193    loongArch64::register::euen::set_sxe(true);
194}
195
196#[cfg(feature = "uspace")]
197core::arch::global_asm!(include_asm_macros!(), include_str!("user_copy.S"));
198
199#[cfg(feature = "uspace")]
200unsafe extern "C" {
201    /// Copies data from source to destination, where addresses may be in user
202    /// space. Equivalent to memcpy.
203    ///
204    /// # Safety
205    /// This function is unsafe because it performs raw memory operations.
206    ///
207    /// # Returns
208    /// Returns the number of bytes not copied. This means 0 indicates success,
209    /// while a value > 0 indicates failure.
210    pub fn user_copy(dst: *mut u8, src: *const u8, size: usize) -> usize;
211}