Skip to content

Commit

Permalink
Implements syscall 165 (#399)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Oct 19, 2023
1 parent 3ae9711 commit edf183a
Show file tree
Hide file tree
Showing 17 changed files with 685 additions and 388 deletions.
54 changes: 54 additions & 0 deletions src/kernel/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::errno::EINVAL;
use crate::info;
use crate::process::{PcbFlags, VThread};
use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls};
use std::sync::Arc;

/// An implementation of machine-dependent services.
pub struct MachDep {}

impl MachDep {
const I386_GET_IOPERM: u32 = 3;
const I386_SET_IOPERM: u32 = 4;
const AMD64_SET_FSBASE: u32 = 129;

pub fn new(sys: &mut Syscalls) -> Arc<Self> {
let mach = Arc::new(Self {});

sys.register(165, &mach, Self::sysarch);

mach
}

fn sysarch(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
let op: u32 = i.args[0].try_into().unwrap();
let parms: *mut u8 = i.args[1].into();
let td = VThread::current();
let mut pcb = td.pcb_mut();

if op < 2 {
return Err(SysErr::Raw(EINVAL));
}

match op {
Self::I386_GET_IOPERM | Self::I386_SET_IOPERM => todo!("sysarch with op = 3 | 4"),
_ => {}
}

match op {
Self::AMD64_SET_FSBASE => {
// We can't check if the value within the user space because we are not a real
// kernel.
let v = unsafe { std::ptr::read_unaligned(parms as _) };

pcb.set_fsbase(v);
*pcb.flags_mut() |= PcbFlags::PCB_FULL_IRET;

info!("FS segment has been changed to {v:#x}.");
}
v => todo!("sysarch with op = {v}"),
}

Ok(SysOut::ZERO)
}
}
26 changes: 8 additions & 18 deletions src/kernel/src/ee/llvm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use self::codegen::Codegen;
use super::{ExecutionEngine, SysErr, SysIn, SysOut};
use super::ExecutionEngine;
use crate::disasm::Disassembler;
use crate::fs::VPathBuf;
use crate::llvm::Llvm;
use crate::rtld::Module;
use crate::syscalls::Syscalls;
use std::sync::Arc;
use thiserror::Error;

