-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathasm.rs
More file actions
143 lines (129 loc) · 3.91 KB
/
Copy pathasm.rs
File metadata and controls
143 lines (129 loc) · 3.91 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Wrapper functions for assembly instructions.
use memory_addr::{PhysAddr, VirtAddr};
use riscv::{
asm,
register::{satp, sstatus, stvec},
};
/// Allows the current CPU to respond to interrupts.
#[inline]
pub fn enable_irqs() {
unsafe { sstatus::set_sie() }
}
/// Makes the current CPU to ignore interrupts.
#[inline]
pub fn disable_irqs() {
unsafe { sstatus::clear_sie() }
}
/// Returns whether the current CPU is allowed to respond to interrupts.
#[inline]
pub fn irqs_enabled() -> bool {
sstatus::read().sie()
}
/// 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() {
riscv::asm::wfi()
}
/// Halt the current CPU.
#[inline]
pub fn halt() {
disable_irqs();
riscv::asm::wfi() // should never return
}
/// Reads the current page table root register for user space (`satp`).
///
/// RISC-V does not have a separate page table root register for user and
/// kernel space, so this operation is the same as [`read_kernel_page_table`].
///
/// Returns the physical address of the page table root.
#[inline]
pub fn read_user_page_table() -> PhysAddr {
pa!(satp::read().ppn() << 12)
}
/// Reads the current page table root register for kernel space (`satp`).
///
/// RISC-V does not have a separate page table root register for user and
/// kernel space, so this operation is the same as [`read_user_page_table`].
///
/// Returns the physical address of the page table root.
#[inline]
pub fn read_kernel_page_table() -> PhysAddr {
read_user_page_table()
}
/// Writes the register to update the current page table root for user space
/// (`satp`).
///
/// RISC-V does not have a separate page table root register for user
/// and kernel space, so this operation is the same as [`write_kernel_page_table`].
///
/// Note that the TLB is **NOT** flushed after this operation.
///
/// # Safety
///
/// This function is unsafe as it changes the virtual memory address space.
#[inline]
pub unsafe fn write_user_page_table(root_paddr: PhysAddr) {
unsafe { satp::set(satp::Mode::Sv39, 0, root_paddr.as_usize() >> 12) };
}
/// Writes the register to update the current page table root for user space
/// (`satp`).
///
/// RISC-V does not have a separate page table root register for user
/// and kernel space, so this operation is the same as [`write_user_page_table`].
///
/// Note that the TLB is **NOT** flushed after this operation.
///
/// # Safety
///
/// This function is unsafe as it changes the virtual memory address space.
#[inline]
pub unsafe fn write_kernel_page_table(root_paddr: PhysAddr) {
unsafe { write_user_page_table(root_paddr) };
}
/// 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 {
asm::sfence_vma(0, vaddr.as_usize())
} else {
asm::sfence_vma_all();
}
}
/// Writes the Supervisor Trap Vector Base Address register (`stvec`).
///
/// # Safety
///
/// This function is unsafe as it changes the exception handling behavior of the
/// current CPU.
#[inline]
pub unsafe fn write_trap_vector_base(stvec: usize) {
let mut reg = stvec::read();
reg.set_address(stvec);
reg.set_trap_mode(stvec::TrapMode::Direct);
unsafe { stvec::write(reg) }
}
/// Reads the thread pointer of the current CPU (`tp`).
///
/// It is used to implement TLS (Thread Local Storage).
#[inline]
pub fn read_thread_pointer() -> usize {
let tp;
unsafe { core::arch::asm!("mv {}, tp", out(reg) tp) };
tp
}
/// Writes the thread pointer of the current CPU (`tp`).
///
/// 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(tp: usize) {
unsafe { core::arch::asm!("mv tp, {}", in(reg) tp) }
}