diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index de4a50d..3a187f0 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -266,6 +266,9 @@ impl MemorySet { false } } + pub fn unmap(&mut self, vpn: VirtPageNum) { + self.page_table.unmap(vpn) + } } /// map area structure, controls a contiguous piece of virtual memory diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index e0fd13c..f7024cf 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -13,7 +13,8 @@ mod memory_set; mod page_table; pub use address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; -use address::{StepByOne, VPNRange}; +use address::StepByOne; +pub use address::VPNRange; pub use frame_allocator::{frame_alloc, FrameTracker}; pub use memory_set::remap_test; pub use memory_set::{MapPermission, MemorySet, KERNEL_SPACE}; diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index e5b4ff0..f0159c2 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -15,6 +15,8 @@ const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_SBRK: usize = 214; +const SYSCALL_MMAP: usize = 222; +const SYSCALL_MUNMAP: usize = 215; mod fs; mod process; @@ -30,6 +32,8 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_YIELD => sys_yield(), SYSCALL_GET_TIME => sys_get_time(), SYSCALL_SBRK => sys_sbrk(args[0] as i32), + SYSCALL_MMAP => sys_mmap(args[0], args[1], args[2]), + SYSCALL_MUNMAP => sys_munmap(args[0], args[1]), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index d302373..dd873bf 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,6 +1,8 @@ //! Process management syscalls -use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next}; +use crate::config::PAGE_SIZE; +use crate::mm::{MapPermission, VPNRange, VirtAddr}; +use crate::task::{change_program_brk, create_new_map_area, exit_current_and_run_next, get_current_task_page_table_entry, suspend_current_and_run_next, unmap_virtual_page}; use crate::timer::get_time_ms; /// task exits and submit an exit code @@ -29,3 +31,47 @@ pub fn sys_sbrk(size: i32) -> isize { -1 } } + +/// map files or devices into memory +pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { + if start % PAGE_SIZE != 0 || prot & !0x7 != 0 || prot & 0x7 == 0 { + return -1; + } + let vpn_start = VirtAddr::from(start).floor(); + let vpn_end = VirtAddr::from(start + len).ceil(); + let vpn_range = VPNRange::new(vpn_start, vpn_end); + for vpn in vpn_range { + if let Some(pte) = get_current_task_page_table_entry(vpn) { + if pte.is_valid() { + return -1; + } + } + } + create_new_map_area( + vpn_start.into(), + vpn_end.into(), + MapPermission::from_bits_truncate((prot << 1) as u8) | MapPermission::U + ); + 0 +} + +/// unmap files or devices into memory +pub fn sys_munmap(start: usize, len: usize) -> isize { + if start % PAGE_SIZE != 0 { + return -1; + } + let vpn_start = VirtAddr::from(start).floor(); + let vpn_end = VirtAddr::from(start + len).ceil(); + let vpn_range = VPNRange::new(vpn_start, vpn_end); + for vpn in vpn_range { + if let Some(pte) = get_current_task_page_table_entry(vpn) { + if !pte.is_valid() { + return -1; + } + unmap_virtual_page(vpn) + } else { + return -1; + } + } + 0 +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index f80440e..d89f20e 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -15,6 +15,7 @@ mod switch; mod task; use crate::loader::{get_app_data, get_num_app}; +use crate::mm::{MapPermission, PageTableEntry, VirtAddr, VirtPageNum}; use crate::sbi::shutdown; use crate::sync::UPSafeCell; use crate::trap::TrapContext; @@ -204,3 +205,24 @@ pub fn current_trap_cx() -> &'static mut TrapContext { pub fn change_program_brk(size: i32) -> Option { TASK_MANAGER.change_current_program_brk(size) } + +/// Get the curent 'Running' task's PTE +pub fn get_current_task_page_table_entry(vpn: VirtPageNum) -> Option { + let inner = TASK_MANAGER.inner.exclusive_access(); + let current = inner.current_task; + inner.tasks[current].memory_set.translate(vpn) +} + +/// create new mapping area +pub fn create_new_map_area(start_va: VirtAddr, end_va: VirtAddr, perm: MapPermission) { + let mut inner = TASK_MANAGER.inner.exclusive_access(); + let current = inner.current_task; + inner.tasks[current].memory_set.insert_framed_area(start_va, end_va, perm); +} + +/// unmap virtual page +pub fn unmap_virtual_page(vpn: VirtPageNum) { + let mut inner = TASK_MANAGER.inner.exclusive_access(); + let current = inner.current_task; + inner.tasks[current].memory_set.unmap(vpn); +} diff --git a/user/src/bin/ch4_mmap0.rs b/user/src/bin/ch4_mmap0.rs new file mode 100644 index 0000000..5247eec --- /dev/null +++ b/user/src/bin/ch4_mmap0.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:输出 Test 04_1 OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + for i in start..(start + len) { + let addr: *mut u8 = i as *mut u8; + unsafe { + *addr = i as u8; + } + } + for i in start..(start + len) { + let addr: *mut u8 = i as *mut u8; + unsafe { + assert_eq!(*addr, i as u8); + } + } + println!("Test 04_1 OK!"); + 0 +} diff --git a/user/src/bin/ch4_mmap1.rs b/user/src/bin/ch4_mmap1.rs new file mode 100644 index 0000000..63d6411 --- /dev/null +++ b/user/src/bin/ch4_mmap1.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 1; + assert_eq!(0, mmap(start, len, prot)); + let addr: *mut u8 = start as *mut u8; + unsafe { + *addr = start as u8; + } + println!("Should cause error, Test 04_2 fail!"); + 0 +} diff --git a/user/src/bin/ch4_mmap2.rs b/user/src/bin/ch4_mmap2.rs new file mode 100644 index 0000000..2489fa6 --- /dev/null +++ b/user/src/bin/ch4_mmap2.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 2; + assert_eq!(0, mmap(start, len, prot)); + let addr: *mut u8 = start as *mut u8; + unsafe { + // *addr = start as u8; // can't write, R == 0 && W == 1 is illegal in riscv + assert!(*addr != 0); + } + println!("Should cause error, Test 04_2 fail!"); + 0 +} diff --git a/user/src/bin/ch4_mmap3.rs b/user/src/bin/ch4_mmap3.rs new file mode 100644 index 0000000..d9d04b6 --- /dev/null +++ b/user/src/bin/ch4_mmap3.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::mmap; + +/* +理想结果:对于错误的 mmap 返回 -1,最终输出 Test 04_4 test OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(mmap(start - len, len + 1, prot), -1); + assert_eq!(mmap(start + len + 1, len, prot), -1); + assert_eq!(mmap(start + len, len, 0), -1); + assert_eq!(mmap(start + len, len, prot | 8), -1); + println!("Test 04_4 test OK!"); + 0 +} diff --git a/user/src/bin/ch4_unmap.rs b/user/src/bin/ch4_unmap.rs new file mode 100644 index 0000000..b64f862 --- /dev/null +++ b/user/src/bin/ch4_unmap.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{mmap, munmap}; + +/* +理想结果:输出 Test 04_5 ummap OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(mmap(start + len, len * 2, prot), 0); + assert_eq!(munmap(start, len), 0); + assert_eq!(mmap(start - len, len + 1, prot), 0); + for i in (start - len)..(start + len * 3) { + let addr: *mut u8 = i as *mut u8; + unsafe { + *addr = i as u8; + } + } + for i in (start - len)..(start + len * 3) { + let addr: *mut u8 = i as *mut u8; + unsafe { + assert_eq!(*addr, i as u8); + } + } + println!("Test 04_5 ummap OK!"); + 0 +} diff --git a/user/src/bin/ch4_unmap2.rs b/user/src/bin/ch4_unmap2.rs new file mode 100644 index 0000000..85ddb75 --- /dev/null +++ b/user/src/bin/ch4_unmap2.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{mmap, munmap}; + +/* +理想结果:输出 Test 04_6 ummap2 OK! +*/ + +#[no_mangle] +fn main() -> i32 { + let start: usize = 0x10000000; + let len: usize = 4096; + let prot: usize = 3; + assert_eq!(0, mmap(start, len, prot)); + assert_eq!(munmap(start, len + 1), -1); + assert_eq!(munmap(start + 1, len - 1), -1); + println!("Test 04_6 ummap2 OK!"); + 0 +} diff --git a/user/src/bin/ch4b_sbrk.rs b/user/src/bin/ch4b_sbrk.rs new file mode 100644 index 0000000..72bffe2 --- /dev/null +++ b/user/src/bin/ch4b_sbrk.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::sbrk; +use core::ptr::slice_from_raw_parts_mut; + +#[no_mangle] +fn main() -> i32 { + println!("Test sbrk start."); + const PAGE_SIZE: usize = 0x1000; + let origin_brk = sbrk(0); + println!("origin break point = {:x}", origin_brk); + let brk = sbrk(PAGE_SIZE as i32); + if brk != origin_brk { + return -1 + } + let brk = sbrk(0); + println!("one page allocated, break point = {:x}", brk); + println!("try write to allocated page"); + let new_page = unsafe { &mut *slice_from_raw_parts_mut(origin_brk as usize as *const u8 as *mut u8, PAGE_SIZE) }; + for pos in 0..PAGE_SIZE { + new_page[pos] = 1; + } + println!("write ok"); + sbrk(PAGE_SIZE as i32 * 10); + let brk = sbrk(0); + println!("10 page allocated, break point = {:x}", brk); + sbrk(PAGE_SIZE as i32 * -11); + let brk = sbrk(0); + println!("11 page DEALLOCATED, break point = {:x}", brk); + println!("try DEALLOCATED more one page, should be failed."); + let ret = sbrk(PAGE_SIZE as i32 * -1); + if ret != -1 { + println!("Test sbrk failed!"); + return -1 + } + println!("Test sbrk almost OK!"); + println!("now write to deallocated page, should cause page fault."); + for pos in 0..PAGE_SIZE { + new_page[pos] = 2; + } + println!("Test sbrk failed!"); + 0 +} diff --git a/user/src/lib.rs b/user/src/lib.rs index bd59ab7..6351f57 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -38,3 +38,11 @@ pub fn get_time() -> isize { pub fn sbrk(size: i32) -> isize { sys_sbrk(size) } + +pub fn munmap(start: usize, len: usize) -> isize { + sys_munmap(start, len) +} + +pub fn mmap(start: usize, len: usize, prot: usize) -> isize { + sys_mmap(start, len, prot) +} diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 31144b9..12a1662 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -5,6 +5,8 @@ const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_SBRK: usize = 214; +pub const SYSCALL_MMAP: usize = 222; +pub const SYSCALL_MUNMAP: usize = 215; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -39,3 +41,11 @@ pub fn sys_get_time() -> isize { pub fn sys_sbrk(size: i32) -> isize { syscall(SYSCALL_SBRK, [size as usize, 0, 0]) } + +pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { + syscall(SYSCALL_MMAP, [start, len, prot]) +} + +pub fn sys_munmap(start: usize, len: usize) -> isize { + syscall(SYSCALL_MUNMAP, [start, len, 0]) +}