Skip to main content

os/mm/
page_table.rs

1//! Implementation of [`PageTableEntry`] and [`PageTable`].
2
3use super::{FrameTracker, PhysPageNum, StepByOne, VirtAddr, VirtPageNum, frame_alloc};
4use alloc::vec;
5use alloc::vec::Vec;
6use bitflags::*;
7
8bitflags! {
9    /// page table entry flags
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    pub bits: usize,
27}
28
29impl PageTableEntry {
30    pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self {
31        PageTableEntry {
32            bits: ppn.0 << 10 | flags.bits as usize,
33        }
34    }
35    pub fn empty() -> Self {
36        PageTableEntry { bits: 0 }
37    }
38    pub fn ppn(&self) -> PhysPageNum {
39        (self.bits >> 10 & ((1usize << 44) - 1)).into()
40    }
41    pub fn flags(&self) -> PTEFlags {
42        PTEFlags::from_bits(self.bits as u8).unwrap()
43    }
44    pub fn is_valid(&self) -> bool {
45        (self.flags() & PTEFlags::V) != PTEFlags::empty()
46    }
47    pub fn readable(&self) -> bool {
48        (self.flags() & PTEFlags::R) != PTEFlags::empty()
49    }
50    pub fn writable(&self) -> bool {
51        (self.flags() & PTEFlags::W) != PTEFlags::empty()
52    }
53    pub fn executable(&self) -> bool {
54        (self.flags() & PTEFlags::X) != PTEFlags::empty()
55    }
56}
57
58/// page table structure
59pub struct PageTable {
60    root_ppn: PhysPageNum,
61    frames: Vec<FrameTracker>,
62}
63
64/// Assume that it won't oom when creating/mapping.
65impl PageTable {
66    pub fn new() -> Self {
67        let frame = frame_alloc().unwrap();
68        PageTable {
69            root_ppn: frame.ppn,
70            frames: vec![frame],
71        }
72    }
73    /// Temporarily used to get arguments from user space.
74    pub fn from_token(satp: usize) -> Self {
75        Self {
76            root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)),
77            frames: Vec::new(),
78        }
79    }
80    fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
81        let idxs = vpn.indexes();
82        let mut ppn = self.root_ppn;
83        let mut result: Option<&mut PageTableEntry> = None;
84        for (i, idx) in idxs.iter().enumerate() {
85            let pte = &mut ppn.get_pte_array()[*idx];
86            if i == 2 {
87                result = Some(pte);
88                break;
89            }
90            if !pte.is_valid() {
91                let frame = frame_alloc().unwrap();
92                *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
93                self.frames.push(frame);
94            }
95            ppn = pte.ppn();
96        }
97        result
98    }
99    fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
100        let idxs = vpn.indexes();
101        let mut ppn = self.root_ppn;
102        let mut result: Option<&mut PageTableEntry> = None;
103        for (i, idx) in idxs.iter().enumerate() {
104            let pte = &mut ppn.get_pte_array()[*idx];
105            if i == 2 {
106                result = Some(pte);
107                break;
108            }
109            if !pte.is_valid() {
110                return None;
111            }
112            ppn = pte.ppn();
113        }
114        result
115    }
116    #[allow(unused)]
117    pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
118        let pte = self.find_pte_create(vpn).unwrap();
119        assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn);
120        *pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
121    }
122    #[allow(unused)]
123    pub fn unmap(&mut self, vpn: VirtPageNum) {
124        let pte = self.find_pte(vpn).unwrap();
125        assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
126        *pte = PageTableEntry::empty();
127    }
128    pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
129        self.find_pte(vpn).map(|pte| *pte)
130    }
131    pub fn token(&self) -> usize {
132        8usize << 60 | self.root_ppn.0
133    }
134}
135
136/// translate a pointer to a mutable u8 Vec through page table
137pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
138    let page_table = PageTable::from_token(token);
139    let mut start = ptr as usize;
140    let end = start + len;
141    let mut v = Vec::new();
142    while start < end {
143        let start_va = VirtAddr::from(start);
144        let mut vpn = start_va.floor();
145        let ppn = page_table.translate(vpn).unwrap().ppn();
146        vpn.step();
147        let mut end_va: VirtAddr = vpn.into();
148        end_va = end_va.min(VirtAddr::from(end));
149        if end_va.page_offset() == 0 {
150            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
151        } else {
152            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
153        }
154        start = end_va.into();
155    }
156    v
157}