diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index f947154..a6a0fdb 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -204,6 +204,16 @@ impl MemorySet { ), None, ); + // used in sbrk + memory_set.push( + MapArea::new( + user_stack_top.into(), + user_stack_top.into(), + MapType::Framed, + MapPermission::R | MapPermission::W | MapPermission::U, + ), + None, + ); // map TrapContext memory_set.push( MapArea::new( @@ -230,6 +240,32 @@ impl MemorySet { pub fn translate(&self, vpn: VirtPageNum) -> Option { self.page_table.translate(vpn) } + #[allow(unused)] + pub fn shrink_to(&mut self, start: VirtAddr, new_end: VirtAddr) -> bool { + if let Some(area) = self + .areas + .iter_mut() + .find(|area| area.vpn_range.get_start() == start.floor()) + { + area.shrink_to(&mut self.page_table, new_end.ceil()); + true + } else { + false + } + } + #[allow(unused)] + pub fn append_to(&mut self, start: VirtAddr, new_end: VirtAddr) -> bool { + if let Some(area) = self + .areas + .iter_mut() + .find(|area| area.vpn_range.get_start() == start.floor()) + { + area.append_to(&mut self.page_table, new_end.ceil()); + true + } else { + false + } + } } /// map area structure, controls a contiguous piece of virtual memory @@ -289,6 +325,20 @@ impl MapArea { self.unmap_one(page_table, vpn); } } + #[allow(unused)] + pub fn shrink_to(&mut self, page_table: &mut PageTable, new_end: VirtPageNum) { + for vpn in VPNRange::new(new_end, self.vpn_range.get_end()) { + self.unmap_one(page_table, vpn) + } + self.vpn_range = VPNRange::new(self.vpn_range.get_start(), new_end); + } + #[allow(unused)] + pub fn append_to(&mut self, page_table: &mut PageTable, new_end: VirtPageNum) { + for vpn in VPNRange::new(self.vpn_range.get_end(), new_end) { + self.map_one(page_table, vpn) + } + self.vpn_range = VPNRange::new(self.vpn_range.get_start(), new_end); + } /// data: start-aligned but maybe with shorter length /// assume that all frames were cleared before pub fn copy_data(&mut self, page_table: &mut PageTable, data: &[u8]) { diff --git a/os/src/sbi.rs b/os/src/sbi.rs index cdee65b..269581f 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -44,7 +44,6 @@ pub fn console_putchar(c: usize) { // pub fn console_getchar() -> usize { // sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) // } - use crate::board::QEMUExit; /// use sbi call to shutdown the kernel pub fn shutdown() -> ! { diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index ee52f5b..e5b4ff0 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -14,6 +14,7 @@ const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; const SYSCALL_GET_TIME: usize = 169; +const SYSCALL_SBRK: usize = 214; mod fs; mod process; @@ -28,6 +29,7 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_YIELD => sys_yield(), SYSCALL_GET_TIME => sys_get_time(), + SYSCALL_SBRK => sys_sbrk(args[0] as i32), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index ec469bd..d302373 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,6 +1,6 @@ //! Process management syscalls -use crate::task::{exit_current_and_run_next, suspend_current_and_run_next}; +use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next}; use crate::timer::get_time_ms; /// task exits and submit an exit code @@ -20,3 +20,12 @@ pub fn sys_yield() -> isize { pub fn sys_get_time() -> isize { get_time_ms() as isize } + +/// change data segment size +pub fn sys_sbrk(size: i32) -> isize { + if let Some(old_brk) = change_program_brk(size) { + old_brk as isize + } else { + -1 + } +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index e96e857..dd27422 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -126,6 +126,13 @@ impl TaskManager { inner.tasks[inner.current_task].get_trap_cx() } + /// Change the current 'Running' task's program break + pub fn change_current_program_brk(&self, size: i32) -> Option { + let mut inner = self.inner.exclusive_access(); + let cur = inner.current_task; + inner.tasks[cur].change_program_brk(size) + } + /// Switch current `Running` task to the task we have found, /// or there is no `Ready` task and we can exit with all applications completed fn run_next_task(&self) { @@ -192,3 +199,8 @@ pub fn current_user_token() -> usize { pub fn current_trap_cx() -> &'static mut TrapContext { TASK_MANAGER.get_current_trap_cx() } + +/// Change the current 'Running' task's program break +pub fn change_program_brk(size: i32) -> Option { + TASK_MANAGER.change_current_program_brk(size) +} diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 6588ad8..6c87ddd 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -11,6 +11,8 @@ pub struct TaskControlBlock { pub memory_set: MemorySet, pub trap_cx_ppn: PhysPageNum, pub base_size: usize, + pub heap_bottom: usize, + pub program_brk: usize, } impl TaskControlBlock { @@ -41,6 +43,8 @@ impl TaskControlBlock { memory_set, trap_cx_ppn, base_size: user_sp, + heap_bottom: user_sp, + program_brk: user_sp, }; // prepare TrapContext in user space let trap_cx = task_control_block.get_trap_cx(); @@ -53,6 +57,27 @@ impl TaskControlBlock { ); task_control_block } + /// change the location of the program break. return None if failed. + pub fn change_program_brk(&mut self, size: i32) -> Option { + let old_break = self.program_brk; + let new_brk = self.program_brk as isize + size as isize; + if new_brk < self.heap_bottom as isize { + return None; + } + let result = if size < 0 { + self.memory_set + .shrink_to(VirtAddr(self.heap_bottom), VirtAddr(new_brk as usize)) + } else { + self.memory_set + .append_to(VirtAddr(self.heap_bottom), VirtAddr(new_brk as usize)) + }; + if result { + self.program_brk = new_brk as usize; + Some(old_break) + } else { + None + } + } } #[derive(Copy, Clone, PartialEq)] diff --git a/user/src/bin/sbrk_test.rs b/user/src/bin/sbrk_test.rs new file mode 100644 index 0000000..af21bd1 --- /dev/null +++ b/user/src/bin/sbrk_test.rs @@ -0,0 +1,46 @@ +#![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; + } + 0 +} diff --git a/user/src/lib.rs b/user/src/lib.rs index 2e99ba4..bd59ab7 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -34,3 +34,7 @@ pub fn yield_() -> isize { pub fn get_time() -> isize { sys_get_time() } + +pub fn sbrk(size: i32) -> isize { + sys_sbrk(size) +} diff --git a/user/src/syscall.rs b/user/src/syscall.rs index b009569..73f936b 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -4,6 +4,7 @@ const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; const SYSCALL_GET_TIME: usize = 169; +const SYSCALL_SBRK: usize = 214; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -34,3 +35,7 @@ pub fn sys_yield() -> isize { pub fn sys_get_time() -> isize { syscall(SYSCALL_GET_TIME, [0, 0, 0]) } + +pub fn sys_sbrk(size: i32) -> isize { + syscall(SYSCALL_SBRK, [size as usize, 0, 0]) +} \ No newline at end of file