/* Copyright (C) 2005 David Decotigny Copyright (C) 2003 Thomas Petazzoni 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. */ /** * @file crt.c * * The C RunTime environment for the basic support of SOS C user * programs */ #include #include #include "libc.h" /* putenv */ #include "crt.h" /** * Macro used to retrieve the 2 parameters passed to any new thread * (architecture-dependent) */ #define GET_THREAD_PARAMETERS(param1, param2) \ asm volatile ("movl %%eax, %0 ; movl %%ebx, %1" \ : "=g"(param1), "=g"(param2) : : "%eax", "%ebx" ) /** * Helper function to retrieve the arg array from the parameter area * sent by the parent. * * Format of the parent's args area: * Number of arguments * Offset of arg 0 (program's name) * Offset of arg 1 * ... * Offset of arg N (last argument) * NULL * Offset of environnement variable 0 * Offset of environnement variable 1 * ... * Offset of environnement variable N * NULL * Contents of arg 0 with terminal \0 * Contents of arg 1 with terminal \0 * ... * Contents of arg N with terminal \0 * Contents of env 0 with terminal \0 * Contents of env 1 with terminal \0 * ... * Contents of env N with terminal \0 * * This function simply transforms the N offsets into the real * addresses of the arg X contents (ie it simply adds the base address * of the area). */ static void unmarshall_argv_envp(void * args, int * argc, const char ** * argv, const char ** * envp) { addr_t * offset = (addr_t*) args; *argc = 0; *argv = NULL; offset = args; /* Get argc */ *argc = * (int*) offset; offset ++; /* Get base of argv */ *argv = (const char **) offset; /* Walk the offsets array and transform these offsets into memory addresses */ for ( ; 0 != *offset ; offset ++) *offset += (addr_t) args; /* Skip the NULL separating argv from envp */ offset ++; /* Get base of envp */ *envp = (const char **) offset; /* Walk the offsets array and transform these offsets into memory addresses */ for ( ; 0 != *offset ; offset ++) { *offset += (addr_t) args; /* Register this environment variable */ putenv((char*) *offset); } } /** * Starter function ! */ /** Function called by crt_asm.S:_start to start user program */ void _cmain(const char* arg_env_area[]) __attribute__((noreturn)); void _cmain(const char* arg_env_area[]) { /* Will hold the parameters that will be passed to main */ const char** argv; const char** envp; int argc; /* This starter function expects a main() function somewhere */ extern int main(int argc, char const* argv[], char const* envp[]); /* Setup the arguments list and the environment variables */ unmarshall_argv_envp (arg_env_area, & argc, & argv, & envp); _sos_exit(main(argc, argv, envp)); } /* * By convention, the USER SOS programs always pass 4 arguments to the * kernel syscall handler: in eax/../edx. For less arguments, the * unused registers are filled with 0s. For more arguments, the 4th * syscall parameter gives the address of the array containing the * remaining arguments. In any case, eax corresponds to the syscall * IDentifier. */ inline int _sos_syscall3(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3) { int ret; asm volatile("movl %1,%%eax \n" "movl %2,%%ebx \n" "movl %3,%%ecx \n" "movl %4,%%edx \n" "int %5\n" "movl %%eax, %0" :"=g"(ret) :"g"(id),"g"(arg1),"g"(arg2),"g"(arg3) ,"i"(SOS_SWINTR_SOS_SYSCALL) :"eax","ebx","ecx","edx","memory"); return ret; } int _sos_syscall0(int id) { return _sos_syscall3(id, 0, 0, 0); } int _sos_syscall1(int id, unsigned int arg1) { return _sos_syscall3(id, arg1, 0, 0); } int _sos_syscall2(int id, unsigned int arg1, unsigned int arg2) { return _sos_syscall3(id, arg1, arg2, 0); } int _sos_syscall4(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4) { unsigned int args[] = { arg3, arg4 }; return _sos_syscall3(id, arg1, arg2, (unsigned)args); } int _sos_syscall5(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5) { unsigned int args[] = { arg3, arg4, arg5 }; return _sos_syscall3(id, arg1, arg2, (unsigned)args); } int _sos_syscall6(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5, unsigned int arg6) { unsigned int args[] = { arg3, arg4, arg5, arg6 }; return _sos_syscall3(id, arg1, arg2, (unsigned)args); } int _sos_syscall7(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5, unsigned int arg6, unsigned int arg7) { unsigned int args[] = { arg3, arg4, arg5, arg6, arg7 }; return _sos_syscall3(id, arg1, arg2, (unsigned)args); } int _sos_syscall8(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5, unsigned int arg6, unsigned int arg7, unsigned int arg8) { unsigned int args[] = { arg3, arg4, arg5, arg6, arg7, arg8 }; return _sos_syscall3(id, arg1, arg2, (unsigned)args); } void _sos_exit(int status) { _sos_syscall1(SOS_SYSCALL_ID_EXIT, (unsigned)status); /* Never reached ! */ for ( ; ; ) ; } int _sos_bochs_write(const char * str, unsigned length) { return _sos_syscall2(SOS_SYSCALL_ID_BOCHS_WRITE, (unsigned)str, length); } int _sos_fork() { return _sos_syscall0(SOS_SYSCALL_ID_FORK); } int _sos_getpid() { return _sos_syscall0(SOS_SYSCALL_ID_GETPID); } int _sos_exec(const char * prog, void const* args, size_t arglen) { return _sos_syscall4(SOS_SYSCALL_ID_EXEC, (unsigned int)prog, (unsigned int)strlen(prog), (unsigned int)args, (unsigned int)arglen); } int _sos_munmap(void * start, size_t length) { return _sos_syscall2(SOS_SYSCALL_ID_MUNMAP, (unsigned int)start, length); } int _sos_mprotect(const void *addr, size_t len, int prot) { return _sos_syscall3(SOS_SYSCALL_ID_MPROTECT, (unsigned int)addr, len, (unsigned int)prot); } int _sos_mresize(void * old_addr, size_t old_len, void * *new_addr, size_t new_len, unsigned long flags) { return _sos_syscall5(SOS_SYSCALL_ID_MRESIZE, (unsigned int)old_addr, old_len, (unsigned int)new_addr, new_len, flags); } int _sos_msync(void *start, size_t length, int flags) { return _sos_syscall3(SOS_SYSCALL_ID_MSYNC, (unsigned int)start, length, flags); } /** * Helper function that represents the start routine of a new user * thread created from user space (syscall new_thread). It takes 2 * arguments that are passsed in the eax/ebx registers (@see * cpu_ustate_init() in hwcore/cpu_context.c): the start function of * the new thread (eax), the argument passed to it (ebx). */ static void thread_routine(void) { sos_thread_func_t * func; unsigned long int arg; GET_THREAD_PARAMETERS(func, arg); func(arg); _sos_exit(0); } int _sos_new_thread(sos_thread_func_t *func, void* arg, size_t stack_size) { return _sos_syscall4(SOS_SYSCALL_ID_NEW_THREAD, (unsigned)thread_routine, (unsigned)func, (unsigned)arg, stack_size); } int _sos_nanosleep(unsigned long int sec, unsigned long int nanosec) { return _sos_syscall2(SOS_SYSCALL_ID_NANOSLEEP, sec, nanosec); } void * _sos_brk(void * new_top_address) { return (void*)_sos_syscall1(SOS_SYSCALL_ID_BRK, (unsigned)new_top_address); } int _sos_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const char *args) { if (!target || !filesystemtype) return -1; return _sos_syscall7(SOS_SYSCALL_ID_MOUNT, (unsigned int)source, source?strlen(source):0, (unsigned int)target, strlen(target), (unsigned int)filesystemtype, mountflags, (unsigned int)args); } int _sos_umount(const char *target) { if (!target) return -1; return _sos_syscall2(SOS_SYSCALL_ID_UMOUNT, (unsigned int)target, strlen(target)); } void _sos_sync(void) { _sos_syscall0(SOS_SYSCALL_ID_SYNC); } int _sos_statvfs(const char *path, struct statvfs *buf) { if (! path) return -1; return _sos_syscall3(SOS_SYSCALL_ID_VFSTAT64, (unsigned)path, strlen(path), (unsigned)buf); } int _sos_open(const char * pathname, int flags, int mode) { if (! pathname) return -1; return _sos_syscall4(SOS_SYSCALL_ID_OPEN, (unsigned)pathname, strlen(pathname), flags, mode); } int _sos_close(int fd) { return _sos_syscall1(SOS_SYSCALL_ID_CLOSE, fd); } int _sos_read(int fd, char * buf, size_t * len) { return _sos_syscall3(SOS_SYSCALL_ID_READ, fd, (unsigned int) buf, (unsigned int) len); } int _sos_write(int fd, const char * buf, size_t * len) { return _sos_syscall3(SOS_SYSCALL_ID_WRITE, fd, (unsigned int) buf, (unsigned int) len); } int _sos_seek64(int fd, loff_t * offset, int whence) { return _sos_syscall3(SOS_SYSCALL_ID_SEEK64, fd, (unsigned int)offset, (unsigned int)whence); } int _sos_fmmap(void ** ptr_hint_addr, size_t len, int prot, int flags, int fd, loff_t offset) { return _sos_syscall7(SOS_SYSCALL_ID_FSMMAP, (unsigned int)ptr_hint_addr, len, prot, flags, (unsigned int)fd, /* offs64_hi */(offset >> 32), /* offs64_lo */(offset & 0xffffffff)); } int _sos_ftruncate64(int fd, loff_t length) { return _sos_syscall2(SOS_SYSCALL_ID_FTRUNCATE64, fd, (unsigned int)length); } int _sos_fcntl(int fd, int cmd, int arg) { return _sos_syscall3(SOS_SYSCALL_ID_FCNTL, fd, (unsigned int)cmd, (unsigned int)arg); } int _sos_ioctl(int fd, int cmd, int arg) { return _sos_syscall3(SOS_SYSCALL_ID_IOCTL, fd, (unsigned int)cmd, (unsigned int)arg); } int _sos_creat(const char *pathname, int mode) { if (! pathname) return -1; return _sos_syscall3(SOS_SYSCALL_ID_CREAT, (unsigned int)pathname, strlen(pathname), mode); } int _sos_link (const char *oldpath, const char *newpath) { if (!oldpath || !newpath) return -1; return _sos_syscall4(SOS_SYSCALL_ID_LINK, (unsigned int)oldpath, strlen(oldpath), (unsigned int)newpath, strlen(newpath)); } int _sos_unlink(const char *pathname) { if (! pathname) return -1; return _sos_syscall2(SOS_SYSCALL_ID_UNLINK, (unsigned int)pathname, strlen(pathname)); } int _sos_rename (const char *oldpath, const char *newpath) { if (!oldpath || !newpath) return -1; return _sos_syscall4(SOS_SYSCALL_ID_RENAME, (unsigned int)oldpath, strlen(oldpath), (unsigned int)newpath, strlen(newpath)); } int _sos_symlink(const char *target, const char *path) { if (!path || !target) return -1; return _sos_syscall4(SOS_SYSCALL_ID_SYMLINK, (unsigned int)path, strlen(path), (unsigned int)target, strlen(target)); } int _sos_mknod(const char *pathname, mode_t mode, int type, unsigned int major, unsigned minor) { if (!pathname) return -1; return _sos_syscall6(SOS_SYSCALL_ID_MKNOD, (unsigned int)pathname, strlen(pathname), type, mode, major, minor); } struct dirent; /* Forward declaration */ int _sos_readdir(int fd, struct dirent * dirent) { return _sos_syscall2(SOS_SYSCALL_ID_READDIR, fd, (unsigned int)dirent); } int _sos_mkdir(const char *pathname, mode_t mode) { if (!pathname) return -1; return _sos_syscall3(SOS_SYSCALL_ID_MKDIR, (unsigned int)pathname, strlen(pathname), mode); } int _sos_rmdir(const char *pathname) { if (!pathname) return -1; return _sos_syscall2(SOS_SYSCALL_ID_RMDIR, (unsigned int)pathname, strlen(pathname)); } int _sos_chmod(const char *pathname, mode_t mode) { if (!pathname) return -1; return _sos_syscall3(SOS_SYSCALL_ID_CHMOD, (unsigned int)pathname, strlen(pathname), mode); } int _sos_stat(const char *pathname, int nofollow, struct stat * st) { if (!pathname || !st) return -1; return _sos_syscall4(SOS_SYSCALL_ID_STAT64, (unsigned int)pathname, strlen(pathname), nofollow, (unsigned int)st); } int _sos_chroot(const char *dirname) { if (!dirname) return -1; return _sos_syscall2(SOS_SYSCALL_ID_CHROOT, (unsigned int)dirname, strlen(dirname)); } int _sos_chdir(const char *dirname) { if (!dirname) return -1; return _sos_syscall2(SOS_SYSCALL_ID_CHDIR, (unsigned int)dirname, strlen(dirname)); } int _sos_fchdir(int fd) { return _sos_syscall1(SOS_SYSCALL_ID_FCHDIR, (unsigned int)fd); }