kernel/arch/riscv64/vm/
mod.rs1pub mod mmu;
9
10extern crate alloc;
11
12use alloc::{boxed::Box, vec};
13use alloc::vec::Vec;
14use hashbrown::HashMap;
15use mmu::PageTable;
16use spin::RwLock;
17use spin::lazy::Lazy;
18
19use crate::mem::page::allocate_raw_pages;
20
21const NUM_OF_ASID: usize = u16::MAX as usize + 1; static mut ASID_BITMAP_TABLES: Lazy<RwLock<[u64; NUM_OF_ASID / 64]>> = Lazy::new(|| {
23 let mut tables = [0; NUM_OF_ASID / 64];
24 tables[0] = 1; RwLock::new(tables)
26});
27static mut PAGE_TABLES: Lazy<RwLock<HashMap<u16, Vec<Box<PageTable>>>>> = Lazy::new(|| RwLock::new(HashMap::new()));
29
30pub fn get_pagetable(ptr: *mut PageTable) -> Option<&'static mut PageTable> {
31 unsafe {
32 if ptr.is_null() {
33 return None;
34 }
35 Some(&mut *ptr)
36 }
37}
38
39fn new_boxed_pagetable() -> Box<PageTable> {
40 let ptr = allocate_raw_pages(1) as *mut PageTable;
41 if ptr.is_null() {
42 panic!("Failed to allocate a new page table");
43 }
44 unsafe { Box::from_raw(ptr) }
45}
46
47
48#[allow(static_mut_refs)]
61pub unsafe fn new_raw_pagetable(asid: u16) -> *mut PageTable {
62 let boxed_pagetable = new_boxed_pagetable();
63 let ptr = Box::into_raw(boxed_pagetable);
64 let mut page_tables = unsafe { PAGE_TABLES.write() };
65 page_tables.get_mut(&asid).expect("Invalid ASID").push(
66 unsafe { Box::from_raw(ptr) } );
68 ptr
70}
71
72#[allow(static_mut_refs)]
73pub fn alloc_virtual_address_space() -> u16 {
74 unsafe {
75 let mut asid_table = ASID_BITMAP_TABLES.write();
76 for word_idx in 0..(NUM_OF_ASID / 64) {
77 let word = asid_table[word_idx];
78 if word != u64::MAX { let bit_pos = (!word).trailing_zeros() as usize; asid_table[word_idx] |= 1 << bit_pos; let asid = (word_idx * 64 + bit_pos) as u16; let root_pagetable_ptr = Box::into_raw(new_boxed_pagetable());
83 let mut page_tables = PAGE_TABLES.write();
84 page_tables.insert(asid, vec![Box::from_raw(root_pagetable_ptr)]);
86
87 if root_pagetable_ptr.is_null() {
88 panic!("Failed to allocate a new root page table");
89 }
90
91 return asid; }
93 };
94 panic!("No available root page table");
95 }
96}
97
98#[allow(static_mut_refs)]
99pub fn free_virtual_address_space(asid: u16) {
100 unsafe {
101 let asid = asid as usize;
102 if asid < NUM_OF_ASID {
103 let bit_pos = asid % 64;
104 let word_idx = asid / 64;
105 let mut asid_table = ASID_BITMAP_TABLES.write();
106 if asid_table[word_idx] & (1 << bit_pos) == 0 {
107 panic!("ASID {} is already free", asid);
108 }
109 let mut page_tables = PAGE_TABLES.write();
110 page_tables.remove(&(asid as u16)); asid_table[word_idx] &= !(1 << bit_pos); } else {
113 panic!("Invalid ASID: {}", asid);
114 }
115 }
116}
117
118#[allow(static_mut_refs)]
119pub fn is_asid_used(asid: u16) -> bool {
120 unsafe {
121 let asid = asid as usize;
122 if asid < NUM_OF_ASID {
123 let word_idx = asid / 64;
124 let bit_pos = asid % 64;
125 let asid_table = ASID_BITMAP_TABLES.read();
126 (asid_table[word_idx] & (1 << bit_pos)) != 0
127 } else {
128 false
129 }
130 }
131}
132
133#[allow(static_mut_refs)]
134pub fn get_root_pagetable_ptr(asid: u16) -> Option<*mut PageTable> {
135 unsafe {
136 if is_asid_used(asid) {
137 let page_tabels = PAGE_TABLES.read();
138 let root_page_table = page_tabels.get(&asid)?[0].as_ref();
140 Some(root_page_table as *const PageTable as *mut PageTable)
141
142 } else {
143 None
144 }
145 }
146}
147
148pub fn get_root_pagetable(asid: u16) -> Option<&'static mut PageTable> {
149 let addr = get_root_pagetable_ptr(asid)?;
150 unsafe {
151 if addr.is_null() {
152 None
153 } else {
154 Some(&mut *addr)
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test_case]
164 fn test_get_page_table() {
165 let asid = alloc_virtual_address_space();
166 let ptr = unsafe { new_raw_pagetable(asid) };
167 let page_table = get_pagetable(ptr);
168 assert!(page_table.is_some());
169 free_virtual_address_space(asid);
170 }
171
172 #[test_case]
173 fn test_get_root_page_table_idx() {
174 let asid = alloc_virtual_address_space();
175 let root_page_table_idx = get_root_pagetable(asid as u16);
176 assert!(root_page_table_idx.is_some());
177 }
178
179 #[test_case]
180 fn test_alloc_virtual_address_space() {
181 let asid_0 = alloc_virtual_address_space();
182 crate::early_println!("Allocated ASID: {}", asid_0);
183 assert!(is_asid_used(asid_0));
184 let asid_1 = alloc_virtual_address_space();
185 crate::early_println!("Allocated ASID: {}", asid_1);
186 assert_eq!(asid_1, asid_0 + 1);
187 assert!(is_asid_used(asid_1));
188 free_virtual_address_space(asid_1);
189 assert!(!is_asid_used(asid_1));
190
191 free_virtual_address_space(asid_0);
192 assert!(!is_asid_used(asid_0));
193 }
194}