sos-code-article10/userland/crt.c
2018-07-13 17:13:10 +02:00

664 lines
14 KiB
C

/* 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 <hwcore/swintr.h>
#include <string.h>
#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);
}