/* 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 #include #include #include #include #include #include #include #include #include #include #include #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; }