diff --git a/os/Cargo.toml b/os/Cargo.toml index e6090767a..d9adeaa57 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -11,5 +11,6 @@ bitflags = "1.2.1" buddy_system_allocator = "0.6" lazy_static = { version = "1.4.0", features = ["spin_no_std"] } log = "0.4" -riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } +riscv = { git = "https://gitee.com/rcore-os/riscv", features = ["inline-asm"] } +virtio-drivers = { git = "https://gitee.com/rcore-os/virtio-drivers", rev = "4ee80e5" } xmas-elf = "0.7.0" diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 7a7b7eae9..132e72dc0 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -63,6 +63,29 @@ impl MemorySet { None, ); } + + /// Unmap a area + pub fn remove_framed_area( + &mut self, + start_va: VirtAddr, + end_va: VirtAddr, + ) -> isize { + let start_vpn: VirtPageNum = start_va.floor(); + let end_vpn: VirtPageNum = end_va.ceil(); + let vpns = VPNRange::new(start_vpn, end_vpn); + for vpn in vpns { + if let Some(pte) = self.page_table.translate(vpn) { + if !pte.is_valid() { + return -1; + } + self.page_table.unmap(vpn); + } else { + return -1; + } + } + 0 + } + fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { map_area.map(&mut self.page_table); if let Some(data) = data { @@ -386,6 +409,8 @@ pub fn kernel_stack_position(app_id: usize) -> (usize, usize) { (bottom, top) } + + /// remap test in kernel space #[allow(unused)] pub fn remap_test() { diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 8563fbd60..882625486 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -13,7 +13,7 @@ mod memory_set; mod page_table; pub use address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; -use address::{StepByOne, VPNRange}; +pub use address::{StepByOne, VPNRange}; pub use frame_allocator::{frame_alloc, FrameTracker}; pub use memory_set::remap_test; pub use memory_set::{kernel_stack_position, MapPermission, MemorySet, KERNEL_SPACE}; @@ -26,3 +26,72 @@ pub fn init() { frame_allocator::init_frame_allocator(); KERNEL_SPACE.exclusive_access().activate(); } + +/// +pub fn copy_to_user(user_token: usize, user_ptr: *mut u8, kernel_src: *const u8, len: usize) -> Result<(), ()> { + let mut buffers = translated_byte_buffer(user_token, user_ptr, len); + let mut offset = 0; + + for buf in buffers.iter_mut() { + let copy_len = core::cmp::min(buf.len(), len - offset); + if len == 0 || copy_len == 0 { + return Err(()); + } + unsafe { + core::ptr::copy_nonoverlapping(kernel_src.add(offset), buf.as_mut_ptr(), copy_len); + } + offset += copy_len; + } + + if offset == len { + Ok(()) + } else { + Err(()) + } +} + +/// +pub fn read_virtaddr(token: usize, virtaddr: usize) -> Result { + let pgtb = PageTable::from_token(token); + let vpn = VirtAddr::from(virtaddr).floor(); + // 获取pte + if let Some(pte) = pgtb.translate(vpn) { + if !pte.is_valid() || !pte.readable() { + //页表项无效或不可读 + Err(()) + } else { + let buf = translated_byte_buffer(token, virtaddr as *const u8, 1); + if buf.is_empty() { + Err(()) + } else { + let val = buf[0][0]; + Ok(val as isize) + } + } + } else { + Err(()) + } +} + +/// +pub fn write_virtaddr(token: usize, virtaddr: usize, data: u8) -> Result<(), ()> { + let pgtb = PageTable::from_token(token); + let vpn = VirtAddr::from(virtaddr).floor(); + // 获取pte + if let Some(pte) = pgtb.translate(vpn) { + if !pte.is_valid() || !pte.writable() { + //页表项无效或不可写 + Err(()) + } else { + let mut buf = translated_byte_buffer(token, virtaddr as *const u8, 1); + if buf.is_empty() { + Err(()) + } else { + buf[0][0] = data; + Ok(()) + } + } + } else { + Err(()) + } +} \ No newline at end of file diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index afa9d293e..44e4c8af8 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -179,3 +179,4 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& } v } + diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 91d7ba329..b6fa69c3c 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -9,6 +9,10 @@ //! For clarity, each single syscall is implemented as its own function, named //! `sys_` then the name of the syscall. You can find functions like this in //! submodules, and you should also implement syscalls this way. + +use crate::task::syscall_num_inc; + +/// write syscall const SYSCALL_WRITE: usize = 64; /// exit syscall const SYSCALL_EXIT: usize = 93; @@ -33,6 +37,7 @@ use process::*; /// handle syscall exception with `syscall_id` and other arguments pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { + syscall_num_inc(syscall_id); match syscall_id { SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_EXIT => sys_exit(args[0] as i32), diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index ec4285bc5..7b1f24f20 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,5 +1,12 @@ //! Process management syscalls -use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next}; + +use crate::config::MEMORY_END; +use crate::task::{get_syscall_times, change_program_brk, exit_current_and_run_next, + suspend_current_and_run_next, current_user_token, map_new_area, + munmap_used_area}; +use crate::timer::get_time_us; +use crate::mm::{copy_to_user, read_virtaddr, write_virtaddr, MapPermission}; +use crate::mm::{VPNRange, VirtAddr, PageTable}; #[repr(C)] #[derive(Debug)] @@ -25,28 +32,118 @@ pub fn sys_yield() -> isize { /// YOUR JOB: get time with second and microsecond /// HINT: You might reimplement it with virtual memory management. /// HINT: What if [`TimeVal`] is splitted by two pages ? -pub fn sys_get_time(_ts: *mut TimeVal, _tz: usize) -> isize { +pub fn sys_get_time(ts: *mut TimeVal, _tz: usize) -> isize { trace!("kernel: sys_get_time"); - -1 + let us = get_time_us(); + let time_val = TimeVal { + sec: us / 1_000_000, + usec: us % 1_000_000, + }; + let user_token = current_user_token(); // 获取当前任务的虚拟内存上下文 + let kernel_src = &time_val as *const TimeVal as *const u8; + let len = core::mem::size_of::(); + + // 使用封装函数复制数据到用户态 + if copy_to_user(user_token, ts as *mut u8, kernel_src, len).is_ok() { + 0 // 返回成功 + } else { + -1 // 返回失败 + } } /// TODO: Finish sys_trace to pass testcases /// HINT: You might reimplement it with virtual memory management. -pub fn sys_trace(_trace_request: usize, _id: usize, _data: usize) -> isize { +pub fn sys_trace(trace_request: usize, id: usize, data: usize) -> isize { trace!("kernel: sys_trace"); - -1 + if id > MEMORY_END { + return -1; + } + match trace_request { + // _trace_request = 0, 获取当前任务id地址处一个字节的无符号整数值 + 0 => { + let addr = id as usize; + let cur_token = current_user_token(); + match read_virtaddr(cur_token, addr) { + Ok(val) => return val, + Err(()) => return -1 as isize, + }; + }, + // _trace_request = 1, 将data写入到id对应地址处 + 1 => { + let addr = id as usize; + let cur_token = current_user_token(); + match write_virtaddr(cur_token, addr, data as u8) { + Ok(()) => 0 as isize, + Err(()) => -1 as isize, + } + }, + // _trace_request = 2, 查询编号为id的系统调用的调用次数 + 2 => { + get_syscall_times(id) as isize + }, + _ => -1, + } } // YOUR JOB: Implement mmap. -pub fn sys_mmap(_start: usize, _len: usize, _port: usize) -> isize { +pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { trace!("kernel: sys_mmap NOT IMPLEMENTED YET!"); - -1 + let start_va = VirtAddr(start); + if !start_va.aligned() { + return -1; + } + if len <= 0 { + return -1; + } + if (prot & !0x7 != 0) || (prot & 0x7 == 0) { + return -1; + } + // len按页向上取整 + let start_va = VirtAddr(start).floor(); + let end_va = VirtAddr(start + len).ceil(); + let pgtb = PageTable::from_token(current_user_token()); + + // 检查虚拟地址是否已经被映射 + let vpns = VPNRange::new(start_va, end_va); + for vpn in vpns { + if let Some(pte) = pgtb.translate(vpn) { + if pte.is_valid() { + return -1; + } + } + }; + + // 构造permission: MapPermission + let mut perm: MapPermission = MapPermission::U; + if prot & 0x1 != 0 { + perm |= MapPermission::R; + } + if prot & 0x2 != 0 { + perm |= MapPermission::W; + } + if prot & 0x4 != 0 { + perm |= MapPermission::X; + } + // 完成虚拟地址到物理地址的映射 + map_new_area(start_va.into(), end_va.into(), perm); + 0 } // YOUR JOB: Implement munmap. -pub fn sys_munmap(_start: usize, _len: usize) -> isize { +pub fn sys_munmap(start: usize, len: usize) -> isize { trace!("kernel: sys_munmap NOT IMPLEMENTED YET!"); - -1 + // 处理参数,获取需要取消映射的区间 + let start_va = VirtAddr(start); + if !start_va.aligned() { + return -1; + } + if len <= 0 { + return -1; + } + let start_vpn = start_va.floor(); + let end_vpn = VirtAddr(start + len).ceil(); + // munmap_used_area的实现参考了荣誉准则标注的内容 + munmap_used_area(start_vpn.into(), end_vpn.into()) } /// change data segment size pub fn sys_sbrk(size: i32) -> isize { diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index a745df864..6c6641131 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -14,6 +14,7 @@ mod switch; #[allow(clippy::module_inception)] mod task; +use crate::config::MAX_SYSCALL_NUM; use crate::loader::{get_app_data, get_num_app}; use crate::sync::UPSafeCell; use crate::trap::TrapContext; @@ -21,6 +22,7 @@ use alloc::vec::Vec; use lazy_static::*; use switch::__switch; pub use task::{TaskControlBlock, TaskStatus}; +use crate::mm::{VirtAddr, MapPermission}; pub use context::TaskContext; @@ -153,6 +155,46 @@ impl TaskManager { panic!("All applications completed!"); } } + + // syscall_num_inc + fn syscall_num_inc(&self, id: usize) { + let mut inner = self.inner.exclusive_access(); + let current = inner.current_task; + if id <= MAX_SYSCALL_NUM { + inner.tasks[current].task_syscall_num[id] += 1; + } + } + + fn get_syscall_times(&self, id: usize) -> u8{ + let inner = self.inner.exclusive_access(); + let current = inner.current_task; + inner.tasks[current].task_syscall_num[id] + } + + fn map_new_area(&self, start_va: VirtAddr, + end_va: VirtAddr, + permission: MapPermission) -> () { + let mut inner = self.inner.exclusive_access(); + let cur = inner.current_task; + inner.tasks[cur].map_virtual_area(start_va, end_va, permission); + } + + fn munmap_used_area(&self, start_va: VirtAddr, end_va: VirtAddr) -> isize { + let mut inner = self.inner.exclusive_access(); + let cur = inner.current_task; + let res = inner.tasks[cur].memory_set.remove_framed_area(start_va, end_va); + res + } +} + +/// increase num_syscall[id] +pub fn syscall_num_inc(id: usize) { + TASK_MANAGER.syscall_num_inc(id); +} + +/// Get the number of times the syscall with call number id is used. +pub fn get_syscall_times(id: usize) -> u8{ + TASK_MANAGER.get_syscall_times(id) } /// Run the first task in task list. @@ -193,6 +235,18 @@ pub fn current_user_token() -> usize { TASK_MANAGER.get_current_token() } +/// Map a new virtual area +pub fn map_new_area(start_va: VirtAddr, + end_va: VirtAddr, + permission: MapPermission) -> () { + TASK_MANAGER.map_new_area(start_va, end_va, permission); +} + +/// +pub fn munmap_used_area(start_va: VirtAddr, end_va: VirtAddr) -> isize{ + TASK_MANAGER.munmap_used_area(start_va, end_va) +} + /// Get the current 'Running' task's trap contexts. pub fn current_trap_cx() -> &'static mut TrapContext { TASK_MANAGER.get_current_trap_cx() diff --git a/os/src/task/task.rs b/os/src/task/task.rs index dce698194..faabb728d 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,8 +1,8 @@ //! Types related to task management use super::TaskContext; -use crate::config::TRAP_CONTEXT_BASE; +use crate::config::{TRAP_CONTEXT_BASE, MAX_SYSCALL_NUM}; use crate::mm::{ - kernel_stack_position, MapPermission, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE, + kernel_stack_position, MapPermission, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE }; use crate::trap::{trap_handler, TrapContext}; @@ -28,6 +28,9 @@ pub struct TaskControlBlock { /// Program break pub program_brk: usize, + + /// The called times of syscall + pub task_syscall_num: [u8; MAX_SYSCALL_NUM], } impl TaskControlBlock { @@ -63,6 +66,7 @@ impl TaskControlBlock { base_size: user_sp, heap_bottom: user_sp, program_brk: user_sp, + task_syscall_num: [0; MAX_SYSCALL_NUM], }; // prepare TrapContext in user space let trap_cx = task_control_block.get_trap_cx(); @@ -96,6 +100,18 @@ impl TaskControlBlock { None } } + /// map a area of virtual address + pub fn map_virtual_area(&mut self, start_va: VirtAddr, + end_va: VirtAddr, + permission: MapPermission + ) -> () { + self.memory_set.insert_framed_area(start_va, end_va, permission); + } + + /// unmap a area of virtual address + pub fn munmap_virtual_area(&mut self, start_va: VirtAddr, end_va: VirtAddr) -> isize { + self.memory_set.remove_framed_area(start_va, end_va) + } } #[derive(Copy, Clone, PartialEq)] diff --git a/reports/lab1.md b/reports/lab1.md new file mode 100644 index 000000000..e57b9184a --- /dev/null +++ b/reports/lab1.md @@ -0,0 +1,68 @@ +# Lab1总结 + +## 1. 实现的功能 + +根据编程任务引入了一个新的系统调用 `sys_trace` ,该系统调用的功能与任务要求一致,主要实现过程如下: +- 对于 `trace_request` 为 0 和 1 的情况 + - 分别调用 `read_volatile` 和 `write_volatile` 来读取或修改对应地址上的值; +- 对于 `trace_request` 为 2 的情况 + - 我们在TCB中引入了一个新的成员 `task_syscall_num` 数组,用于记录对应 id 的系统调用被调用的次数; + - 为 `TaskManager` 封装了两个新的函数,用于增加和获取对应 `id` 系统调用的使用次数; + - 在 `syscall` 中我们调用该函数,让目标值 `+1`; + - 最后在 `trace_request == 2` 的分支调用 `get_syscall_times` 返回我们需要的值 + +## 2. 简答作业 + +### 2.1 + +RustSBI version:0.3.0-alpha.2,adapting to RISC-V SBI v1.0.0 + +**内核报错信息** +```shell +[kernel] PageFault in application, bad addr = 0x0, bad instruction = 0x804003a4, kernel killed it. +[kernel] IllegalInstruction in application, kernel killed it. +[kernel] IllegalInstruction in application, kernel killed it. +``` +- `ch2b_bad_address` : 访问到错误地址,触发页错误; +- `ch2b_bad_instructions` : 在U态使用了S态指令,由于权限不够,触发非法指令异常; +- `ch2b_bad_register` : 同样地,在用户态也不能访问 `sstatus` 寄存器,触发非法指令异常; + +### 2.2 +#### 2.2.1 + +1. 刚进入 `__restore` 时,`sp` 指向了内核栈的栈顶,栈上包含了 `TrapContext` +2. `__restore` 的两种使用场景:**初始化用户程序上下文** 和 **处理陷阱后恢复用户程序的上下文,并跳转执行** + +#### 2.2.2 +这几行代码将栈上的值恢复到了寄存器中,涉及的寄存器如下: +- `sstatus` : 保存了当前特权级别,实现用户态和监督模式的相互转换; +- `sepc` : 保存了进入 Trap 前的 pc 寄存器值,异常返回时能够回到之前的用户程序执行地址; +- `sscratch` : 通常保存了内核栈地址,与sp交换后指向用户栈; + +#### 2.2.3 +`x2`(用户栈指针被保存到 `sscratch`, 无需再次保存) 和 `x4` (线程相关寄存器,未使用,不保存) + +#### 2.2.4 +该指令后,`sp` 重新指向用户栈,`sscratch` 重新指向内核栈 + +#### 2.2.5 +状态切换发生在最后一行 `sret` ,该指令包含的操作:按照当前特权级将 `sstatus` 的 `SPP` 字段设置为U或者S,跳转到 `sepc`寄存器指向的指令,继续执行 + +#### 2.2.6 +该指令后,`sp` 指向内核栈,`sscratch` 指向用户栈 + +#### 2.2.7 +`ecall` + +## 3. 荣誉准则 +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + +无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + +无 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 \ No newline at end of file diff --git a/reports/lab2.md b/reports/lab2.md new file mode 100644 index 000000000..760c35a24 --- /dev/null +++ b/reports/lab2.md @@ -0,0 +1,43 @@ +# Lab2总结 + +## 2. 实现的功能 + +本章实验修改与实现了几个系统调用,修改`sys_get_time`和`sys_trace`的重点是利于框架中提供的`translated_byte_buffer`函数,完成用户态与内核态的数据交换。此外,要注意的是如何从用户地址转换到`pagetable`。 +`sys_mmap`和`sys_munmap`的实现,重点是根据输入地址获取到`vpn, pte`然后调用框架提供的代码完成 + +## 2. 简答作业 + +### 2.1 +- SV39的PTE由[53: 10]这44位物理页号和[7: 0]这8位标志位组成; +- V代表页表项是否合法; +- R/W/X分别代表页表项对应页面是否允许读/写/执行; +- U代表该页表项是否允许在用户态进行访问; +- A表示该位清零后,该页表项对应页面是否被访问过; +- D表示该位被清零后,该页表项对应页面是否被修改过; + +### 2.2 +1. 当我们访问页面时,对应页表项无效或者访问权限不够会导致缺页异常 +2. `satp`, `stvec`, `sepc`, `sscause`, `stval` +3. lazy的好处是节省资源,提高内存利用率,简化了内存管理 +4. 21MB,大约5120个三级页数,10个二级页数,1个一级页数 +5. 在mmap时只修改页表项的权限位,不进行映射,当触发缺页中断时,在中断处理函数中完成映射 +6. V位会被清零 + +### 2.3 +1. 修改`satp`寄存器可以更换页表 +2. PTE的标志位不要设置U +3. 使用单页表可以避免切换页表带来的消耗,管理起来更加简单 +4. 当发生异常时,需要从用户态陷入到内核态,对于双页表实现,此时需要更换页表;我会选择在切换进程时更换页表 + +## 3. 荣誉准则 +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + +> 主要是人工智能 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + +(参考资料)[https://hangx-ma.github.io/2023/07/04/rcore-note-ch4.html] + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 \ No newline at end of file