os/mm/
page_table.rs

1//! Implementation of [`PageTableEntry`] and [`PageTable`].
2
3use super::{FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum, frame_alloc};
4use alloc::string::String;
5use alloc::vec;
6use alloc::vec::Vec;
7use bitflags::*;
8
9bitflags! {
10    pub struct PTEFlags: u8 {
11        const V = 1 << 0;
12        const R = 1 << 1;
13        const W = 1 << 2;
14        const X = 1 << 3;
15        const U = 1 << 4;
16        const G = 1 << 5;
17        const A = 1 << 6;
18        const D = 1 << 7;
19    }
20}
21
22#[derive(Copy, Clone)]
23#[repr(C)]
24/// page table entry structure
25pub struct PageTableEntry {
26    ///PTE
27    pub bits: usize,
28}
29
30impl PageTableEntry {
31    ///Create a PTE from ppn
32    pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self {
33        PageTableEntry {
34            bits: ppn.0 << 10 | flags.bits as usize,
35        }
36    }
37    ///Return an empty PTE
38    pub fn empty() -> Self {
39        PageTableEntry { bits: 0 }
40    }
41    ///Return 44bit ppn
42    pub fn ppn(&self) -> PhysPageNum {
43        (self.bits >> 10 & ((1usize << 44) - 1)).into()
44    }
45    ///Return 10bit flag
46    pub fn flags(&self) -> PTEFlags {
47        PTEFlags::from_bits(self.bits as u8).unwrap()
48    }
49    ///Check PTE valid
50    pub fn is_valid(&self) -> bool {
51        (self.flags() & PTEFlags::V) != PTEFlags::empty()
52    }
53    ///Check PTE readable
54    pub fn readable(&self) -> bool {
55        (self.flags() & PTEFlags::R) != PTEFlags::empty()
56    }
57    ///Check PTE writable
58    pub fn writable(&self) -> bool {
59        (self.flags() & PTEFlags::W) != PTEFlags::empty()
60    }
61    ///Check PTE executable
62    pub fn executable(&self) -> bool {
63        (self.flags() & PTEFlags::X) != PTEFlags::empty()
64    }
65}
66
67pub struct PageTable {
68    root_ppn: PhysPageNum,
69    frames: Vec<FrameTracker>,
70}
71
72/// Assume that it won't oom when creating/mapping.
73impl PageTable {
74    pub fn new() -> Self {
75        let frame = frame_alloc().unwrap();
76        PageTable {
77            root_ppn: frame.ppn,
78            frames: vec![frame],
79        }
80    }
81    /// Temporarily used to get arguments from user space.
82    pub fn from_token(satp: usize) -> Self {
83        Self {
84            root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)),
85            frames: Vec::new(),
86        }
87    }
88    fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
89        let idxs = vpn.indexes();
90        let mut ppn = self.root_ppn;
91        let mut result: Option<&mut PageTableEntry> = None;
92        for (i, idx) in idxs.iter().enumerate() {
93            let pte = &mut ppn.get_pte_array()[*idx];
94            if i == 2 {
95                result = Some(pte);
96                break;
97            }
98            if !pte.is_valid() {
99                let frame = frame_alloc().unwrap();
100                *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
101                self.frames.push(frame);
102            }
103            ppn = pte.ppn();
104        }
105        result
106    }
107    fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
108        let idxs = vpn.indexes();
109        let mut ppn = self.root_ppn;
110        let mut result: Option<&mut PageTableEntry> = None;
111        for (i, idx) in idxs.iter().enumerate() {
112            let pte = &mut ppn.get_pte_array()[*idx];
113            if i == 2 {
114                result = Some(pte);
115                break;
116            }
117            if !pte.is_valid() {
118                return None;
119            }
120            ppn = pte.ppn();
121        }
122        result
123    }
124    #[allow(unused)]
125    pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
126        let pte = self.find_pte_create(vpn).unwrap();
127        assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn);
128        *pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
129    }
130    #[allow(unused)]
131    pub fn unmap(&mut self, vpn: VirtPageNum) {
132        let pte = self.find_pte(vpn).unwrap();
133        assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
134        *pte = PageTableEntry::empty();
135    }
136    pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
137        self.find_pte(vpn).map(|pte| *pte)
138    }
139    pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
140        self.find_pte(va.clone().floor()).map(|pte| {
141            //println!("translate_va:va = {:?}", va);
142            let aligned_pa: PhysAddr = pte.ppn().into();
143            //println!("translate_va:pa_align = {:?}", aligned_pa);
144            let offset = va.page_offset();
145            let aligned_pa_usize: usize = aligned_pa.into();
146            (aligned_pa_usize + offset).into()
147        })
148    }
149    pub fn token(&self) -> usize {
150        8usize << 60 | self.root_ppn.0
151    }
152}
153/// translate a pointer to a mutable u8 Vec through page table
154pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
155    let page_table = PageTable::from_token(token);
156    let mut start = ptr as usize;
157    let end = start + len;
158    let mut v = Vec::new();
159    while start < end {
160        let start_va = VirtAddr::from(start);
161        let mut vpn = start_va.floor();
162        let ppn = page_table.translate(vpn).unwrap().ppn();
163        vpn.step();
164        let mut end_va: VirtAddr = vpn.into();
165        end_va = end_va.min(VirtAddr::from(end));
166        if end_va.page_offset() == 0 {
167            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
168        } else {
169            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
170        }
171        start = end_va.into();
172    }
173    v
174}
175/// translate a pointer to a mutable u8 Vec end with `\0` through page table to a `String`
176pub fn translated_str(token: usize, ptr: *const u8) -> String {
177    let page_table = PageTable::from_token(token);
178    let mut string = String::new();
179    let mut va = ptr as usize;
180    loop {
181        let ch: u8 = *(page_table
182            .translate_va(VirtAddr::from(va))
183            .unwrap()
184            .get_mut());
185        if ch == 0 {
186            break;
187        } else {
188            string.push(ch as char);
189            va += 1;
190        }
191    }
192    string
193}
194///translate a generic through page table and return a mutable reference
195pub fn translated_refmut<T>(token: usize, ptr: *mut T) -> &'static mut T {
196    //println!("into translated_refmut!");
197    let page_table = PageTable::from_token(token);
198    let va = ptr as usize;
199    //println!("translated_refmut: before translate_va");
200    page_table
201        .translate_va(VirtAddr::from(va))
202        .unwrap()
203        .get_mut()
204}