Expand Down Expand Up @@ -69,30 +70,19 @@ impl ExecutionEngine for LlvmEngine {
type SetupModuleErr = SetupModuleError;
type GetFunctionErr = GetFunctionError;

fn register_syscall<O>(
&self,
id: u32,
o: &Arc<O>,
h: fn(&Arc<O>, &SysIn) -> Result<SysOut, SysErr>,
) {
fn set_syscalls(&self, v: Syscalls) {
todo!()
}

fn setup_module<E>(&self, md: &mut Module<E>) -> Result<(), Self::SetupModuleErr>
where
E: ExecutionEngine,
{
fn setup_module(self: &Arc<Self>, md: &mut Module<Self>) -> Result<(), Self::SetupModuleErr> {
todo!()
}

unsafe fn get_function<E>(
&self,
md: &Arc<Module<E>>,
unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module<Self>>,
addr: usize,
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr>
where
E: ExecutionEngine,
{
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr> {
todo!()
}
}
Expand Down
235 changes: 10 additions & 225 deletions src/kernel/src/ee/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use crate::arnd::Arnd;
use crate::errno::{strerror, Errno, ENAMETOOLONG, ENOENT};
use crate::fs::{VPath, VPathBuf};
use crate::memory::MemoryManager;
use crate::process::{ResourceLimit, VProc};
use crate::rtld::Module;
use crate::syscalls::Syscalls;
use std::error::Error;
use std::ffi::{c_char, CStr, CString};
use std::fmt::{Debug, Display, Formatter};
use std::ffi::CString;
use std::fmt::Debug;
use std::marker::PhantomPinned;
use std::mem::size_of_val;
use std::num::{NonZeroI32, TryFromIntError};
use std::pin::Pin;
use std::sync::Arc;

Expand All @@ -23,35 +21,15 @@ pub trait ExecutionEngine: Debug + Send + Sync + 'static {
type SetupModuleErr: Error;
type GetFunctionErr: Error;

/// The implementor must not have any variable that need to be dropped on the stack before
/// invoking the registered handler. The reason is because the handler might exit the calling
/// thread without returning from the handler.
///
/// See https://github.com/freebsd/freebsd-src/blob/release/9.1.0/sys/kern/init_sysent.c#L36 for
/// standard FreeBSD syscalls.
///
/// # Panics
/// If `id` is not a valid number or the syscall with identifier `id` is already registered.
fn register_syscall<O: Send + Sync + 'static>(
&self,
id: u32,
o: &Arc<O>,
h: fn(&Arc<O>, &SysIn) -> Result<SysOut, SysErr>,
);

// TODO: Is it possible to force E as Self?
fn setup_module<E>(&self, md: &mut Module<E>) -> Result<(), Self::SetupModuleErr>
where
E: ExecutionEngine;

// TODO: Is it possible to force E as Self?
unsafe fn get_function<E>(
&self,
md: &Arc<Module<E>>,
/// If this method called a second time.
fn set_syscalls(&self, v: Syscalls);
fn setup_module(self: &Arc<Self>, md: &mut Module<Self>) -> Result<(), Self::SetupModuleErr>;
unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module<Self>>,
addr: usize,
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr>
where
E: ExecutionEngine;
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr>;
}

/// A function that was produced by [`ExecutionEngine`].
Expand All @@ -63,199 +41,6 @@ pub trait RawFn: Send + Sync + 'static {
unsafe fn exec1<R, A>(&self, a: A) -> R;
}

/// Input of the syscall handler.
#[repr(C)]
pub struct SysIn<'a> {
pub id: u32,
pub offset: usize,
pub module: &'a VPathBuf,
pub args: [SysArg; 6],
}

/// An argument of the syscall.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct SysArg(usize);

impl SysArg {
pub unsafe fn to_path<'a>(self) -> Result<Option<&'a VPath>, SysErr> {
if self.0 == 0 {
return Ok(None);
}

// TODO: Check maximum path length on the PS4.
let path = CStr::from_ptr(self.0 as _);
let path = match path.to_str() {
Ok(v) => match VPath::new(v) {
Some(v) => v,
None => todo!("syscall with non-absolute path {v}"),
},
Err(_) => return Err(SysErr::Raw(ENOENT)),
};

Ok(Some(path))
}

/// See `copyinstr` on the PS4 for a reference.
pub unsafe fn to_str<'a>(self, max: usize) -> Result<Option<&'a str>, SysErr> {
if self.0 == 0 {
return Ok(None);
}

let ptr = self.0 as *const c_char;
let mut len = None;

for i in 0..max {
if *ptr.add(i) == 0 {
len = Some(i);
break;
}
}

match len {
Some(i) => Ok(Some(
std::str::from_utf8(std::slice::from_raw_parts(ptr as _, i)).unwrap(),
)),
None => Err(SysErr::Raw(ENAMETOOLONG)),
}
}

pub fn get(self) -> usize {
self.0
}
}

impl<T> From<SysArg> for *const T {
fn from(v: SysArg) -> Self {
v.0 as _
}
}

impl<T> From<SysArg> for *mut T {
fn from(v: SysArg) -> Self {
v.0 as _
}
}

impl From<SysArg> for u64 {
fn from(v: SysArg) -> Self {
v.0 as _
}
}

impl From<SysArg> for usize {
fn from(v: SysArg) -> Self {
v.0
}
}

impl TryFrom<SysArg> for i32 {
type Error = TryFromIntError;

fn try_from(v: SysArg) -> Result<Self, Self::Error> {
TryInto::<u32>::try_into(v.0).map(|v| v as i32)
}
}

impl TryFrom<SysArg> for u32 {
type Error = TryFromIntError;

fn try_from(v: SysArg) -> Result<Self, Self::Error> {
v.0.try_into()
}
}

/// Outputs of the syscall handler.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SysOut {
rax: usize,
rdx: usize,
}

impl SysOut {
pub const ZERO: Self = Self { rax: 0, rdx: 0 };
}

impl<T> From<*mut T> for SysOut {
fn from(value: *mut T) -> Self {
Self {
rax: value as _,
rdx: 0,
}
}
}

impl From<i32> for SysOut {
fn from(value: i32) -> Self {
Self {
rax: value as isize as usize, // Sign extended.
rdx: 0,
}
}
}

impl From<usize> for SysOut {
fn from(value: usize) -> Self {
Self { rax: value, rdx: 0 }
}
}

impl From<NonZeroI32> for SysOut {
fn from(value: NonZeroI32) -> Self {
Self {
rax: value.get() as isize as usize, // Sign extended.
rdx: 0,
}
}
}

/// Error of each syscall.
#[derive(Debug)]
pub enum SysErr {
Raw(NonZeroI32),
Object(Box<dyn Errno>),
}

impl SysErr {
pub fn errno(&self) -> NonZeroI32 {
match self {
Self::Raw(v) => *v,
Self::Object(v) => v.errno(),
}
}
}

impl From<Box<dyn Errno>> for SysErr {
fn from(value: Box<dyn Errno>) -> Self {
Self::Object(value)
}
}

impl<T: Errno + 'static> From<T> for SysErr {
fn from(value: T) -> Self {
Self::Object(Box::new(value))
}
}

impl Error for SysErr {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Raw(_) => None,
Self::Object(e) => e.source(),
}
}
}

impl Display for SysErr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Raw(v) => f.write_str(strerror(*v)),
Self::Object(e) => Display::fmt(&e, f),
}
}
}

/// Encapsulate an argument of the PS4 entry point.
pub struct EntryArg<E: ExecutionEngine> {
vp: Arc<VProc>,
Expand Down
Loading

0 comments on commit edf183a

Please sign in to comment.