Skip to main content

os/mm/
memory_set.rs

1//! Implementation of [`MapArea`] and [`MemorySet`].
2
3use super::{FrameTracker, frame_alloc};
4use super::{PTEFlags, PageTable, PageTableEntry};
5use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum};
6use super::{StepByOne, VPNRange};
7use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, USER_STACK_SIZE};
8use crate::sync::UPSafeCell;
9use alloc::collections::BTreeMap;
10use alloc::sync::Arc;
11use alloc::vec::Vec;
12use core::arch::asm;
13use lazy_static::*;
14use riscv::register::satp;
15
16unsafe extern "C" {
17    safe fn stext();
18    safe fn etext();
19    safe fn srodata();
20    safe fn erodata();
21    safe fn sdata();
22    safe fn edata();
23    safe fn sbss_with_stack();
24    safe fn ebss();
25    safe fn ekernel();
26    safe fn strampoline();
27}
28
29lazy_static! {
30    /// a memory set instance through lazy_static! managing kernel space
31    pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> =
32        Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) });
33}
34/// memory set structure, controls virtual-memory space
35pub struct MemorySet {
36    page_table: PageTable,
37    areas: Vec<MapArea>,
38}
39
40impl MemorySet {
41    ///Create an empty `MemorySet`
42    pub fn new_bare() -> Self {
43        Self {
44            page_table: PageTable::new(),
45            areas: Vec::new(),
46        }
47    }
48    ///Get pagetable `root_ppn`
49    pub fn token(&self) -> usize {
50        self.page_table.token()
51    }
52    /// Assume that no conflicts.
53    pub fn insert_framed_area(
54        &mut self,
55        start_va: VirtAddr,
56        end_va: VirtAddr,
57        permission: MapPermission,
58    ) {
59        self.push(
60            MapArea::new(start_va, end_va, MapType::Framed, permission),
61            None,
62        );
63    }
64    ///Remove `MapArea` that starts with `start_vpn`
65    pub fn remove_area_with_start_vpn(&mut self, start_vpn: VirtPageNum) {
66        if let Some((idx, area)) = self
67            .areas
68            .iter_mut()
69            .enumerate()
70            .find(|(_, area)| area.vpn_range.get_start() == start_vpn)
71        {
72            area.unmap(&mut self.page_table);
73            self.areas.remove(idx);
74        }
75    }
76    fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) {
77        map_area.map(&mut self.page_table);
78        if let Some(data) = data {
79            map_area.copy_data(&self.page_table, data);
80        }
81        self.areas.push(map_area);
82    }
83    /// Mention that trampoline is not collected by areas.
84    fn map_trampoline(&mut self) {
85        self.page_table.map(
86            VirtAddr::from(TRAMPOLINE).into(),
87            PhysAddr::from(linker_symbol_addr!(strampoline)).into(),
88            PTEFlags::R | PTEFlags::X,
89        );
90    }
91    /// Without kernel stacks.
92    pub fn new_kernel() -> Self {
93        let mut memory_set = Self::new_bare();
94        // map trampoline
95        memory_set.map_trampoline();
96        // map kernel sections
97        println!(
98            ".text [{:#x}, {:#x})",
99            linker_symbol_addr!(stext),
100            linker_symbol_addr!(etext)
101        );
102        println!(
103            ".rodata [{:#x}, {:#x})",
104            linker_symbol_addr!(srodata),
105            linker_symbol_addr!(erodata)
106        );
107        println!(
108            ".data [{:#x}, {:#x})",
109            linker_symbol_addr!(sdata),
110            linker_symbol_addr!(edata)
111        );
112        println!(
113            ".bss [{:#x}, {:#x})",
114            linker_symbol_addr!(sbss_with_stack),
115            linker_symbol_addr!(ebss)
116        );
117        println!("mapping .text section");
118        memory_set.push(
119            MapArea::new(
120                (linker_symbol_addr!(stext)).into(),
121                (linker_symbol_addr!(etext)).into(),
122                MapType::Identical,
123                MapPermission::R | MapPermission::X,
124            ),
125            None,
126        );
127        println!("mapping .rodata section");
128        memory_set.push(
129            MapArea::new(
130                (linker_symbol_addr!(srodata)).into(),
131                (linker_symbol_addr!(erodata)).into(),
132                MapType::Identical,
133                MapPermission::R,
134            ),
135            None,
136        );
137        println!("mapping .data section");
138        memory_set.push(
139            MapArea::new(
140                (linker_symbol_addr!(sdata)).into(),
141                (linker_symbol_addr!(edata)).into(),
142                MapType::Identical,
143                MapPermission::R | MapPermission::W,
144            ),
145            None,
146        );
147        println!("mapping .bss section");
148        memory_set.push(
149            MapArea::new(
150                (linker_symbol_addr!(sbss_with_stack)).into(),
151                (linker_symbol_addr!(ebss)).into(),
152                MapType::Identical,
153                MapPermission::R | MapPermission::W,
154            ),
155            None,
156        );
157        println!("mapping physical memory");
158        memory_set.push(
159            MapArea::new(
160                (linker_symbol_addr!(ekernel)).into(),
161                MEMORY_END.into(),
162                MapType::Identical,
163                MapPermission::R | MapPermission::W,
164            ),
165            None,
166        );
167        println!("mapping memory-mapped registers");
168        for pair in MMIO {
169            memory_set.push(
170                MapArea::new(
171                    (*pair).0.into(),
172                    ((*pair).0 + (*pair).1).into(),
173                    MapType::Identical,
174                    MapPermission::R | MapPermission::W,
175                ),
176                None,
177            );
178        }
179        memory_set
180    }
181    /// Include sections in elf and trampoline and TrapContext and user stack,
182    /// also returns user_sp and entry point.
183    pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
184        let mut memory_set = Self::new_bare();
185        // map trampoline
186        memory_set.map_trampoline();
187        // map program headers of elf, with U flag
188        let elf = xmas_elf::ElfFile::new(elf_data).unwrap();
189        let elf_header = elf.header;
190        let magic = elf_header.pt1.magic;
191        assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
192        let ph_count = elf_header.pt2.ph_count();
193        let mut max_end_vpn = VirtPageNum(0);
194        for i in 0..ph_count {
195            let ph = elf.program_header(i).unwrap();
196            if ph.get_type().unwrap() == xmas_elf::program::Type::Load {
197                let start_va: VirtAddr = (ph.virtual_addr() as usize).into();
198                let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into();
199                let mut map_perm = MapPermission::U;
200                let ph_flags = ph.flags();
201                if ph_flags.is_read() {
202                    map_perm |= MapPermission::R;
203                }
204                if ph_flags.is_write() {
205                    map_perm |= MapPermission::W;
206                }
207                if ph_flags.is_execute() {
208                    map_perm |= MapPermission::X;
209                }
210                let map_area = MapArea::new(start_va, end_va, MapType::Framed, map_perm);
211                max_end_vpn = map_area.vpn_range.get_end();
212                memory_set.push(
213                    map_area,
214                    Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize]),
215                );
216            }
217        }
218        // map user stack with U flags
219        let max_end_va: VirtAddr = max_end_vpn.into();
220        let mut user_stack_bottom: usize = max_end_va.into();
221        // guard page
222        user_stack_bottom += PAGE_SIZE;
223        let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
224        memory_set.push(
225            MapArea::new(
226                user_stack_bottom.into(),
227                user_stack_top.into(),
228                MapType::Framed,
229                MapPermission::R | MapPermission::W | MapPermission::U,
230            ),
231            None,
232        );
233        // map TrapContext
234        memory_set.push(
235            MapArea::new(
236                TRAP_CONTEXT.into(),
237                TRAMPOLINE.into(),
238                MapType::Framed,
239                MapPermission::R | MapPermission::W,
240            ),
241            None,
242        );
243        (
244            memory_set,
245            user_stack_top,
246            elf.header.pt2.entry_point() as usize,
247        )
248    }
249    ///Clone a same `MemorySet`
250    pub fn from_existed_user(user_space: &Self) -> Self {
251        let mut memory_set = Self::new_bare();
252        // map trampoline
253        memory_set.map_trampoline();
254        // copy data sections/trap_context/user_stack
255        for area in user_space.areas.iter() {
256            let new_area = MapArea::from_another(area);
257            memory_set.push(new_area, None);
258            // copy data from another space
259            for vpn in area.vpn_range {
260                let src_ppn = user_space.translate(vpn).unwrap().ppn();
261                let dst_ppn = memory_set.translate(vpn).unwrap().ppn();
262                dst_ppn
263                    .get_bytes_array()
264                    .copy_from_slice(src_ppn.get_bytes_array());
265            }
266        }
267        memory_set
268    }
269    ///Refresh TLB with `sfence.vma`
270    pub fn activate(&self) {
271        let satp = self.page_table.token();
272        unsafe {
273            satp::write(satp::Satp::from_bits(satp));
274            asm!("sfence.vma");
275        }
276    }
277    ///Translate throuth pagetable
278    pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
279        self.page_table.translate(vpn)
280    }
281    ///Remove all `MapArea`
282    pub fn recycle_data_pages(&mut self) {
283        //*self = Self::new_bare();
284        self.areas.clear();
285    }
286}
287/// map area structure, controls a contiguous piece of virtual memory
288pub struct MapArea {
289    vpn_range: VPNRange,
290    data_frames: BTreeMap<VirtPageNum, FrameTracker>,
291    map_type: MapType,
292    map_perm: MapPermission,
293}
294
295impl MapArea {
296    pub fn new(
297        start_va: VirtAddr,
298        end_va: VirtAddr,
299        map_type: MapType,
300        map_perm: MapPermission,
301    ) -> Self {
302        let start_vpn: VirtPageNum = start_va.floor();
303        let end_vpn: VirtPageNum = end_va.ceil();
304        Self {
305            vpn_range: VPNRange::new(start_vpn, end_vpn),
306            data_frames: BTreeMap::new(),
307            map_type,
308            map_perm,
309        }
310    }
311    pub fn from_another(another: &Self) -> Self {
312        Self {
313            vpn_range: VPNRange::new(another.vpn_range.get_start(), another.vpn_range.get_end()),
314            data_frames: BTreeMap::new(),
315            map_type: another.map_type,
316            map_perm: another.map_perm,
317        }
318    }
319    pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
320        let ppn: PhysPageNum;
321        match self.map_type {
322            MapType::Identical => {
323                ppn = PhysPageNum(vpn.0);
324            }
325            MapType::Framed => {
326                let frame = frame_alloc().unwrap();
327                ppn = frame.ppn;
328                self.data_frames.insert(vpn, frame);
329            }
330        }
331        let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
332        page_table.map(vpn, ppn, pte_flags);
333    }
334    pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
335        if self.map_type == MapType::Framed {
336            self.data_frames.remove(&vpn);
337        }
338        page_table.unmap(vpn);
339    }
340    pub fn map(&mut self, page_table: &mut PageTable) {
341        for vpn in self.vpn_range {
342            self.map_one(page_table, vpn);
343        }
344    }
345    pub fn unmap(&mut self, page_table: &mut PageTable) {
346        for vpn in self.vpn_range {
347            self.unmap_one(page_table, vpn);
348        }
349    }
350    /// data: start-aligned but maybe with shorter length
351    /// assume that all frames were cleared before
352    pub fn copy_data(&mut self, page_table: &PageTable, data: &[u8]) {
353        assert_eq!(self.map_type, MapType::Framed);
354        let mut start: usize = 0;
355        let mut current_vpn = self.vpn_range.get_start();
356        let len = data.len();
357        loop {
358            let src = &data[start..len.min(start + PAGE_SIZE)];
359            let dst = &mut page_table
360                .translate(current_vpn)
361                .unwrap()
362                .ppn()
363                .get_bytes_array()[..src.len()];
364            dst.copy_from_slice(src);
365            start += PAGE_SIZE;
366            if start >= len {
367                break;
368            }
369            current_vpn.step();
370        }
371    }
372}
373
374#[derive(Copy, Clone, PartialEq, Debug)]
375/// map type for memory set: identical or framed
376pub enum MapType {
377    Identical,
378    Framed,
379}
380
381bitflags! {
382    /// map permission corresponding to that in pte: `R W X U`
383    pub struct MapPermission: u8 {
384        ///Readable
385        const R = 1 << 1;
386        ///Writable
387        const W = 1 << 2;
388        ///Excutable
389        const X = 1 << 3;
390        ///Accessible in U mode
391        const U = 1 << 4;
392    }
393}
394
395#[allow(unused)]
396///Check PageTable running correctly
397pub fn remap_test() {
398    let mut kernel_space = KERNEL_SPACE.exclusive_access();
399    let mid_text: VirtAddr = ((linker_symbol_addr!(stext) + linker_symbol_addr!(etext)) / 2).into();
400    let mid_rodata: VirtAddr =
401        ((linker_symbol_addr!(srodata) + linker_symbol_addr!(erodata)) / 2).into();
402    let mid_data: VirtAddr = ((linker_symbol_addr!(sdata) + linker_symbol_addr!(edata)) / 2).into();
403    assert!(
404        !kernel_space
405            .page_table
406            .translate(mid_text.floor())
407            .unwrap()
408            .writable(),
409    );
410    assert!(
411        !kernel_space
412            .page_table
413            .translate(mid_rodata.floor())
414            .unwrap()
415            .writable(),
416    );
417    assert!(
418        !kernel_space
419            .page_table
420            .translate(mid_data.floor())
421            .unwrap()
422            .executable(),
423    );
424    println!("remap_test passed!");
425}