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