From 7f1e36816c03514b83fa4b62f068d76f098d1c86 Mon Sep 17 00:00:00 2001 From: ChenRuiwei <1982833213@qq.com> Date: Sun, 7 Apr 2024 21:34:56 +0800 Subject: [PATCH] feat(user_ptr): implement user_ptr --- Cargo.lock | 1 + Makefile | 174 ----------- arch/src/riscv64/interrupts.rs | 5 + config/src/mm.rs | 1 + docs/roadmap.md | 3 + kernel/Cargo.toml | 1 + kernel/src/main.rs | 3 + kernel/src/mm/memory_space/mod.rs | 10 + kernel/src/mm/memory_space/vm_area.rs | 4 +- kernel/src/mm/mod.rs | 5 +- kernel/src/mm/user_ptr.rs | 417 ++++++++++++++++++++++++++ kernel/src/trap/kernel_trap.rs | 54 +++- kernel/src/trap/trap.asm | 36 +++ modules/memory/src/address.rs | 326 -------------------- modules/memory/src/address/mod.rs | 75 +++++ modules/memory/src/address/offset.rs | 67 +++++ modules/memory/src/address/phys.rs | 121 ++++++++ modules/memory/src/address/step.rs | 81 +++++ modules/memory/src/address/virt.rs | 133 ++++++++ modules/memory/src/lib.rs | 1 + modules/systype/src/lib.rs | 10 +- 21 files changed, 1012 insertions(+), 516 deletions(-) delete mode 100644 Makefile create mode 100644 docs/roadmap.md create mode 100644 kernel/src/mm/user_ptr.rs delete mode 100644 modules/memory/src/address.rs create mode 100644 modules/memory/src/address/mod.rs create mode 100644 modules/memory/src/address/offset.rs create mode 100644 modules/memory/src/address/phys.rs create mode 100644 modules/memory/src/address/step.rs create mode 100644 modules/memory/src/address/virt.rs diff --git a/Cargo.lock b/Cargo.lock index 361e8933..f98a1ebf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,7 @@ dependencies = [ "bit_field", "bitflags 2.5.0", "buddy_system_allocator", + "cfg-if", "config", "driver", "executor", diff --git a/Makefile b/Makefile deleted file mode 100644 index 26b45125..00000000 --- a/Makefile +++ /dev/null @@ -1,174 +0,0 @@ -# Building variables -DOCKER_NAME = titanix -BOARD := qemu -export TARGET = riscv64gc-unknown-none-elf -export MODE = release -export LOG = error - - -# Tools -OBJDUMP = rust-objdump --arch-name=riscv64 -OBJCOPY = rust-objcopy --binary-architecture=riscv64 -QEMU = qemu-system-riscv64 -RISCV_GDB ?= riscv64-unknown-elf-gdb -PAGER ?= less - - -# Target files -TARGET_DIR := ./target/$(TARGET)/$(MODE) -VENDOR_DIR := ./third-party/vendor - -KERNEL_ELF := $(TARGET_DIR)/kernel -KERNEL_BIN := $(KERNEL_ELF).bin -KERNEL_ASM := $(KERNEL_ELF).asm - -USER_APPS_DIR := ./user/src/bin -USER_APPS := $(wildcard $(USER_APPS_DIR)/*.rs) -USER_ELFS := $(patsubst $(USER_APPS_DIR)/%.rs, $(TARGET_DIR)/%, $(USER_APPS)) -USER_BINS := $(patsubst $(USER_APPS_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(USER_APPS)) - -FS_IMG_DIR := ./fs-img -FS_IMG := $(FS_IMG_DIR)/sdcard.img -TEST := 23 -ifeq ($(TEST), rootfs) - TEST_DIR := ./Titanix-rootfs/rootfs -else - TEST_DIR := ./testcase/$(TEST) -endif - - -# Crate features -export STRACE := -export SUBMIT := -export TMPFS := -export SMP := -export PRELIMINARY := - - -# Args -DISASM_ARGS = -d - -BOOTLOADER := default -CPUS := 2 -QEMU_ARGS := -ifeq ($(SUBMIT), ) - QEMU_ARGS += -m 512M -else - QEMU_ARGS += -m 128M -endif -QEMU_ARGS += -machine virt -QEMU_ARGS += -nographic -QEMU_ARGS += -smp $(CPUS) -QEMU_ARGS += -kernel $(KERNEL_BIN) -QEMU_ARGS += -bios $(BOOTLOADER) -QEMU_ARGS += -drive file=$(FS_IMG),if=none,format=raw,id=x0 -QEMU_ARGS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 - -DOCKER_RUN_ARGS := run -DOCKER_RUN_ARGS += --rm -DOCKER_RUN_ARGS += -it -DOCKER_RUN_ARGS += --privileged -DOCKER_RUN_ARGS += --network="host" -DOCKER_RUN_ARGS += -v $(PWD):/mnt -DOCKER_RUN_ARGS += -v /dev:/dev -DOCKER_RUN_ARGS += -w /mnt -DOCKER_RUN_ARGS += $(DOCKER_NAME) -DOCKER_RUN_ARGS += bash - - -# File targets -$(KERNEL_ASM): $(KERNEL_ELF) - @$(OBJDUMP) $(DISASM_ARGS) $(KERNEL_ELF) > $(KERNEL_ASM) - @echo "Updated: $(KERNEL_ASM)" - - -# Phony targets -PHONY := all -all: build run - -PHONY += build_docker -build_docker: - docker build --network="host" -t ${DOCKER_NAME} . - -PHONY += docker -docker: - docker $(DOCKER_RUN_ARGS) - -PHONY += env -env: - @(cargo install --list | grep "cargo-binutils" > /dev/null 2>&1) || cargo install cargo-binutils - @cargo vendor $(VENDOR_DIR) - -PHONY += fmt -fmt: - @cargo fmt - -PHONY += build -build: fmt user fs-img kernel - -PHONY += kernel -kernel: - @echo "building kernel..." - @echo Platform: $(BOARD) - @cd kernel && make build - @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $(KERNEL_BIN) - @echo "building kernel finished" - -PHONY += user -user: - @echo "building user..." - @cd user && make build - @$(foreach elf, $(USER_ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) - @cp ./testcase/22/busybox $(TARGET_DIR)/busybox - @echo "building user finished" - -PHONY += fs-img -fs-img: - @echo "building fs-img..." - @rm -rf $(FS_IMG) - @mkdir -p $(FS_IMG_DIR) - @dd if=/dev/zero of=$(FS_IMG) count=1363148 bs=1K - @mkfs.vfat -F 32 $(FS_IMG) - @echo "making fatfs image by using $(TEST_DIR)" - @mkdir -p mnt - @mount -t vfat -o user,umask=000,utf8=1 --source $(FS_IMG) --target mnt - @cp -r $(TEST_DIR)/* mnt - @umount mnt - @rm -rf mnt - @chmod -R 777 $(FS_IMG_DIR) - @echo "building fs-img finished" - -PHONY += qemu -qemu: - @echo "start to run kernel in qemu..." - $(QEMU) $(QEMU_ARGS) - -PHONY += run -run: qemu - -PHONY += brun -brun: kernel run - -PHONY += clean -clean: - @cargo clean - @rm -rf $(FS_IMG) - -PHONY += disasm -disasm: $(KERNEL_ASM) - @$(PAGER) $(KERNEL_ASM) - -PHONY += trace -trace: - addr2line -fipe $(KERNEL_ELF) | rustfilt - -PHONY += drun -drun: - $(QEMU) $(QEMU_ARGS) -s -S - -PHONY += gdb -gdb: - $(RISCV_GDB) -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234' - -.PHONY: $(PHONY) - diff --git a/arch/src/riscv64/interrupts.rs b/arch/src/riscv64/interrupts.rs index 00468ce7..2f7950b5 100644 --- a/arch/src/riscv64/interrupts.rs +++ b/arch/src/riscv64/interrupts.rs @@ -45,6 +45,11 @@ pub fn set_trap_handler(handler_addr: usize) { } } +#[inline] +pub fn set_trap_handler_vector(handler_addr: usize) { + unsafe { stvec::write(handler_addr, TrapMode::Vectored) } +} + /// Disable interrupt and resume to the interrupt state before when it gets /// dropped pub struct InterruptGuard { diff --git a/config/src/mm.rs b/config/src/mm.rs index b734c3cb..51801d34 100644 --- a/config/src/mm.rs +++ b/config/src/mm.rs @@ -17,6 +17,7 @@ pub const HART_START_ADDR: usize = 0x80200000; pub const USER_STACK_SIZE: usize = 1024 * 1024 * 8; // 8M pub const PAGE_SIZE: usize = 1 << PAGE_SIZE_BITS; +pub const PAGE_MASK: usize = PAGE_SIZE - 1; pub const PAGE_SIZE_BITS: usize = 12; pub const PTE_NUM_ONE_PAGE: usize = 512; diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 00000000..2aac9b4a --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,3 @@ +# Roadmap + +- Userptr diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 72a20be4..b4c6f6a5 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -17,6 +17,7 @@ async_utils = { path = "../modules/async_utils/" } irq_count = { path = "../modules/irq_count/" } recycle_allocator = { path = "../modules/recycle_allocator/" } +cfg-if = "1.0" buddy_system_allocator = "0.9" bitflags = "2.5" bit_field = "0.10" diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 589c65f6..83f00c35 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -25,6 +25,9 @@ extern crate alloc; #[macro_use] extern crate bitflags; +#[macro_use] +extern crate cfg_if; + #[macro_use] extern crate driver; diff --git a/kernel/src/mm/memory_space/mod.rs b/kernel/src/mm/memory_space/mod.rs index 47bbcd4d..0b44f5bb 100644 --- a/kernel/src/mm/memory_space/mod.rs +++ b/kernel/src/mm/memory_space/mod.rs @@ -8,9 +8,11 @@ use log::info; use memory::{PageTable, VirtAddr, VirtPageNum}; use spin::Lazy; use sync::mutex::SpinNoIrqLock; +use systype::SysResult; use xmas_elf::ElfFile; use self::vm_area::VmArea; +use super::user_ptr::PageFaultAccessType; use crate::{ mm::{ memory_space::vm_area::{MapPermission, VmAreaType}, @@ -331,6 +333,14 @@ impl MemorySpace { (memory_space, user_stack_top, entry_point, auxv) } + pub fn handle_pagefault( + &mut self, + vaddr: VirtAddr, + access_type: PageFaultAccessType, + ) -> SysResult<()> { + todo!(); + } + pub fn activate(&self) { self.page_table.activate(); } diff --git a/kernel/src/mm/memory_space/vm_area.rs b/kernel/src/mm/memory_space/vm_area.rs index bc8daed2..49ae2715 100644 --- a/kernel/src/mm/memory_space/vm_area.rs +++ b/kernel/src/mm/memory_space/vm_area.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{borrow::ToOwned, vec::Vec}; use config::mm::PAGE_SIZE; use memory::{pte::PTEFlags, StepByOne, VPNRange, VirtAddr, VirtPageNum}; @@ -117,7 +117,7 @@ impl VmArea { for vpn in self.vpn_range { page_table.map( vpn, - VirtAddr::from(vpn).to_pa().into(), + VirtAddr::from(vpn).to_offset().to_pa().into(), self.map_perm.into(), ) } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index a807dece..b9a7ecc1 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -9,6 +9,7 @@ /// pub mod memory_space; mod page; +mod user_ptr; use config::board::MEMORY_END; use log::info; @@ -27,8 +28,8 @@ pub fn init() { } heap::init_heap_allocator(); frame::init_frame_allocator( - VirtAddr::from(_ekernel as usize).to_pa().into(), - VirtAddr::from(MEMORY_END).to_pa().into(), + VirtAddr::from(_ekernel as usize).to_offset().to_pa().into(), + VirtAddr::from(MEMORY_END).to_offset().to_pa().into(), ); info!("KERNEL SPACE init finished"); mm::activate_kernel_space(); diff --git a/kernel/src/mm/user_ptr.rs b/kernel/src/mm/user_ptr.rs new file mode 100644 index 00000000..81ee5ecf --- /dev/null +++ b/kernel/src/mm/user_ptr.rs @@ -0,0 +1,417 @@ +//! UserPtr +//! 这个模块用来绕过裸指针的异步 Send 检查 +//! +//! Adapted from FTL OS + +use alloc::{string::String, sync::Arc, vec::Vec}; +use core::{ + fmt::{Display, Formatter}, + intrinsics::size_of, + marker::PhantomData, + ops::ControlFlow, +}; + +use memory::{StepByOne, VirtAddr}; +use riscv::register::scause; +use systype::{SysError, SysResult}; + +use crate::{ + task::Task, + trap::{ + kernel_trap::{set_kernel_user_rw_trap, will_read_fail, will_write_fail}, + set_kernel_trap_entry, + }, +}; + +pub trait Policy: Clone + Copy + 'static {} + +pub trait Read: Policy {} +pub trait Write: Policy {} + +#[derive(Clone, Copy)] +pub struct In; +#[derive(Clone, Copy)] +pub struct Out; +#[derive(Clone, Copy)] +pub struct InOut; + +impl Policy for In {} +impl Policy for Out {} +impl Policy for InOut {} +impl Read for In {} +impl Write for Out {} +impl Read for InOut {} +impl Write for InOut {} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct UserPtr { + ptr: *mut T, + _mark: PhantomData

, +} + +pub type UserReadPtr = UserPtr; +pub type UserWritePtr = UserPtr; +pub type UserInOutPtr = UserPtr; + +unsafe impl Send for UserPtr {} +unsafe impl Sync for UserPtr {} + +impl UserPtr { + fn new(ptr: *mut T) -> Self { + Self { + ptr, + _mark: PhantomData, + } + } + + pub fn null() -> Self { + Self::new(core::ptr::null_mut()) + } + pub fn from_usize(a: usize) -> Self { + Self::new(a as *mut T) + } + pub fn is_null(self) -> bool { + self.ptr.is_null() + } + pub fn not_null(self) -> bool { + !self.ptr.is_null() + } + pub fn as_usize(self) -> usize { + self.ptr as usize + } + pub fn raw_ptr(self) -> *const T { + self.ptr + } + /// return None if UserAddr == nullptr + pub fn as_ptr(self) -> Option<*const T> { + if self.ptr.is_null() { + return None; + } + Some(self.ptr) + } + pub fn offset(self, count: isize) -> Self { + Self::new(unsafe { self.ptr.offset(count) }) + } + pub fn transmute(self) -> UserPtr { + UserPtr::new(self.ptr as *mut V) + } + pub fn add(self, count: usize) -> Self { + Self::new(unsafe { self.ptr.add(count) }) + } +} +impl UserPtr { + pub fn nonnull(self) -> Option { + self.not_null().then_some(self) + } + + #[must_use] + pub fn as_ref(self, task: &Arc) -> SysResult<&T> { + debug_assert!(self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::(), + PageFaultAccessType::RO, + )?; + let res = unsafe { &*self.ptr }; + Ok(res) + } + + #[must_use] + pub fn as_slice(self, n: usize, task: &Arc) -> SysResult<&[T]> { + debug_assert!(n == 0 || self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::() * n, + PageFaultAccessType::RO, + )?; + let res = unsafe { core::slice::from_raw_parts(self.ptr, n) }; + Ok(res) + } + + #[must_use] + pub fn read(self, task: &Arc) -> SysResult { + debug_assert!(self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::(), + PageFaultAccessType::RO, + )?; + let res = unsafe { core::ptr::read(self.ptr) }; + Ok(res) + } + + #[must_use] + pub fn read_array(self, n: usize, task: &Arc) -> SysResult> { + debug_assert!(n == 0 || self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::() * n, + PageFaultAccessType::RO, + )?; + + let mut res = Vec::with_capacity(n); + unsafe { + let ptr = self.ptr; + for i in 0..n { + res.push(ptr.add(i).read()); + } + } + + Ok(res) + } +} + +impl UserPtr { + #[must_use] + pub fn read_cstr(self, task: &Arc) -> SysResult { + debug_assert!(self.not_null()); + let mut str = String::with_capacity(32); + let mut has_ended = false; + + task.ensure_user_area( + VirtAddr::from(self.as_usize()), + usize::MAX, + PageFaultAccessType::RO, + |beg, len| unsafe { + let mut ptr = beg.as_mut_ptr(); + for _ in 0..len { + let c = ptr.read(); + if c == 0 { + has_ended = true; + return ControlFlow::Break(None); + } + str.push(c as char); + ptr = ptr.offset(1); + } + ControlFlow::Continue(()) + }, + )?; + + if has_ended { + Ok(str) + } else { + Err(SysError::EINVAL) + } + } +} + +impl UserPtr { + pub fn raw_ptr_mut(self) -> *mut T { + self.ptr + } + pub fn nonnull_mut(self) -> Option { + self.not_null().then_some(self) + } + + #[must_use] + pub fn as_mut(self, task: &Arc) -> SysResult<&mut T> { + debug_assert!(self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::(), + PageFaultAccessType::RW, + )?; + let res = unsafe { &mut *self.ptr }; + Ok(res) + } + + #[must_use] + pub fn as_mut_slice(self, n: usize, task: &Arc) -> SysResult<&mut [T]> { + debug_assert!(n == 0 || self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::() * n, + PageFaultAccessType::RW, + )?; + let res = unsafe { core::slice::from_raw_parts_mut(self.ptr, n) }; + Ok(res) + } + + #[must_use] + pub fn write(self, task: &Arc, val: T) -> SysResult<()> { + debug_assert!(self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::(), + PageFaultAccessType::RW, + )?; + unsafe { core::ptr::write(self.ptr, val) }; + Ok(()) + } + + #[must_use] + pub fn write_array(self, task: &Arc, val: &[T]) -> SysResult<()> { + debug_assert!(self.not_null()); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + size_of::() * val.len(), + PageFaultAccessType::RW, + )?; + unsafe { + let mut ptr = self.ptr; + for &v in val { + ptr.write(v); + ptr = ptr.offset(1); + } + } + Ok(()) + } +} + +impl UserPtr { + #[must_use] + /// should only be used at syscall getdent with dynamic-len structure + pub unsafe fn write_as_bytes(self, task: &Arc, val: &U) -> SysResult<()> { + debug_assert!(self.not_null()); + + let len = size_of::(); + task.just_ensure_user_area( + VirtAddr::from(self.as_usize()), + len, + PageFaultAccessType::RW, + )?; + + unsafe { + let view = core::slice::from_raw_parts(val as *const U as *const u8, len); + let mut ptr = self.ptr as *mut u8; + for &c in view { + ptr.write(c); + ptr = ptr.offset(1); + } + } + Ok(()) + } + + #[must_use] + pub fn write_cstr(self, task: &Arc, val: &str) -> SysResult<()> { + debug_assert!(self.not_null()); + + let mut str = val.as_bytes(); + let mut has_filled_zero = false; + + task.ensure_user_area( + VirtAddr::from(self.as_usize()), + val.len() + 1, + PageFaultAccessType::RW, + |beg, len| unsafe { + let mut ptr = beg.as_mut_ptr(); + let writable_len = len.min(str.len()); + for _ in 0..writable_len { + let c = str[0]; + str = &str[1..]; + ptr.write(c); + ptr = ptr.offset(1); + } + if str.is_empty() && writable_len < len { + ptr.write(0); + has_filled_zero = true; + } + ControlFlow::Continue(()) + }, + )?; + + if has_filled_zero { + Ok(()) + } else { + Err(SysError::EINVAL) + } + } +} + +impl From for UserPtr { + fn from(a: usize) -> Self { + Self::from_usize(a) + } +} + +impl Display for UserPtr { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "UserPtr({:#x})", self.as_usize()) + } +} + +impl Task { + #[inline(always)] + fn just_ensure_user_area( + &self, + begin: VirtAddr, + len: usize, + access: PageFaultAccessType, + ) -> SysResult<()> { + self.ensure_user_area(begin, len, access, |_, _| ControlFlow::Continue(())) + } + + /// ensure that the whole range is accessible, or return an error + #[inline(always)] + fn ensure_user_area( + &self, + begin: VirtAddr, + len: usize, + access: PageFaultAccessType, + mut f: impl FnMut(VirtAddr, usize) -> ControlFlow>, + ) -> SysResult<()> { + if len == 0 { + return Ok(()); + } + + set_kernel_user_rw_trap(); + + let test_fn = match access { + PageFaultAccessType::RO => will_read_fail, + PageFaultAccessType::RW => will_write_fail, + _ => panic!("invalid access type"), + }; + + let mut curr_vaddr = begin; + let mut readable_len = 0; + while readable_len < len { + if test_fn(curr_vaddr.0) { + // self.with_mut_memory(|m| m.handle_pagefault(curr_vaddr, + // access)) .map_err(|_| SysError::EFAULT)?; + } + + let next_page_beg: VirtAddr = VirtAddr::from(curr_vaddr.floor().next()); + let len = next_page_beg - curr_vaddr; + + match f(curr_vaddr, len) { + ControlFlow::Continue(_) => {} + ControlFlow::Break(None) => return Ok(()), + ControlFlow::Break(Some(e)) => return Err(e), + } + + readable_len += len; + curr_vaddr = next_page_beg; + } + + set_kernel_trap_entry(); + Ok(()) + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + pub struct PageFaultAccessType: u8 { + const READ = 1 << 1; + const WRITE = 1 << 2; + const EXECUTE = 1 << 3; + } +} + +impl PageFaultAccessType { + // no write & no execute == read only + pub const RO: Self = Self::READ; + // can't use | (bits or) here + // see https://github.com/bitflags/bitflags/issues/180 + pub const RW: Self = Self::RO.union(Self::WRITE); + pub const RX: Self = Self::RO.union(Self::EXECUTE); + + pub fn from_exception(e: scause::Exception) -> Self { + match e { + scause::Exception::InstructionPageFault => Self::RX, + scause::Exception::LoadPageFault => Self::RO, + scause::Exception::StorePageFault => Self::RW, + _ => panic!("unexcepted exception type for PageFaultAccessType"), + } + } +} diff --git a/kernel/src/trap/kernel_trap.rs b/kernel/src/trap/kernel_trap.rs index da915a70..8f4ba8d7 100644 --- a/kernel/src/trap/kernel_trap.rs +++ b/kernel/src/trap/kernel_trap.rs @@ -1,9 +1,12 @@ +use arch::interrupts::set_trap_handler_vector; use irq_count::IRQ_COUNTER; use riscv::register::{ scause::{self, Interrupt, Trap}, - sepc, stval, + sepc, stval, stvec, }; +use crate::{processor::hart::local_hart, when_debug}; + /// Kernel trap handler #[no_mangle] pub fn kernel_trap_handler() { @@ -19,18 +22,55 @@ pub fn kernel_trap_handler() { todo!() } _ => { - log::error!( + panic!( "[kernel] {:?}(scause:{}) in application, bad addr = {:#x}, bad instruction = {:#x}, kernel panicked!!", scause::read().cause(), scause::read().bits(), stval::read(), sepc::read(), ); - panic!( - "a trap {:?} from kernel! stval {:#x}", - scause::read().cause(), - stval::read() - ); } } } + +extern "C" { + fn __user_rw_trap_vector(); +} + +#[inline(always)] +pub fn set_kernel_user_rw_trap() { + let trap_vaddr = __user_rw_trap_vector as usize; + set_trap_handler_vector(trap_vaddr); + log::trace!( + "Switch to User-RW checking mode for hart {} at STVEC: 0x{:x}", + local_hart().hart_id(), + trap_vaddr + ); +} + +#[inline(always)] +pub fn will_read_fail(vaddr: usize) -> bool { + when_debug!({ + let curr_stvec = stvec::read().address(); + debug_assert!(curr_stvec == __user_rw_trap_vector as usize); + }); + + extern "C" { + fn __try_read_user(vaddr: usize) -> bool; + } + + unsafe { __try_read_user(vaddr) } +} + +#[inline(always)] +pub fn will_write_fail(vaddr: usize) -> bool { + when_debug!({ + let curr_stvec = stvec::read().address(); + debug_assert!(curr_stvec == __user_rw_trap_vector as usize); + }); + + extern "C" { + fn __try_write_user(vaddr: usize) -> bool; + } + unsafe { __try_write_user(vaddr) } +} diff --git a/kernel/src/trap/trap.asm b/kernel/src/trap/trap.asm index ce795569..7f481d0a 100644 --- a/kernel/src/trap/trap.asm +++ b/kernel/src/trap/trap.asm @@ -141,3 +141,39 @@ __trap_from_kernel: addi sp, sp, 17*8 sret + +__try_read_user: + mv a1, a0 + # 先将 a0 设置为 0 + mv a0, zero + # 尝试读取用户空间的内存 + lb a1, 0(a1) + # 如果上条指令出现了缺页异常, 那么就会跳转到 __user_check_exception_entry + # 而后者会将 a0 设置为 1 并将 sepc + 4 + # 于是在发生缺页异常时, a0 为 1, 否则为 0 + ret + +# 检查写入同理 +__try_write_user: + mv a2, a0 + mv a0, zero + sb a1, 0(a2) + ret + +__user_rw_exception_entry: + csrr a0, sepc + addi a0, a0, 4 + csrw sepc, a0 + li a0, 1 + csrr a1, scause + sret + + .align 8 +__user_rw_trap_vector: + j __user_rw_exception_entry + .rept 16 + .align 2 + j __trap_from_kernel + .endr + unimp + diff --git a/modules/memory/src/address.rs b/modules/memory/src/address.rs deleted file mode 100644 index 3a0e5756..00000000 --- a/modules/memory/src/address.rs +++ /dev/null @@ -1,326 +0,0 @@ -//! Implementation of physical and virtual address and page number. -use core::{ - fmt::{self, Debug, Formatter}, - mem::size_of, -}; - -use config::mm::{ - PAGE_SIZE, PAGE_SIZE_BITS, PAGE_TABLE_LEVEL_NUM, PTE_NUM_ONE_PAGE, VIRT_RAM_OFFSET, -}; - -use crate::PageTableEntry; - -const PA_WIDTH_SV39: usize = 56; -const VA_WIDTH_SV39: usize = 39; -const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; -const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; - -/// Physical address -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -pub struct PhysAddr(pub usize); - -/// Virtual address -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -pub struct VirtAddr(pub usize); - -/// Physical page number -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -pub struct PhysPageNum(pub usize); - -/// Virtual page number -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -pub struct VirtPageNum(pub usize); - -impl Debug for VirtAddr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("VA:{:#x}", self.0)) - } -} -impl Debug for VirtPageNum { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("VPN:{:#x}", self.0)) - } -} -impl Debug for PhysAddr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("PA:{:#x}", self.0)) - } -} -impl Debug for PhysPageNum { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("PPN:{:#x}", self.0)) - } -} - -impl From for PhysAddr { - fn from(v: usize) -> Self { - let tmp = v as isize >> PA_WIDTH_SV39; - assert!(tmp == 0 || tmp == -1); - Self(v) - } -} -impl From for PhysPageNum { - fn from(v: usize) -> Self { - let tmp = v as isize >> PPN_WIDTH_SV39; - assert!(tmp == 0 || tmp == -1); - Self(v) - } -} -impl From for VirtAddr { - fn from(v: usize) -> Self { - let tmp = v as isize >> VA_WIDTH_SV39; - assert!(tmp == 0 || tmp == -1, "invalid va: {:#x}", v); - Self(v) - } -} -impl From for VirtPageNum { - fn from(v: usize) -> Self { - let tmp = v >> (VPN_WIDTH_SV39 - 1); - assert!(tmp == 0 || tmp == (1 << (52 - VPN_WIDTH_SV39 + 1)) - 1); - Self(v) - } -} -impl From for usize { - fn from(v: PhysAddr) -> Self { - v.0 - } -} -impl From for usize { - fn from(v: PhysPageNum) -> Self { - v.0 - } -} -impl From for usize { - fn from(v: VirtAddr) -> Self { - if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) { - v.0 | (!((1 << VA_WIDTH_SV39) - 1)) - } else { - v.0 - } - } -} -impl From for usize { - fn from(v: VirtPageNum) -> Self { - v.0 - } -} - -impl VirtAddr { - /// `VirtAddr`->`VirtPageNum` - pub fn floor(&self) -> VirtPageNum { - VirtPageNum(self.0 / PAGE_SIZE) - } - /// `VirtAddr`->`VirtPageNum` - pub fn ceil(&self) -> VirtPageNum { - log::info!("ceil: self.0 = {:x}", self.0); - VirtPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) - } - /// Get page offset - pub fn page_offset(&self) -> usize { - self.0 & (PAGE_SIZE - 1) - } - /// Check page aligned - pub fn aligned(&self) -> bool { - self.page_offset() == 0 - } - pub fn to_pa(&self) -> PhysAddr { - debug_assert!(self.0 >= VIRT_RAM_OFFSET); - (self.0 - VIRT_RAM_OFFSET).into() - } -} -impl From for VirtPageNum { - fn from(v: VirtAddr) -> Self { - assert_eq!(v.page_offset(), 0); - v.floor() - } -} -impl From for VirtAddr { - fn from(v: VirtPageNum) -> Self { - Self(v.0 << PAGE_SIZE_BITS) - } -} -impl PhysAddr { - /// `PhysAddr`->`PhysPageNum` - pub fn floor(&self) -> PhysPageNum { - PhysPageNum(self.0 / PAGE_SIZE) - } - /// `PhysAddr`->`PhysPageNum` - pub fn ceil(&self) -> PhysPageNum { - PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) - } - /// Get page offset - pub fn page_offset(&self) -> usize { - self.0 & (PAGE_SIZE - 1) - } - /// Check page aligned - pub fn aligned(&self) -> bool { - self.page_offset() == 0 - } - #[inline] - pub fn to_va(&self) -> VirtAddr { - (self.0 + VIRT_RAM_OFFSET).into() - } - /// Get mutable reference to `VirtAddr` value - pub fn reinterpret(&self) -> &'static T { - let va: VirtAddr = self.to_va(); - unsafe { (va.0 as *mut T).as_ref().unwrap() } - } - /// Get mutable reference to `VirtAddr` value - pub fn reinterpret_mut(&self) -> &'static mut T { - let va: VirtAddr = self.to_va(); - unsafe { (va.0 as *mut T).as_mut().unwrap() } - } -} -impl From for PhysPageNum { - fn from(v: PhysAddr) -> Self { - assert_eq!(v.page_offset(), 0); - v.floor() - } -} -impl From for PhysAddr { - fn from(v: PhysPageNum) -> Self { - Self(v.0 << PAGE_SIZE_BITS) - } -} - -impl PhysPageNum { - /// Get `PageTableEntry` on `VirtPageNum` - pub fn pte_array(&self) -> &'static mut [PageTableEntry] { - let va: VirtAddr = PhysAddr::from(*self).to_va(); - unsafe { core::slice::from_raw_parts_mut(va.0 as *mut PageTableEntry, PTE_NUM_ONE_PAGE) } - } - /// Get bytes array of a physical page - pub fn bytes_array(&self) -> &'static mut [u8] { - let va: VirtAddr = PhysAddr::from(*self).to_va(); - unsafe { core::slice::from_raw_parts_mut(va.0 as *mut u8, PAGE_SIZE) } - } - /// Get usize array of a physical page - pub fn usize_array(&self) -> &'static mut [usize] { - let va: VirtAddr = PhysAddr::from(*self).to_va(); - unsafe { - core::slice::from_raw_parts_mut(va.0 as *mut usize, PAGE_SIZE / size_of::()) - } - } - /// - pub fn reinterpret(&self) -> &'static T { - PhysAddr::from(*self).reinterpret() - } - /// - pub fn reinterpret_mut(&self) -> &'static mut T { - PhysAddr::from(*self).reinterpret_mut() - } -} - -impl VirtPageNum { - /// Return VPN 3 level indices - pub fn indices(&self) -> [usize; PAGE_TABLE_LEVEL_NUM] { - let mut vpn = self.0; - let mut indices = [0usize; PAGE_TABLE_LEVEL_NUM]; - for i in (0..PAGE_TABLE_LEVEL_NUM).rev() { - indices[i] = vpn & 511; - vpn >>= 9; - } - indices - } -} - -/// step the give type -pub trait StepByOne { - /// Step the give type - fn step(&mut self); -} - -impl StepByOne for VirtAddr { - fn step(&mut self) { - self.0 += 1; - } -} - -impl StepByOne for VirtPageNum { - fn step(&mut self) { - self.0 += 1; - } -} - -impl StepByOne for PhysPageNum { - fn step(&mut self) { - self.0 += 1; - } -} - -/// a simple range structure for type T -#[derive(Copy, Clone, Debug)] -pub struct SimpleRange -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - l: T, - r: T, -} - -impl SimpleRange -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - pub fn new(start: T, end: T) -> Self { - assert!(start <= end, "start {:?} > end {:?}!", start, end); - Self { l: start, r: end } - } - pub fn start(&self) -> T { - self.l - } - pub fn end(&self) -> T { - self.r - } - /// Note that the new right bound cannot be smaller than left bound - pub fn modify_right_bound(&mut self, new_right: T) { - assert!(new_right >= self.l); - self.r = new_right; - } -} - -impl IntoIterator for SimpleRange -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - type Item = T; - type IntoIter = SimpleRangeIterator; - fn into_iter(self) -> Self::IntoIter { - SimpleRangeIterator::new(self.l, self.r) - } -} - -/// iterator for the simple range structure -pub struct SimpleRangeIterator -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - current: T, - end: T, -} -impl SimpleRangeIterator -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - pub fn new(l: T, r: T) -> Self { - Self { current: l, end: r } - } -} -impl Iterator for SimpleRangeIterator -where - T: StepByOne + Copy + PartialEq + PartialOrd + Debug, -{ - type Item = T; - fn next(&mut self) -> Option { - if self.current == self.end { - None - } else { - let t = self.current; - self.current.step(); - Some(t) - } - } -} - -/// a simple range structure for virtual page number -pub type VPNRange = SimpleRange; diff --git a/modules/memory/src/address/mod.rs b/modules/memory/src/address/mod.rs new file mode 100644 index 00000000..354bad49 --- /dev/null +++ b/modules/memory/src/address/mod.rs @@ -0,0 +1,75 @@ +mod offset; +mod phys; +mod step; +mod virt; + +const PA_WIDTH_SV39: usize = 56; +const VA_WIDTH_SV39: usize = 39; +const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; +const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; + +use config::mm::PAGE_SIZE_BITS; +pub use phys::{PhysAddr, PhysPageNum}; +pub use step::StepByOne; +pub use virt::{VPNRange, VirtAddr, VirtPageNum}; + +macro_rules! impl_arithmetic_with_usize { + ($t:ty) => { + impl const core::ops::Add for $t { + type Output = Self; + #[inline] + fn add(self, rhs: usize) -> Self { + Self(self.0 + rhs) + } + } + impl const core::ops::AddAssign for $t { + #[inline] + fn add_assign(&mut self, rhs: usize) { + *self = *self + rhs; + } + } + impl const core::ops::Sub for $t { + type Output = Self; + #[inline] + fn sub(self, rhs: usize) -> Self { + Self(self.0 - rhs) + } + } + impl const core::ops::SubAssign for $t { + #[inline] + fn sub_assign(&mut self, rhs: usize) { + *self = *self - rhs; + } + } + impl const core::ops::Sub<$t> for $t { + type Output = usize; + #[inline] + fn sub(self, rhs: $t) -> usize { + self.0 - rhs.0 + } + } + }; +} + +macro_rules! impl_fmt { + ($t:ty, $prefix:expr) => { + impl fmt::Debug for $t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("{}:{:#x}", $prefix, self.0)) + } + } + impl fmt::LowerHex for $t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("{}:{:#x}", $prefix, self.0)) + } + } + impl fmt::UpperHex for $t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("{}:{:#X}", $prefix, self.0)) + } + } + }; +} + +pub(self) use impl_arithmetic_with_usize; +pub(self) use impl_fmt; diff --git a/modules/memory/src/address/offset.rs b/modules/memory/src/address/offset.rs new file mode 100644 index 00000000..79d518ff --- /dev/null +++ b/modules/memory/src/address/offset.rs @@ -0,0 +1,67 @@ +use config::mm::{PAGE_SIZE, VIRT_RAM_OFFSET}; + +use crate::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; + +/// Offset address +/// +/// It is only used for kernel, which maps an area with an offset. +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct OffsetAddr { + // stores pa in usize + pa_u: usize, +} + +/// Offset page number +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct OffsetPageNum { + ppn_u: usize, +} + +impl From for OffsetAddr { + fn from(pa: PhysAddr) -> Self { + Self { pa_u: pa.0 } + } +} + +impl From for OffsetAddr { + fn from(va: VirtAddr) -> Self { + assert!(va.0 >= VIRT_RAM_OFFSET); + Self { + pa_u: va.0 - VIRT_RAM_OFFSET, + } + } +} +impl From for OffsetPageNum { + fn from(ppn: PhysPageNum) -> Self { + Self { ppn_u: ppn.0 } + } +} + +impl From for OffsetPageNum { + fn from(vpn: VirtPageNum) -> Self { + assert!(vpn.0 >= VIRT_RAM_OFFSET / PAGE_SIZE); + Self { + ppn_u: vpn.0 - VIRT_RAM_OFFSET / PAGE_SIZE, + } + } +} + +impl OffsetAddr { + pub fn to_pa(&self) -> PhysAddr { + self.pa_u.into() + } + + pub fn to_va(&self) -> VirtAddr { + (self.pa_u + VIRT_RAM_OFFSET).into() + } +} + +impl OffsetPageNum { + pub fn to_ppn(&self) -> PhysPageNum { + self.ppn_u.into() + } + + pub fn to_vpn(&self) -> VirtPageNum { + (self.ppn_u + VIRT_RAM_OFFSET / PAGE_SIZE).into() + } +} diff --git a/modules/memory/src/address/phys.rs b/modules/memory/src/address/phys.rs new file mode 100644 index 00000000..7b054708 --- /dev/null +++ b/modules/memory/src/address/phys.rs @@ -0,0 +1,121 @@ +//! Implementation of physical and virtual address and page number. +use core::{ + fmt::{self, Debug, Formatter}, + mem::size_of, +}; + +use config::mm::{ + PAGE_MASK, PAGE_SIZE, PAGE_SIZE_BITS, PAGE_TABLE_LEVEL_NUM, PTE_NUM_ONE_PAGE, VIRT_RAM_OFFSET, +}; + +use super::{ + impl_fmt, + offset::{OffsetAddr, OffsetPageNum}, + step::StepByOne, +}; +use crate::{ + address::{PA_WIDTH_SV39, PPN_WIDTH_SV39}, + PageTableEntry, VirtAddr, +}; + +/// Physical address +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysAddr(pub usize); + +/// Physical page number +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysPageNum(pub usize); + +impl From for PhysAddr { + fn from(u: usize) -> Self { + let tmp = u as isize >> PA_WIDTH_SV39; + assert!(tmp == 0 || tmp == -1); + Self(u) + } +} +impl From for PhysPageNum { + fn from(u: usize) -> Self { + let tmp = u as isize >> PPN_WIDTH_SV39; + assert!(tmp == 0 || tmp == -1); + Self(u) + } +} +impl From for usize { + fn from(pa: PhysAddr) -> Self { + pa.0 + } +} +impl From for usize { + fn from(ppn: PhysPageNum) -> Self { + ppn.0 + } +} + +impl PhysAddr { + /// `PhysAddr`->`PhysPageNum` + pub fn floor(&self) -> PhysPageNum { + PhysPageNum(self.0 / PAGE_SIZE) + } + /// `PhysAddr`->`PhysPageNum` + pub fn ceil(&self) -> PhysPageNum { + PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) + } + /// Get page offset + pub fn page_offset(&self) -> usize { + self.0 & PAGE_MASK + } + /// Check page aligned + pub fn aligned(&self) -> bool { + self.page_offset() == 0 + } + pub fn to_offset(&self) -> OffsetAddr { + (*self).into() + } +} + +impl From for PhysPageNum { + fn from(pa: PhysAddr) -> Self { + assert!(pa.aligned()); + pa.floor() + } +} +impl From for PhysAddr { + fn from(ppn: PhysPageNum) -> Self { + Self(ppn.0 << PAGE_SIZE_BITS) + } +} + +impl PhysPageNum { + pub fn to_pa(&self) -> PhysAddr { + (*self).into() + } + pub fn to_offset(&self) -> OffsetPageNum { + (*self).into() + } + /// Get `PageTableEntry` on `VirtPageNum` + pub fn pte_array(&self) -> &'static mut [PageTableEntry] { + let va: VirtAddr = self.to_offset().to_vpn().into(); + unsafe { core::slice::from_raw_parts_mut(va.0 as *mut PageTableEntry, PTE_NUM_ONE_PAGE) } + } + /// Get bytes array of a physical page + pub fn bytes_array(&self) -> &'static mut [u8] { + let va: VirtAddr = self.to_offset().to_vpn().into(); + unsafe { core::slice::from_raw_parts_mut(va.0 as *mut u8, PAGE_SIZE) } + } + /// Get usize array of a physical page + pub fn usize_array(&self) -> &'static mut [usize] { + let va: VirtAddr = self.to_offset().to_vpn().into(); + unsafe { + core::slice::from_raw_parts_mut(va.0 as *mut usize, PAGE_SIZE / size_of::()) + } + } +} + +impl StepByOne for PhysPageNum { + fn step(&mut self) { + self.0 += 1; + } +} + +impl_fmt!(PhysAddr, "PA"); +impl_fmt!(PhysPageNum, "PPN"); diff --git a/modules/memory/src/address/step.rs b/modules/memory/src/address/step.rs new file mode 100644 index 00000000..3b8c9420 --- /dev/null +++ b/modules/memory/src/address/step.rs @@ -0,0 +1,81 @@ +use core::fmt::Debug; + +/// step the give type +pub trait StepByOne { + /// Step the give type + fn step(&mut self); +} + +/// a simple range structure for type T +#[derive(Copy, Clone, Debug)] +pub struct SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + l: T, + r: T, +} + +impl SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + pub fn new(start: T, end: T) -> Self { + assert!(start <= end, "start {:?} > end {:?}!", start, end); + Self { l: start, r: end } + } + pub fn start(&self) -> T { + self.l + } + pub fn end(&self) -> T { + self.r + } + /// Note that the new right bound cannot be smaller than left bound + pub fn modify_right_bound(&mut self, new_right: T) { + assert!(new_right >= self.l); + self.r = new_right; + } +} + +impl IntoIterator for SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + type Item = T; + type IntoIter = SimpleRangeIterator; + fn into_iter(self) -> Self::IntoIter { + SimpleRangeIterator::new(self.l, self.r) + } +} + +/// iterator for the simple range structure +pub struct SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + current: T, + end: T, +} +impl SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + pub fn new(l: T, r: T) -> Self { + Self { current: l, end: r } + } +} +impl Iterator for SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + type Item = T; + fn next(&mut self) -> Option { + if self.current == self.end { + None + } else { + let t = self.current; + self.current.step(); + Some(t) + } + } +} diff --git a/modules/memory/src/address/virt.rs b/modules/memory/src/address/virt.rs new file mode 100644 index 00000000..e8ddf803 --- /dev/null +++ b/modules/memory/src/address/virt.rs @@ -0,0 +1,133 @@ +//! Implementation of physical and virtual address and page number. +use core::{ + fmt::{self, Debug, Formatter}, + mem::size_of, +}; + +use config::mm::{ + PAGE_MASK, PAGE_SIZE, PAGE_SIZE_BITS, PAGE_TABLE_LEVEL_NUM, PTE_NUM_ONE_PAGE, VIRT_RAM_OFFSET, +}; + +use super::{ + impl_arithmetic_with_usize, impl_fmt, + offset::OffsetAddr, + step::{SimpleRange, StepByOne}, +}; +use crate::address::{VA_WIDTH_SV39, VPN_WIDTH_SV39}; + +/// Virtual address +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtAddr(pub usize); + +/// Virtual page number +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtPageNum(pub usize); + +impl_fmt!(VirtAddr, "VA"); +impl_fmt!(VirtPageNum, "VPN"); +impl_arithmetic_with_usize!(VirtPageNum); +impl_arithmetic_with_usize!(VirtAddr); + +impl From for VirtAddr { + fn from(v: usize) -> Self { + let tmp = v as isize >> VA_WIDTH_SV39; + assert!(tmp == 0 || tmp == -1, "invalid va: {:#x}", v); + Self(v) + } +} +impl From for VirtPageNum { + fn from(v: usize) -> Self { + let tmp = v >> (VPN_WIDTH_SV39 - 1); + assert!(tmp == 0 || tmp == (1 << (52 - VPN_WIDTH_SV39 + 1)) - 1); + Self(v) + } +} + +impl From for usize { + fn from(v: VirtAddr) -> Self { + if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) { + v.0 | (!((1 << VA_WIDTH_SV39) - 1)) + } else { + v.0 + } + } +} + +impl From for usize { + fn from(v: VirtPageNum) -> Self { + v.0 + } +} + +impl VirtAddr { + pub fn to_offset(&self) -> OffsetAddr { + (*self).into() + } + /// `VirtAddr`->`VirtPageNum` + pub fn floor(&self) -> VirtPageNum { + VirtPageNum(self.0 / PAGE_SIZE) + } + /// `VirtAddr`->`VirtPageNum` + pub fn ceil(&self) -> VirtPageNum { + (self.floor() + 1).into() + } + /// Get page offset + pub fn page_offset(&self) -> usize { + self.0 & PAGE_MASK + } + /// Check page aligned + pub fn aligned(&self) -> bool { + self.page_offset() == 0 + } + pub const fn as_ptr(self) -> *const u8 { + self.0 as *const u8 + } + pub const fn as_mut_ptr(self) -> *mut u8 { + self.0 as *mut u8 + } +} +impl From for VirtPageNum { + fn from(v: VirtAddr) -> Self { + assert_eq!(v.page_offset(), 0); + v.floor() + } +} +impl From for VirtAddr { + fn from(v: VirtPageNum) -> Self { + Self(v.0 << PAGE_SIZE_BITS) + } +} + +impl VirtPageNum { + pub fn to_va(&self) -> VirtAddr { + (*self).into() + } + pub fn next(&self) -> Self { + *self + 1 + } + /// Return VPN 3 level indices + pub fn indices(&self) -> [usize; PAGE_TABLE_LEVEL_NUM] { + let mut vpn = self.0; + let mut indices = [0usize; PAGE_TABLE_LEVEL_NUM]; + for i in (0..PAGE_TABLE_LEVEL_NUM).rev() { + indices[i] = vpn & 511; + vpn >>= 9; + } + indices + } +} + +impl StepByOne for VirtAddr { + fn step(&mut self) { + self.0 += 1; + } +} + +impl StepByOne for VirtPageNum { + fn step(&mut self) { + self.0 += 1; + } +} + +/// a simple range structure for virtual page number +pub type VPNRange = SimpleRange; diff --git a/modules/memory/src/lib.rs b/modules/memory/src/lib.rs index 75a38e5b..40622552 100644 --- a/modules/memory/src/lib.rs +++ b/modules/memory/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(alloc_error_handler)] +#![feature(const_trait_impl)] extern crate alloc; diff --git a/modules/systype/src/lib.rs b/modules/systype/src/lib.rs index df9abc42..1d716bbf 100644 --- a/modules/systype/src/lib.rs +++ b/modules/systype/src/lib.rs @@ -5,8 +5,8 @@ extern crate alloc; use alloc::boxed::Box; use core::{future::Future, pin::Pin}; -pub type SyscallResult = Result; -pub type SysResult = Result; +pub type SyscallResult = Result; +pub type SysResult = Result; pub type SysFuture<'a, T> = Pin + Send + 'a>>; @@ -16,7 +16,7 @@ pub type ASysResult<'a, T> = SysFuture<'a, SysResult>; /// Linux specific error codes defined in `errno.h`. #[repr(i32)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum LinuxError { +pub enum SysError { /// Operation not permitted EPERM = 1, /// No such file or directory @@ -101,10 +101,10 @@ pub enum LinuxError { ECONNREFUSED = 111, } -impl LinuxError { +impl SysError { /// Returns the error description. pub const fn as_str(&self) -> &'static str { - use self::LinuxError::*; + use self::SysError::*; match self { EPERM => "Operation not permitted", ENOENT => "No such file or directory",