diff --git a/kernel/src/arch/aarch64/exception.rs b/kernel/src/arch/aarch64/exception.rs index 21085dc..5be036d 100644 --- a/kernel/src/arch/aarch64/exception.rs +++ b/kernel/src/arch/aarch64/exception.rs @@ -88,8 +88,9 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) { match err_code { EC_DATA_ABORT_EL0 | EC_DATA_ABORT_ELX => { let far = FAR_EL1.get() as usize; + let iss = esr & 0x1FFFFFF; - if far < mem::KERNEL_OFFSET && sched::is_ready() { + if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET && sched::is_ready() { let thread = Thread::current(); let proc = thread.owner().unwrap(); diff --git a/kernel/src/dev/tty.rs b/kernel/src/dev/tty.rs index 43f94bc..4ab8da4 100644 --- a/kernel/src/dev/tty.rs +++ b/kernel/src/dev/tty.rs @@ -8,7 +8,7 @@ use libsys::{ ioctl::IoctlCmd }; use core::mem::size_of; -use crate::syscall::arg::validate_user_ptr_struct; +use crate::syscall::arg; #[derive(Debug)] struct CharRingInner { @@ -45,12 +45,12 @@ pub trait TtyDevice: SerialDevice { match cmd { IoctlCmd::TtyGetAttributes => { // TODO validate size - let res = validate_user_ptr_struct::(ptr)?; + let res = arg::struct_mut::(ptr)?; *res = self.ring().config.lock().clone(); Ok(size_of::()) }, IoctlCmd::TtySetAttributes => { - let src = validate_user_ptr_struct::(ptr)?; + let src = arg::struct_ref::(ptr)?; *self.ring().config.lock() = src.clone(); Ok(size_of::()) }, diff --git a/kernel/src/mem/virt/table.rs b/kernel/src/mem/virt/table.rs index e49d5db..12985a0 100644 --- a/kernel/src/mem/virt/table.rs +++ b/kernel/src/mem/virt/table.rs @@ -271,12 +271,17 @@ impl Space { todo!(); // res.map(virt_addr, dst_phys, flags)?; } else { - // TODO only apply CoW to writable pages - flags |= MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW; - l2_table[l2i].set_cow(); - unsafe { - asm!("tlbi vaae1, {}", in(reg) virt_addr); + let writable = flags & MapAttributes::AP_BOTH_READONLY == MapAttributes::AP_BOTH_READWRITE; + + if writable { + flags |= MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW; + l2_table[l2i].set_cow(); + + unsafe { + asm!("tlbi vaae1, {}", in(reg) virt_addr); + } } + res.map(virt_addr, dst_phys, flags)?; } } diff --git a/kernel/src/syscall/arg.rs b/kernel/src/syscall/arg.rs index caedd58..952a9e5 100644 --- a/kernel/src/syscall/arg.rs +++ b/kernel/src/syscall/arg.rs @@ -1,6 +1,7 @@ //! System call argument ABI helpers use crate::mem; +use core::alloc::Layout; use core::mem::size_of; use libsys::error::Errno; @@ -22,34 +23,62 @@ macro_rules! invalid_memory { } } -fn translate(virt: usize) -> Option { +#[inline(always)] +fn is_el0_accessible(virt: usize, write: bool) -> bool { let mut res: usize; unsafe { - asm!("at s1e1r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res); - } - if res & 1 == 0 { - Some(res & !(0xFFF | (0xFF << 56))) - } else { - None + if write { + asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res); + } else { + asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res); + } } + res & 1 == 0 } -/// Unwraps a slim structure pointer -pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> { - validate_user_ptr_struct_option(base).and_then(|e| e.ok_or(Errno::InvalidArgument)) +pub fn struct_ref<'a, T>(base: usize) -> Result<&'a T, Errno> { + let layout = Layout::new::(); + if base % layout.align() != 0 { + invalid_memory!( + "Structure pointer is misaligned: base={:#x}, expected {:?}", + base, + layout + ); + } + let bytes = buf_ref(base, layout.size())?; + Ok(unsafe { &*(bytes.as_ptr() as *const T) }) } -pub fn validate_user_ptr_struct_option<'a, T>(base: usize) -> Result, Errno> { +pub fn struct_mut<'a, T>(base: usize) -> Result<&'a mut T, Errno> { + let layout = Layout::new::(); + if base % layout.align() != 0 { + invalid_memory!( + "Structure pointer is misaligned: base={:#x}, expected {:?}", + base, + layout + ); + } + let bytes = buf_mut(base, layout.size())?; + Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }) +} + +pub fn option_struct_ref<'a, T>(base: usize) -> Result, Errno> { if base == 0 { Ok(None) } else { - let bytes = validate_user_ptr(base, size_of::())?; - Ok(Some(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })) + struct_ref(base).map(Some) } } -/// Unwraps an user buffer reference -pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> { +pub fn option_struct_mut<'a, T>(base: usize) -> Result, Errno> { + if base == 0 { + Ok(None) + } else { + struct_mut(base).map(Some) + } +} + +fn validate_ptr(base: usize, len: usize, writable: bool) -> Result<(), Errno> { if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET { invalid_memory!( "User region refers to kernel memory: base={:#x}, len={:#x}", @@ -59,9 +88,9 @@ pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Er } for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) { - if translate(i * mem::PAGE_SIZE).is_none() { + if !is_el0_accessible(i * mem::PAGE_SIZE, writable) { invalid_memory!( - "User region refers to unmapped memory: base={:#x}, len={:#x} (page {:#x})", + "User region refers to inaccessible/unmapped memory: base={:#x}, len={:#x} (page {:#x})", base, len, i * mem::PAGE_SIZE @@ -69,21 +98,38 @@ pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Er } } + Ok(()) +} + +pub fn buf_ref<'a>(base: usize, len: usize) -> Result<&'a [u8], Errno> { + validate_ptr(base, len, false)?; + Ok(unsafe { core::slice::from_raw_parts(base as *const u8, len) }) +} + +pub fn buf_mut<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> { + validate_ptr(base, len, true)?; Ok(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, len) }) } -/// Unwraps a nullable user buffer reference -pub fn validate_user_ptr_null<'a>(base: usize, len: usize) -> Result, Errno> { +pub fn option_buf_ref<'a>(base: usize, len: usize) -> Result, Errno> { if base == 0 { Ok(None) } else { - validate_user_ptr(base, len).map(Some) + buf_ref(base, len).map(Some) + } +} + +pub fn option_buf_mut<'a>(base: usize, len: usize) -> Result, Errno> { + if base == 0 { + Ok(None) + } else { + buf_mut(base, len).map(Some) } } /// Unwraps user string argument -pub fn validate_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Errno> { - let bytes = validate_user_ptr(base, len)?; +pub fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Errno> { + let bytes = buf_ref(base, len)?; core::str::from_utf8(bytes).map_err(|_| { warnln!( "User string contains invalid UTF-8 characters: base={:#x}, len={:#x}", diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index fe4b483..520547e 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -19,7 +19,6 @@ use libsys::{ use vfs::VnodeRef; pub mod arg; -pub use arg::*; /// Creates a "fork" process from current one using its register frame. /// See [Process::fork()]. @@ -59,7 +58,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { let proc = Process::current(); let fd = FileDescriptor::from(args[0] as u32); let mut io = proc.io.lock(); - let buf = validate_user_ptr(args[1], args[2])?; + let buf = arg::buf_mut(args[1], args[2])?; io.file(fd)?.borrow_mut().read(buf) }, @@ -67,13 +66,13 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { let proc = Process::current(); let fd = FileDescriptor::from(args[0] as u32); let mut io = proc.io.lock(); - let buf = validate_user_ptr(args[1], args[2])?; + let buf = arg::buf_ref(args[1], args[2])?; io.file(fd)?.borrow_mut().write(buf) }, SystemCall::Open => { let at_fd = FileDescriptor::from_i32(args[0] as i32)?; - let path = validate_user_str(args[1], args[2])?; + let path = arg::str_ref(args[1], args[2])?; let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?; let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?; @@ -99,8 +98,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { }, SystemCall::FileStatus => { let at_fd = FileDescriptor::from_i32(args[0] as i32)?; - let filename = validate_user_str(args[1], args[2])?; - let buf = validate_user_ptr_struct::(args[3])?; + let filename = arg::str_ref(args[1], args[2])?; + let buf = arg::struct_mut::(args[3])?; let flags = args[4] as u32; let proc = Process::current(); @@ -119,8 +118,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { node.ioctl(cmd, args[2], args[3]) }, SystemCall::Select => { - let rfds = validate_user_ptr_struct_option::(args[0])?; - let wfds = validate_user_ptr_struct_option::(args[1])?; + let rfds = arg::option_struct_mut::(args[0])?; + let wfds = arg::option_struct_mut::(args[1])?; let timeout = if args[2] == 0 { None } else { @@ -131,7 +130,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { }, SystemCall::Access => { let at_fd = FileDescriptor::from_i32(args[0] as i32)?; - let path = validate_user_str(args[1], args[2])?; + let path = arg::str_ref(args[1], args[2])?; let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?; let flags = args[4] as u32; @@ -156,14 +155,14 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { let node = { let proc = Process::current(); let mut io = proc.io.lock(); - let filename = validate_user_str(args[0], args[1])?; + let filename = arg::str_ref(args[0], args[1])?; // TODO argv, envp array passing ABI? let node = io.ioctx().find(None, filename, true)?; drop(io); node }; let file = node.open(OpenFlags::O_RDONLY)?; - Process::execve(|space| elf::load_elf(space, file), 0).unwrap(); + Process::execve(move |space| elf::load_elf(space, file), 0).unwrap(); panic!(); }, SystemCall::Exit => { @@ -181,7 +180,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { SystemCall::WaitPid => { // TODO special "pid" values let pid = unsafe { Pid::from_raw(args[0] as u32) }; - let status = validate_user_ptr_struct::(args[1])?; + let status = arg::struct_mut::(args[1])?; match Process::waitpid(pid) { Ok(exit) => { @@ -204,7 +203,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { SystemCall::GetPid => todo!(), SystemCall::GetTid => Ok(Thread::current().id() as usize), SystemCall::Sleep => { - let rem_buf = validate_user_ptr_null(args[1], size_of::() * 2)?; + let rem_buf = arg::option_buf_ref(args[1], size_of::() * 2)?; let mut rem = Duration::new(0, 0); let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem); if res == Err(Errno::Interrupt) { @@ -249,11 +248,9 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { // Debugging SystemCall::DebugTrace => { - let buf = validate_user_ptr(args[0], args[1])?; + let buf = arg::str_ref(args[0], args[1])?; print!(Level::Debug, "[trace] "); - for &byte in buf.iter() { - print!(Level::Debug, "{}", byte as char); - } + print!(Level::Debug, "{}", buf); println!(Level::Debug, ""); Ok(args[1]) }, diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index f0a32e7..64ea822 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -41,16 +41,6 @@ fn main() -> i32 { let mut buf = [0; 256]; let mut stdin = io::stdin(); - let delay = libusr::thread::spawn(|| { - let mut t = [0; 2]; - libusr::sys::sys_ex_nanosleep(1_000_000_000, &mut t); - }); - - delay.join(); - - libusr::signal::set_handler(libusr::sys::Signal::Interrupt, libusr::signal::SignalHandler::Ignore); - libusr::sys::sys_ex_kill(libusr::sys::SignalDestination::This, libusr::sys::Signal::Interrupt); - loop { print!("> "); let line = readline(&mut stdin, &mut buf).unwrap();