refactor: improve system call ABI
This commit is contained in:
+1
-3
@@ -15,9 +15,7 @@ fn main() -> i32 {
|
||||
loop {
|
||||
print!("> ");
|
||||
|
||||
let count = unsafe {
|
||||
libusr::sys::sys_read(0, buf.as_mut_ptr(), buf.len())
|
||||
};
|
||||
let count = unsafe { libusr::sys::sys_read(0, &mut buf) };
|
||||
if count < 0 {
|
||||
trace!("Read from stdio failed");
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//! System call argument ABI helpers
|
||||
|
||||
use crate::mem;
|
||||
use core::mem::size_of;
|
||||
use error::Errno;
|
||||
|
||||
fn translate(virt: usize) -> Option<usize> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps a slim structure pointer
|
||||
pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
|
||||
}
|
||||
|
||||
/// Unwraps an user buffer reference
|
||||
pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
|
||||
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
|
||||
warnln!(
|
||||
"User region refers to kernel memory: base={:#x}, len={:#x}",
|
||||
base,
|
||||
len
|
||||
);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) {
|
||||
if translate(i * mem::PAGE_SIZE).is_none() {
|
||||
warnln!(
|
||||
"User region refers to unmapped memory: base={:#x}, len={:#x} (page {:#x})",
|
||||
base,
|
||||
len,
|
||||
i * mem::PAGE_SIZE
|
||||
);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
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<Option<&'a mut [u8]>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
validate_user_ptr(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)?;
|
||||
core::str::from_utf8(bytes).map_err(|_| {
|
||||
warnln!("User string contains invalid UTF-8 characters: base={:#x}, len={:#x}", base, len);
|
||||
Errno::InvalidArgument
|
||||
})
|
||||
}
|
||||
// if base > mem::KERNEL_OFFSET {
|
||||
// warnln!("User string refers to kernel memory: base={:#x}", base);
|
||||
// return Err(Errno::InvalidArgument);
|
||||
// }
|
||||
//
|
||||
// let base_ptr = base as *const u8;
|
||||
// let mut len = 0;
|
||||
// let mut page_valid = false;
|
||||
// loop {
|
||||
// if len == limit {
|
||||
// warnln!("User string exceeded limit: base={:#x}", base);
|
||||
// return Err(Errno::InvalidArgument);
|
||||
// }
|
||||
//
|
||||
// if (base + len) % mem::PAGE_SIZE == 0 {
|
||||
// page_valid = false;
|
||||
// }
|
||||
//
|
||||
// if !page_valid && translate((base + len) & !0xFFF).is_none() {
|
||||
// warnln!(
|
||||
// "User string refers to unmapped memory: base={:#x}, off={:#x}",
|
||||
// base,
|
||||
// len
|
||||
// );
|
||||
// return Err(Errno::InvalidArgument);
|
||||
// }
|
||||
//
|
||||
// page_valid = true;
|
||||
//
|
||||
// let byte = unsafe { *base_ptr.add(len) };
|
||||
// if byte == 0 {
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// len += 1;
|
||||
// }
|
||||
//
|
||||
// let slice = unsafe { core::slice::from_raw_parts(base_ptr, len) };
|
||||
// core::str::from_utf8(slice).map_err(|_| {
|
||||
// warnln!(
|
||||
// "User string contains invalid UTF-8 characters: base={:#x}",
|
||||
// base
|
||||
// );
|
||||
// Errno::InvalidArgument
|
||||
// })
|
||||
// }
|
||||
@@ -1,113 +1,17 @@
|
||||
//! System call implementation
|
||||
|
||||
use crate::arch::platform::exception::ExceptionFrame;
|
||||
use crate::debug::Level;
|
||||
use crate::mem;
|
||||
use crate::proc::{wait, Process, Pid};
|
||||
use crate::proc::{wait, Pid, Process};
|
||||
use core::mem::size_of;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
use libcommon::{Read, Write};
|
||||
use syscall::{abi, stat::AT_FDCWD};
|
||||
use vfs::{FileMode, OpenFlags, Stat};
|
||||
use crate::arch::platform::exception::ExceptionFrame;
|
||||
|
||||
fn translate(virt: usize) -> Option<usize> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
|
||||
}
|
||||
|
||||
fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
|
||||
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
|
||||
warnln!(
|
||||
"User region refers to kernel memory: base={:#x}, len={:#x}",
|
||||
base,
|
||||
len
|
||||
);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) {
|
||||
if translate(i * mem::PAGE_SIZE).is_none() {
|
||||
warnln!(
|
||||
"User region refers to unmapped memory: base={:#x}, len={:#x} (page {:#x})",
|
||||
base,
|
||||
len,
|
||||
i * mem::PAGE_SIZE
|
||||
);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, len) })
|
||||
}
|
||||
|
||||
fn validate_user_ptr_null<'a>(base: usize, len: usize) -> Result<Option<&'a mut [u8]>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
validate_user_ptr(base, len).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_user_str<'a>(base: usize, limit: usize) -> Result<&'a str, Errno> {
|
||||
if base > mem::KERNEL_OFFSET {
|
||||
warnln!("User string refers to kernel memory: base={:#x}", base);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let base_ptr = base as *const u8;
|
||||
let mut len = 0;
|
||||
let mut page_valid = false;
|
||||
loop {
|
||||
if len == limit {
|
||||
warnln!("User string exceeded limit: base={:#x}", base);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if (base + len) % mem::PAGE_SIZE == 0 {
|
||||
page_valid = false;
|
||||
}
|
||||
|
||||
if !page_valid && translate((base + len) & !0xFFF).is_none() {
|
||||
warnln!(
|
||||
"User string refers to unmapped memory: base={:#x}, off={:#x}",
|
||||
base,
|
||||
len
|
||||
);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
page_valid = true;
|
||||
|
||||
let byte = unsafe { *base_ptr.add(len) };
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let slice = unsafe { core::slice::from_raw_parts(base_ptr, len) };
|
||||
core::str::from_utf8(slice).map_err(|_| {
|
||||
warnln!(
|
||||
"User string contains invalid UTF-8 characters: base={:#x}",
|
||||
base
|
||||
);
|
||||
Errno::InvalidArgument
|
||||
})
|
||||
}
|
||||
mod arg;
|
||||
use arg::*;
|
||||
|
||||
/// Creates a "fork" process from current one using its register frame.
|
||||
/// See [Process::fork()].
|
||||
@@ -132,9 +36,9 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
// I/O system calls
|
||||
abi::SYS_OPENAT => {
|
||||
let at_fd = args[0];
|
||||
let path = validate_user_str(args[1], 256)?;
|
||||
let mode = FileMode::from_bits(args[2] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let opts = OpenFlags::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let path = validate_user_str(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)?;
|
||||
|
||||
let at = if at_fd as i32 == AT_FDCWD {
|
||||
None
|
||||
@@ -166,8 +70,8 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let fd = args[0];
|
||||
let filename = validate_user_str(args[1], 256)?;
|
||||
let buf = validate_user_ptr_struct::<Stat>(args[2])?;
|
||||
let filename = validate_user_str(args[1], args[2])?;
|
||||
let buf = validate_user_ptr_struct::<Stat>(args[3])?;
|
||||
|
||||
// TODO "self" flag
|
||||
let at = if fd as i32 != AT_FDCWD {
|
||||
+8
-15
@@ -1,25 +1,18 @@
|
||||
use crate::sys::{self, Stat};
|
||||
use syscall::stat::AT_FDCWD;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::fmt;
|
||||
|
||||
const STDOUT_FILENO: i32 = 0;
|
||||
|
||||
pub fn stat(pathname: &str) -> Result<Stat, ()> {
|
||||
let mut buf = MaybeUninit::<Stat>::uninit();
|
||||
let mut path = [0u8; 256];
|
||||
|
||||
let bytes = pathname.as_bytes();
|
||||
path[..pathname.len()].copy_from_slice(&bytes);
|
||||
path[pathname.len()] = 0;
|
||||
|
||||
unsafe {
|
||||
let res = sys::sys_fstatat(AT_FDCWD, path.as_ptr(), buf.as_mut_ptr(), 0);
|
||||
if res != 0 {
|
||||
todo!();
|
||||
}
|
||||
Ok(buf.assume_init())
|
||||
let mut buf = Stat::default();
|
||||
let res = unsafe {
|
||||
sys::sys_fstatat(AT_FDCWD, pathname, &mut buf, 0)
|
||||
};
|
||||
if res != 0 {
|
||||
todo!();
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
// print!/println! group
|
||||
@@ -58,6 +51,6 @@ pub fn _print(args: fmt::Arguments) {
|
||||
};
|
||||
writer.write_fmt(args).ok();
|
||||
unsafe {
|
||||
sys::sys_write(STDOUT_FILENO, &BUFFER as *const _, writer.pos);
|
||||
sys::sys_write(STDOUT_FILENO, &BUFFER[..writer.pos]);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ pub mod io;
|
||||
|
||||
pub mod sys {
|
||||
pub use syscall::calls::*;
|
||||
pub use syscall::stat::{AT_FDCWD, Stat, OpenFlags};
|
||||
pub use syscall::stat::{AT_FDCWD, Stat, OpenFlags, FileMode};
|
||||
}
|
||||
|
||||
#[link_section = ".text._start"]
|
||||
|
||||
+1
-1
@@ -30,6 +30,6 @@ pub fn _trace(args: fmt::Arguments) {
|
||||
};
|
||||
writer.write_fmt(args).ok();
|
||||
unsafe {
|
||||
sys::sys_ex_debug_trace(&BUFFER as *const _, writer.pos);
|
||||
sys::sys_ex_debug_trace(&BUFFER[..writer.pos]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,3 @@ bitflags = "^1.3.0"
|
||||
|
||||
[features]
|
||||
user = []
|
||||
|
||||
# TODO implement this feature
|
||||
linux_compat = []
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
pub const SYS_EX_DEBUG_TRACE: usize = 128;
|
||||
pub const SYS_EX_NANOSLEEP: usize = 129;
|
||||
|
||||
// From aarch64 linux
|
||||
pub const SYS_EXIT: usize = 1;
|
||||
pub const SYS_READ: usize = 2;
|
||||
pub const SYS_WRITE: usize = 3;
|
||||
|
||||
+45
-21
@@ -1,5 +1,7 @@
|
||||
use crate::abi;
|
||||
use crate::stat::Stat;
|
||||
use crate::stat::{Stat, OpenFlags, FileMode};
|
||||
|
||||
// TODO document the syscall ABI
|
||||
|
||||
macro_rules! syscall {
|
||||
($num:expr) => {{
|
||||
@@ -35,58 +37,80 @@ macro_rules! syscall {
|
||||
in("x3") $a3, in("x8") $num, options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1, in("x2") $a2,
|
||||
in("x3") $a3, in("x4") $a4, in("x8") $num, options(nostack));
|
||||
res
|
||||
}};
|
||||
}
|
||||
|
||||
/// Integer/size argument
|
||||
macro_rules! argn {
|
||||
($a:expr) => ($a as usize)
|
||||
}
|
||||
/// Pointer/base argument
|
||||
macro_rules! argp {
|
||||
($a:expr) => ($a as *mut core::ffi::c_void as usize)
|
||||
}
|
||||
// /// Immutable pointer/base argument
|
||||
// macro_rules! argpi {
|
||||
// ($a:expr) => ($a as *const core::ffi::c_void as usize)
|
||||
// }
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_exit(status: i32) -> ! {
|
||||
syscall!(abi::SYS_EXIT, status as usize);
|
||||
syscall!(abi::SYS_EXIT, argn!(status));
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_close(fd: i32) -> i32 {
|
||||
syscall!(abi::SYS_CLOSE, fd as usize) as i32
|
||||
syscall!(abi::SYS_CLOSE, argn!(fd)) as i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_ex_nanosleep(ns: u64, rem: *mut [u64; 2]) -> i32 {
|
||||
syscall!(abi::SYS_EX_NANOSLEEP, ns as usize, rem as usize) as i32
|
||||
pub unsafe fn sys_ex_nanosleep(ns: u64, rem: &mut [u64; 2]) -> i32 {
|
||||
syscall!(abi::SYS_EX_NANOSLEEP, argn!(ns), argp!(rem.as_mut_ptr())) as i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_ex_debug_trace(msg: *const u8, len: usize) -> usize {
|
||||
syscall!(abi::SYS_EX_DEBUG_TRACE, msg as usize, len)
|
||||
pub unsafe fn sys_ex_debug_trace(msg: &[u8]) -> usize {
|
||||
syscall!(abi::SYS_EX_DEBUG_TRACE, argp!(msg.as_ptr()), argn!(msg.len()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_openat(at: i32, pathname: *const u8, mode: u32, flags: u32) -> i32 {
|
||||
pub unsafe fn sys_openat(at: i32, pathname: &str, mode: FileMode, flags: OpenFlags) -> i32 {
|
||||
syscall!(
|
||||
abi::SYS_OPENAT,
|
||||
at as usize,
|
||||
pathname as usize,
|
||||
mode as usize,
|
||||
flags as usize
|
||||
argn!(at),
|
||||
argp!(pathname.as_ptr()),
|
||||
argn!(pathname.len()),
|
||||
argn!(mode.bits()),
|
||||
argn!(flags.bits())
|
||||
) as i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_read(fd: i32, data: *mut u8, len: usize) -> isize {
|
||||
syscall!(abi::SYS_READ, fd as usize, data as usize, len) as isize
|
||||
pub unsafe fn sys_read(fd: i32, data: &mut [u8]) -> isize {
|
||||
syscall!(abi::SYS_READ, argn!(fd), argp!(data.as_mut_ptr()), argn!(data.len())) as isize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_write(fd: i32, data: *const u8, len: usize) -> isize {
|
||||
syscall!(abi::SYS_WRITE, fd as usize, data as usize, len) as isize
|
||||
pub unsafe fn sys_write(fd: i32, data: &[u8]) -> isize {
|
||||
syscall!(abi::SYS_WRITE, argn!(fd), argp!(data.as_ptr()), argn!(data.len())) as isize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_fstatat(at: i32, pathname: *const u8, statbuf: *mut Stat, flags: i32) -> i32 {
|
||||
pub unsafe fn sys_fstatat(at: i32, pathname: &str, statbuf: &mut Stat, flags: i32) -> i32 {
|
||||
syscall!(
|
||||
abi::SYS_FSTATAT,
|
||||
at as usize,
|
||||
pathname as usize,
|
||||
statbuf as usize,
|
||||
flags as usize
|
||||
argn!(at),
|
||||
argp!(pathname.as_ptr()),
|
||||
argn!(pathname.len()),
|
||||
argp!(statbuf as *mut Stat),
|
||||
argn!(flags)
|
||||
) as i32
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "linux_compat")]
|
||||
compile_error!("Not yet implemented");
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Stat {
|
||||
pub mode: u32,
|
||||
|
||||
Reference in New Issue
Block a user