kernel/arch/riscv64/vm/
mod.rs

1//! Virtual memory module for RISC-V architecture.
2//! 
3//! The virtual memory module is responsible for managing the virtual memory of the system.
4//! The module provides functions to initialize the virtual memory system, map physical memory to
5//! virtual memory, and switch page tables.
6//! 
7
8pub 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; // Maximum ASID value
22static 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; // Mark the first ASID as used to avoid returning 0, which is reserved
25    RwLock::new(tables)
26});
27// static mut ROOT_PAGE_TABLES: Lazy<RwLock<HashMap<u16, *mut PageTable>>> = Lazy::new(|| RwLock::new(HashMap::new()));
28static 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/// Allocates a new raw page table for the given ASID.
49/// 
50/// # Arguments
51/// * `asid` - The Address Space ID (ASID) for which the page table is allocated.
52/// 
53/// # Returns
54/// A raw pointer to the newly allocated page table.
55/// 
56/// # Safety
57/// This function is unsafe because it dereferences a raw pointer, which can lead to undefined behavior
58/// if the pointer is null or invalid.
59/// 
60#[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) } // Store the boxed page table in the HashMap
67    );
68    // Ensure the pointer is valid and return it
69    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 { // Check if there is a free ASID in this word
79                let bit_pos = (!word).trailing_zeros() as usize; // Find the first free bit (Must be < 64)
80                asid_table[word_idx] |= 1 << bit_pos; // Mark this ASID as used
81                let asid = (word_idx * 64 + bit_pos) as u16; // Calculate the ASID
82                let root_pagetable_ptr = Box::into_raw(new_boxed_pagetable());
83                let mut page_tables = PAGE_TABLES.write();
84                // Insert the new root page table into the HashMap
85                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; // Return the allocated ASID
92            }
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)); // Remove the page table associated with this ASID
111            asid_table[word_idx] &= !(1 << bit_pos); // Mark this ASID as free
112        } 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            // Root page table is always at index 0 for each ASID
139            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}