sos-code-article10/sos/syscall.c

1483 lines
34 KiB
C

/* Copyright (C) 2005 David Decotigny
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include <sos/assert.h>
#include <sos/thread.h>
#include <sos/kmalloc.h>
#include <sos/klibc.h>
#include <drivers/bochs.h>
#include <sos/physmem.h>
#include <sos/umem_vmm.h>
#include <drivers/zero.h>
#include <drivers/mem.h>
#include <sos/binfmt_elf32.h>
#include <hwcore/cpu_context.h>
#include <sos/uaccess.h>
#include "syscall.h"
/** To get rid of gcc's "dereferencing type-punned pointer will break
strict-aliasing rules" warning */
#define SYSCALL_VAR32_PTR(ui32_variable) \
((void*)&(ui32_variable))
/**
* THE syscall entry point
*/
sos_ret_t sos_do_syscall(int syscall_id,
const struct sos_cpu_state *user_ctxt)
{
sos_ret_t retval;
switch(syscall_id)
{
case SOS_SYSCALL_ID_EXIT:
{
unsigned int status;
retval = sos_syscall_get1arg(user_ctxt, & status);
if (SOS_OK != retval)
break;
sos_thread_exit();
retval = -SOS_EFATAL; /* Not reached */
}
break;
case SOS_SYSCALL_ID_FORK:
{
struct sos_thread *cur_thr, *new_thr;
struct sos_process *new_proc;
cur_thr = sos_thread_get_current();
/* Duplicate the current process (and its address space) */
new_proc = sos_process_create(NULL, TRUE);
if (! new_proc)
{
retval = -SOS_ENOMEM;
break;
}
/* Create *the* thread in this new processs, copy of the
current user thread (same registers, EXCEPT eax which is
set to 0) */
new_thr =
sos_duplicate_user_thread(NULL, new_proc,
cur_thr,
user_ctxt,
0 /* fork return value for child ! */);
if (! new_thr)
{
sos_process_unref(new_proc);
retval = -SOS_ENOMEM;
break;
}
sos_process_unref(new_proc);
/* Return to the "parent" thread with a value different from
0. Unix says it should be the "PID" of the child. We don't
have such a "PID" notion for now */
retval = (sos_ui32_t)sos_process_get_pid(new_proc);
}
break;
case SOS_SYSCALL_ID_GETPID:
{
struct sos_thread *cur_thr;
struct sos_process * proc;
cur_thr = sos_thread_get_current();
proc = cur_thr->process;
retval = (sos_ui32_t) sos_process_get_pid(proc);
}
break;
case SOS_SYSCALL_ID_EXEC:
{
struct sos_thread *cur_thr, *new_thr;
struct sos_process * proc;
struct sos_umem_vmm_as *new_as;
sos_uaddr_t user_str, ustack, start_uaddr;
sos_uaddr_t src_argaddr;
sos_size_t len_args;
sos_size_t len;
char * str;
cur_thr = sos_thread_get_current();
proc = cur_thr->process;
/* Make sure the process has exactly 1 thread in it */
if (sos_process_get_nb_threads(proc) != 1)
{
retval = -SOS_EBUSY;
break;
}
/* Get the user arguments */
retval = sos_syscall_get4args(user_ctxt, & user_str, & len,
& src_argaddr, & len_args);
if (SOS_OK != retval)
break;
/* Don't go any further if the arg/env array is obviously too
large */
if (SOS_DEFAULT_USER_STACK_SIZE <= len_args)
{
retval = -SOS_EINVAL;
break;
}
/* Copy the program name into kernel sppace */
retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
if (SOS_OK != retval)
{
break;
}
/* Create a new empty address space to map the program */
new_as = sos_umem_vmm_create_empty_as(proc);
if (! new_as)
{
sos_kfree((sos_vaddr_t)str);
retval = -SOS_ENOMEM;
break;
}
/* Map the program in it */
start_uaddr = sos_binfmt_elf32_map(new_as, str);
if (start_uaddr == (sos_uaddr_t)NULL)
{
sos_umem_vmm_delete_as(new_as);
sos_kfree((sos_vaddr_t)str);
retval = -SOS_ENOENT;
break;
}
/* Allocate space for the user stack (8MB) */
#define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
ustack = (SOS_PAGING_UPPER_USER_ADDRESS
- SOS_DEFAULT_USER_STACK_SIZE)
+ 1;
retval = sos_dev_zero_map(new_as, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
/* PRIVATE */ 0);
if (SOS_OK != retval)
{
sos_umem_vmm_delete_as(new_as);
sos_kfree((sos_vaddr_t)str);
break;
}
/* ustack now refers to the initial stack pointer of the new
user thread: update it here */
ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE
- len_args), 4);
if (len_args != sos_usercpy(new_as, ustack,
NULL, src_argaddr,
len_args))
{
sos_umem_vmm_delete_as(new_as);
sos_kfree((sos_vaddr_t)str);
retval = -SOS_EFAULT;
break;
}
/* Now create the user thread */
new_thr = sos_create_user_thread(NULL,
proc,
start_uaddr,
0, 0,
ustack,
SOS_SCHED_PRIO_TS_LOWEST);
if (! new_thr)
{
sos_umem_vmm_delete_as(new_as);
sos_kfree((sos_vaddr_t)str);
retval = -SOS_ENOMEM;
break;
}
sos_process_set_name(proc, str);
/* Switch to this address space */
retval = sos_process_set_address_space(proc,
new_as);
if (SOS_OK != retval)
{
sos_umem_vmm_delete_as(new_as);
sos_kfree((sos_vaddr_t)str);
break;
}
/* The current thread must exit now */
sos_kfree((sos_vaddr_t)str);
sos_thread_exit();
retval = -SOS_EFATAL;
}
break;
case SOS_SYSCALL_ID_MUNMAP:
{
sos_uaddr_t start_uaddr;
sos_size_t size;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(start_uaddr),
SYSCALL_VAR32_PTR(size));
if (SOS_OK != retval)
break;
retval = sos_umem_vmm_unmap(my_as, start_uaddr, size);
}
break;
case SOS_SYSCALL_ID_MPROTECT:
{
sos_uaddr_t start_uaddr;
sos_size_t size;
sos_ui32_t new_access_rights;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(start_uaddr),
SYSCALL_VAR32_PTR(size),
SYSCALL_VAR32_PTR(new_access_rights));
if (SOS_OK != retval)
break;
retval = sos_thread_prepare_user_space_access(NULL, (sos_vaddr_t)NULL);
if (SOS_OK != retval)
break;
retval = sos_umem_vmm_chprot(my_as, start_uaddr, size,
new_access_rights);
sos_thread_end_user_space_access();
}
break;
case SOS_SYSCALL_ID_MRESIZE:
{
sos_uaddr_t old_uaddr;
sos_size_t old_size;
sos_uaddr_t *uptr_new_uaddr;
sos_uaddr_t new_uaddr;
sos_size_t new_size;
sos_ui32_t flags;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get5args(user_ctxt,
SYSCALL_VAR32_PTR(old_uaddr),
SYSCALL_VAR32_PTR(old_size),
SYSCALL_VAR32_PTR(uptr_new_uaddr),
SYSCALL_VAR32_PTR(new_size),
SYSCALL_VAR32_PTR(flags));
if (SOS_OK != retval)
break;
if (sizeof(new_uaddr) != sos_memcpy_from_user((sos_vaddr_t)& new_uaddr,
(sos_uaddr_t)
uptr_new_uaddr,
sizeof(new_uaddr)))
{
retval = -SOS_EFAULT;
break;
}
retval = sos_thread_prepare_user_space_access(NULL, (sos_vaddr_t)NULL);
if (SOS_OK != retval)
break;
retval = sos_umem_vmm_resize(my_as, old_uaddr, old_size,
& new_uaddr, new_size, flags);
sos_thread_end_user_space_access();
if (SOS_OK != retval)
break;
if (sizeof(new_uaddr)
== sos_memcpy_to_user((sos_uaddr_t)uptr_new_uaddr,
(sos_vaddr_t)&new_uaddr,
sizeof(new_uaddr)))
{
retval = -SOS_EFAULT;
break;
}
}
break;
case SOS_SYSCALL_ID_MSYNC:
{
sos_uaddr_t start_uaddr;
sos_size_t size;
sos_ui32_t flags;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(start_uaddr),
SYSCALL_VAR32_PTR(size),
SYSCALL_VAR32_PTR(flags));
if (SOS_OK != retval)
break;
retval = sos_thread_prepare_user_space_access(NULL, (sos_vaddr_t)NULL);
if (SOS_OK != retval)
break;
retval = sos_umem_vmm_sync(my_as, start_uaddr, size, flags);
sos_thread_end_user_space_access();
}
break;
case SOS_SYSCALL_ID_NEW_THREAD:
{
sos_uaddr_t start_func;
sos_ui32_t start_arg1, start_arg2;
sos_size_t stack_size;
sos_uaddr_t stack_uaddr;
struct sos_thread * new_thr;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get4args(user_ctxt,
SYSCALL_VAR32_PTR(start_func),
SYSCALL_VAR32_PTR(start_arg1),
SYSCALL_VAR32_PTR(start_arg2),
SYSCALL_VAR32_PTR(stack_size));
if (SOS_OK != retval)
break;
if (stack_size <= 0)
{
retval = -SOS_EINVAL;
break;
}
/* Allocate the stack */
stack_uaddr = 0;
stack_size = SOS_PAGE_ALIGN_SUP(stack_size);
retval = sos_dev_zero_map(my_as, & stack_uaddr, stack_size,
SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
/* PRIVATE */ 0);
if (SOS_OK != retval)
break;
/* Now create the user thread */
new_thr = sos_create_user_thread(NULL,
sos_thread_get_current()->process,
start_func,
start_arg1,
start_arg2,
stack_uaddr + stack_size - 4,
SOS_SCHED_PRIO_TS_LOWEST);
if (! new_thr)
{
sos_umem_vmm_unmap(my_as, stack_uaddr, stack_size);
retval = -SOS_ENOMEM;
break;
}
}
break;
case SOS_SYSCALL_ID_NANOSLEEP:
{
struct sos_time delay;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(delay.sec),
SYSCALL_VAR32_PTR(delay.nanosec));
if (SOS_OK != retval)
break;
retval = sos_thread_sleep(& delay);
}
break;
case SOS_SYSCALL_ID_BRK:
{
sos_uaddr_t new_top_heap;
struct sos_umem_vmm_as * my_as;
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
retval = sos_syscall_get1arg(user_ctxt,
SYSCALL_VAR32_PTR(new_top_heap));
if (SOS_OK != retval)
break;
retval = sos_thread_prepare_user_space_access(NULL, (sos_vaddr_t)NULL);
if (SOS_OK != retval)
break;
retval = sos_umem_vmm_brk(my_as, new_top_heap);
sos_thread_end_user_space_access();
}
break;
/**
* File system interface
*/
case SOS_SYSCALL_ID_MOUNT:
{
sos_uaddr_t user_src;
sos_size_t srclen;
char * kernel_src = NULL;
sos_uaddr_t user_dst;
sos_size_t dstlen;
char * kernel_dst;
sos_ui32_t mountflags;
sos_uaddr_t user_fstype;
char * kernel_fstype;
sos_uaddr_t user_args;
char * kernel_args = NULL;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get7args(user_ctxt,
SYSCALL_VAR32_PTR(user_src),
SYSCALL_VAR32_PTR(srclen),
SYSCALL_VAR32_PTR(user_dst),
SYSCALL_VAR32_PTR(dstlen),
SYSCALL_VAR32_PTR(user_fstype),
SYSCALL_VAR32_PTR(mountflags),
SYSCALL_VAR32_PTR(user_args));
if (SOS_OK != retval)
break;
if (user_src != (sos_uaddr_t)NULL)
{
retval = sos_strndup_from_user(&kernel_src, user_src, srclen, 0);
if (SOS_OK != retval)
break;
}
retval = sos_strndup_from_user(&kernel_dst, user_dst, dstlen, 0);
if (SOS_OK != retval)
{
if (kernel_src)
sos_kfree((sos_vaddr_t)kernel_src);
break;
}
retval = sos_strndup_from_user(& kernel_fstype, user_fstype, 256, 0);
if (SOS_OK != retval)
{
if (kernel_src)
sos_kfree((sos_vaddr_t)kernel_src);
sos_kfree((sos_vaddr_t)kernel_dst);
break;
}
if (user_args != (sos_uaddr_t)NULL)
{
retval = sos_strndup_from_user(& kernel_args, user_args, 1024, 0);
if (SOS_OK != retval)
{
if (kernel_src)
sos_kfree((sos_vaddr_t)kernel_src);
sos_kfree((sos_vaddr_t)kernel_dst);
sos_kfree((sos_vaddr_t)kernel_fstype);
break;
}
}
retval = sos_fs_mount(proc, kernel_src, srclen,
kernel_dst, dstlen,
kernel_fstype,
mountflags,
kernel_args,
NULL);
if (kernel_src)
sos_kfree((sos_vaddr_t)kernel_src);
sos_kfree((sos_vaddr_t)kernel_dst);
sos_kfree((sos_vaddr_t)kernel_fstype);
if (kernel_args)
sos_kfree((sos_vaddr_t)kernel_args);
}
break;
case SOS_SYSCALL_ID_UMOUNT:
{
sos_uaddr_t user_str;
sos_size_t len;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_umount(proc,
path, len);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_SYNC:
{
sos_fs_sync_all_fs();
retval = SOS_OK;
}
break;
case SOS_SYSCALL_ID_VFSTAT64:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_uaddr_t user_vfstat_struct;
struct sos_fs_statfs kernel_vfstat_struct;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(user_vfstat_struct));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_vfstat(proc, path, len, & kernel_vfstat_struct);
sos_kfree((sos_vaddr_t)path);
if (SOS_OK != retval)
break;
if (sizeof(kernel_vfstat_struct)
!= sos_memcpy_to_user(user_vfstat_struct,
(sos_vaddr_t) & kernel_vfstat_struct,
sizeof(kernel_vfstat_struct)))
retval = -SOS_EFAULT;
}
break;
case SOS_SYSCALL_ID_OPEN:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_ui32_t open_flags;
sos_ui32_t access_rights;
char * path;
struct sos_fs_opened_file * of;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get4args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(open_flags),
SYSCALL_VAR32_PTR(access_rights));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_open(proc,
path, len,
open_flags,
access_rights,
& of);
sos_kfree((sos_vaddr_t)path);
if (SOS_OK != retval)
break;
retval = sos_process_register_opened_file(proc, of, 0);
if (retval < 0)
{
sos_fs_close(of);
break;
}
}
break;
case SOS_SYSCALL_ID_CLOSE:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get1arg(user_ctxt,
SYSCALL_VAR32_PTR(fd));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
retval = sos_process_unregister_opened_file(proc, fd);
if (SOS_OK != retval)
break;
retval = sos_fs_close(of);
}
break;
case SOS_SYSCALL_ID_READ:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_uaddr_t uaddr_buf;
sos_uaddr_t uaddr_buflen;
sos_size_t kernel_buflen;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(uaddr_buf),
SYSCALL_VAR32_PTR(uaddr_buflen));
if (SOS_OK != retval)
break;
/* Retrieve the value for "buflen" */
retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen,
uaddr_buflen,
sizeof(kernel_buflen));
if (sizeof(kernel_buflen) != retval)
{
retval = -SOS_EFAULT;
break;
}
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual reading */
retval = sos_fs_read(of, uaddr_buf, & kernel_buflen);
/* Send successful number of bytes read to user */
sos_memcpy_to_user(uaddr_buflen,
(sos_vaddr_t)& kernel_buflen,
sizeof(kernel_buflen));
}
break;
case SOS_SYSCALL_ID_READDIR:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_uaddr_t uaddr_direntry;
struct sos_fs_dirent direntry;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(uaddr_direntry));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual readdir */
retval = sos_fs_readdir(of, & direntry);
if (SOS_OK != retval)
break;
/* Send direntry structure to user */
if (sizeof(direntry) != sos_memcpy_to_user(uaddr_direntry,
(sos_vaddr_t)& direntry,
sizeof(direntry)))
retval = -SOS_EFAULT;
}
break;
case SOS_SYSCALL_ID_WRITE:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_uaddr_t uaddr_buf;
sos_uaddr_t uaddr_buflen;
sos_size_t kernel_buflen;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(uaddr_buf),
SYSCALL_VAR32_PTR(uaddr_buflen));
if (SOS_OK != retval)
break;
/* Retrieve the value for "buflen" */
retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen,
uaddr_buflen,
sizeof(kernel_buflen));
if (sizeof(kernel_buflen) != retval)
{
retval = -SOS_EFAULT;
break;
}
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual writing */
retval = sos_fs_write(of, uaddr_buf, & kernel_buflen);
/* Send successful number of bytes written to user */
sos_memcpy_to_user(uaddr_buflen,
(sos_vaddr_t)& kernel_buflen,
sizeof(kernel_buflen));
}
break;
case SOS_SYSCALL_ID_SEEK64:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_uaddr_t uaddr_offset;
sos_seek_whence_t whence;
sos_lsoffset_t kernel_offset, result_position;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(uaddr_offset),
SYSCALL_VAR32_PTR(whence));
if (SOS_OK != retval)
break;
/* Retrieve the value for "buflen" */
retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_offset,
uaddr_offset,
sizeof(kernel_offset));
if (sizeof(kernel_offset) != retval)
{
retval = -SOS_EFAULT;
break;
}
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual seek */
retval = sos_fs_seek(of, kernel_offset, whence, & result_position);
/* Send successful number of bytes written to user */
sos_memcpy_to_user(uaddr_offset,
(sos_vaddr_t)& result_position,
sizeof(kernel_offset));
}
break;
case SOS_SYSCALL_ID_FTRUNCATE64:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_lsoffset_t length;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(length));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual trunc */
retval = sos_fs_ftruncate(of, length);
}
break;
case SOS_SYSCALL_ID_FSMMAP:
{
sos_uaddr_t ptr_hint_uaddr;
sos_uaddr_t hint_uaddr;
sos_size_t length;
sos_ui32_t prot;
sos_ui32_t flags;
int fd;
sos_ui32_t offs64_hi;
sos_ui32_t offs64_lo;
sos_luoffset_t offset_in_resource;
struct sos_fs_opened_file * of;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get7args(user_ctxt,
SYSCALL_VAR32_PTR(ptr_hint_uaddr),
SYSCALL_VAR32_PTR(length),
SYSCALL_VAR32_PTR(prot),
SYSCALL_VAR32_PTR(flags),
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(offs64_hi),
SYSCALL_VAR32_PTR(offs64_lo));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Compute 64 bits offset value */
offset_in_resource = offs64_hi;
offset_in_resource <<= 32;
offset_in_resource |= offs64_lo;
retval = sos_memcpy_from_user((sos_vaddr_t)& hint_uaddr,
ptr_hint_uaddr,
sizeof(hint_uaddr));
if (sizeof(hint_uaddr) != retval)
{
retval = -SOS_EFAULT;
break;
}
retval = sos_fs_mmap(of, & hint_uaddr, length, prot, flags,
offset_in_resource);
if (SOS_OK == retval)
{
if (sizeof(hint_uaddr)
!= sos_memcpy_to_user(ptr_hint_uaddr,
(sos_vaddr_t)& hint_uaddr,
sizeof(hint_uaddr)))
{
sos_umem_vmm_unmap(sos_process_get_address_space(proc),
hint_uaddr, length);
retval = -SOS_EFAULT;
}
}
}
break;
case SOS_SYSCALL_ID_FSYNC:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get1arg(user_ctxt,
SYSCALL_VAR32_PTR(fd));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual sync */
retval = sos_fs_fsync(of);
}
break;
case SOS_SYSCALL_ID_FCNTL:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_ui32_t cmd, arg;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(cmd),
SYSCALL_VAR32_PTR(arg));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual fcntl */
retval = sos_fs_fcntl(of, cmd, arg);
}
break;
case SOS_SYSCALL_ID_IOCTL:
{
struct sos_fs_opened_file * of;
struct sos_process * proc;
sos_ui32_t cmd, arg;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(fd),
SYSCALL_VAR32_PTR(cmd),
SYSCALL_VAR32_PTR(arg));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Do the actual ioctl */
retval = sos_fs_ioctl(of, cmd, arg);
}
break;
case SOS_SYSCALL_ID_CREAT:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_ui32_t access_rights;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(access_rights));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_creat(proc,
path, len,
access_rights);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_LINK:
case SOS_SYSCALL_ID_RENAME:
{
sos_uaddr_t user_oldpath, user_newpath;
sos_size_t oldpathlen, newpathlen;
char * kernel_oldpath, * kernel_newpath;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get4args(user_ctxt,
SYSCALL_VAR32_PTR(user_oldpath),
SYSCALL_VAR32_PTR(oldpathlen),
SYSCALL_VAR32_PTR(user_newpath),
SYSCALL_VAR32_PTR(newpathlen));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&kernel_oldpath,
user_oldpath,
oldpathlen, 0);
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&kernel_newpath,
user_newpath,
newpathlen, 0);
if (SOS_OK != retval)
{
sos_kfree((sos_vaddr_t) kernel_oldpath);
break;
}
if (syscall_id == SOS_SYSCALL_ID_LINK)
retval = sos_fs_link(proc,
kernel_oldpath, oldpathlen,
kernel_newpath, newpathlen);
else
retval = sos_fs_rename(proc,
kernel_oldpath, oldpathlen,
kernel_newpath, newpathlen);
sos_kfree((sos_vaddr_t)kernel_oldpath);
sos_kfree((sos_vaddr_t)kernel_newpath);
}
break;
case SOS_SYSCALL_ID_UNLINK:
{
sos_uaddr_t user_str;
sos_size_t len;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_unlink(proc,
path, len);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_SYMLINK:
{
sos_uaddr_t user_path, user_targetpath;
sos_size_t pathlen, targetpathlen;
char * kernel_path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get4args(user_ctxt,
SYSCALL_VAR32_PTR(user_path),
SYSCALL_VAR32_PTR(pathlen),
SYSCALL_VAR32_PTR(user_targetpath),
SYSCALL_VAR32_PTR(targetpathlen));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&kernel_path,
user_path,
pathlen, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_symlink(proc,
kernel_path, pathlen,
user_targetpath, targetpathlen);
sos_kfree((sos_vaddr_t)kernel_path);
}
break;
case SOS_SYSCALL_ID_MKNOD:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_ui32_t access_rights;
int type;
char * path;
struct sos_fs_dev_id_t dev_id;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get6args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(type),
SYSCALL_VAR32_PTR(access_rights),
SYSCALL_VAR32_PTR(dev_id.device_class),
SYSCALL_VAR32_PTR(dev_id.device_instance));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
switch (type)
{
case SOS_FS_NODE_REGULAR_FILE:
retval = sos_fs_creat(proc, path, len, access_rights);
break;
case SOS_FS_NODE_DIRECTORY:
retval = sos_fs_mkdir(proc, path, len, access_rights);
break;
case SOS_FS_NODE_SYMLINK:
retval = -SOS_ENOSUP;
break;
case SOS_FS_NODE_DEVICE_CHAR:
case SOS_FS_NODE_DEVICE_BLOCK:
retval = sos_fs_mknod(proc,
path, len, type, access_rights, &dev_id);
break;
default:
retval = -SOS_EINVAL;
break;
}
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_MKDIR:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_ui32_t access_rights;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(access_rights));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_mkdir(proc,
path, len, access_rights);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_RMDIR:
{
sos_uaddr_t user_str;
sos_size_t len;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_rmdir(proc, path, len);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_CHMOD:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_ui32_t access_rights;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get3args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(access_rights));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_chmod(proc, path, len, access_rights);
sos_kfree((sos_vaddr_t)path);
}
break;
case SOS_SYSCALL_ID_STAT64:
{
sos_uaddr_t user_str;
sos_size_t len;
sos_uaddr_t user_stat_struct;
struct sos_fs_stat kernel_stat_struct;
int nofollow;
char * path;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get4args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len),
SYSCALL_VAR32_PTR(nofollow),
SYSCALL_VAR32_PTR(user_stat_struct));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_stat(proc, path, len, nofollow, & kernel_stat_struct);
sos_kfree((sos_vaddr_t)path);
if (SOS_OK != retval)
break;
if (sizeof(kernel_stat_struct)
!= sos_memcpy_to_user(user_stat_struct,
(sos_vaddr_t) & kernel_stat_struct,
sizeof(kernel_stat_struct)))
retval = -SOS_EFAULT;
}
break;
case SOS_SYSCALL_ID_CHROOT:
case SOS_SYSCALL_ID_CHDIR:
{
sos_uaddr_t user_str;
sos_size_t len;
char * path;
struct sos_fs_opened_file * of, * old_of;
struct sos_process * proc;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get2args(user_ctxt,
SYSCALL_VAR32_PTR(user_str),
SYSCALL_VAR32_PTR(len));
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(&path, user_str, len, 0);
if (SOS_OK != retval)
break;
retval = sos_fs_open(proc,
path, len,
SOS_FS_OPEN_DIRECTORY,
SOS_FS_OPEN_READ,
& of);
sos_kfree((sos_vaddr_t)path);
if (SOS_OK != retval)
break;
if (syscall_id == SOS_SYSCALL_ID_CHROOT)
retval = sos_process_chroot(proc, of, & old_of);
else
retval = sos_process_chdir(proc, of, & old_of);
if (retval < 0)
{
sos_fs_close(of);
break;
}
sos_fs_close(old_of);
}
break;
case SOS_SYSCALL_ID_FCHDIR:
{
struct sos_fs_opened_file * of, * new_of, * old_of;
struct sos_process * proc;
int fd;
proc = sos_thread_get_current()->process;
retval = sos_syscall_get1arg(user_ctxt,
SYSCALL_VAR32_PTR(fd));
if (SOS_OK != retval)
break;
of = sos_process_get_opened_file(proc, fd);
if (NULL == of)
{
retval = -SOS_EBADF;
break;
}
/* Duplicate this FD */
retval = sos_fs_duplicate_opened_file(of, proc, & new_of);
if (SOS_OK != retval)
break;
/* Do the actual chdir */
retval = sos_process_chdir(proc, new_of, & old_of);
if (retval < 0)
{
sos_fs_close(new_of);
break;
}
sos_fs_close(old_of);
}
break;
case SOS_SYSCALL_ID_BOCHS_WRITE:
{
sos_uaddr_t user_str;
sos_size_t len;
char * str;
retval = sos_syscall_get2args(user_ctxt, & user_str, & len);
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
if (SOS_OK == retval)
{
sos_bochs_printf("THR 0x%x: ",
(unsigned)sos_thread_get_current());
retval = sos_bochs_putstring(str);
retval = len;
sos_kfree((sos_vaddr_t)str);
}
}
break;
/* ***********************************************
* Debug syscalls (will be removed in the future)
*/
/**
* Syscall 4012: hexdump of a user-space memory region
* args: addr_start size, retval=ignored
*/
case 4012:
{
sos_uaddr_t user_str;
unsigned int len;
unsigned char * str;
retval = sos_syscall_get2args(user_ctxt, & user_str, & len);
if (SOS_OK != retval)
break;
str = (unsigned char*)sos_kmalloc(len, 0);
if (str)
{
sos_bochs_printf("THR %p, Hexdump(0x%x, %d):\n",
sos_thread_get_current(), user_str, len);
retval = sos_memcpy_from_user((sos_vaddr_t) str, user_str, len);
sos_bochs_printf(" (Successfully copied %d out of %d)\n",
retval, len);
if (retval > 0)
sos_bochs_hexdump(str, retval);
sos_kfree((sos_vaddr_t)str);
}
else
retval = -SOS_ENOMEM;
}
break;
/**
* Syscall 4004: lists the VR of the current thread's address space
* args: debug_string, retval=ignored
*/
case 4004:
{
sos_uaddr_t ustr;
char * kstr;
struct sos_umem_vmm_as * my_as;
retval = sos_syscall_get1arg(user_ctxt, & ustr);
if (SOS_OK != retval)
break;
retval = sos_strndup_from_user(& kstr, ustr, 256, 0);
if (SOS_OK != retval)
break;
extern void sos_dump_as(const struct sos_umem_vmm_as *, const char *);
my_as
= sos_process_get_address_space(sos_thread_get_current()->process);
sos_dump_as(my_as, kstr);
sos_kfree((sos_vaddr_t)kstr);
}
break;
/**
* Syscall 4008: dump the list of processes in the system
* args: none, retval=ignored
*/
case 4008:
{
extern void sos_process_dumplist(void);
sos_process_dumplist();
retval = SOS_OK;
}
break;
default:
sos_bochs_printf("Syscall: UNKNOWN[%d]\n", syscall_id);
retval = -SOS_ENOSUP;
}
return retval;
}