Added more syscalls
[akaros.git] / user / parlib / src / sparc / newlib_backend.c
index ee47d2a..a50a076 100644 (file)
@@ -1,12 +1,15 @@
 /* See COPYRIGHT for copyright information. */
 /* Andrew Waterman <waterman@eecs.bekeley.edu> */
 
+#include <sys/fcntl.h>
+#include <stdio.h>
 #include <arch/arch.h>
 #include <arch/frontend.h>
 #include <parlib.h>
 #include <sys/stat.h>
 #include <sys/unistd.h>
 #include <sys/times.h>
+#include <sys/wait.h>
 #include <sys/time.h>
 #include <debug.h>
 #include <hart.h>
@@ -30,7 +33,7 @@
        char name##_blah[2*PGSIZE] __attribute__((aligned(8))); \
        char* name = (char*)(((uint32_t)name##_blah+PGSIZE)/PGSIZE*PGSIZE)
        
-#define copy_if_off_page(ptr,len) \
+#define memcpy_if_off_page(ptr,len) \
        assert(len <= PGSIZE); \
        char buf##ptr[2*PGSIZE] __attribute__((aligned(8))); \
        if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
                ptr = buf2##ptr; \
        }
 
+#define strcpy_if_off_page(ptr,len) \
+       assert(len <= PGSIZE); \
+       char buf##ptr[2*PGSIZE] __attribute__((aligned(8))); \
+       if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
+       { \
+               char* buf2##ptr = (char*)(((uint32_t)buf##ptr+PGSIZE)/PGSIZE*PGSIZE); \
+               strcpy(buf2##ptr,ptr); \
+               ptr = buf2##ptr; \
+       }
+
 #define buf_if_off_page(ptr,len) \
        assert(len <= PGSIZE); \
-       char buf##ptr [2*PGSIZE]; \
+       char buf##ptr [2*PGSIZE] __attribute__((aligned(8))); \
        char* buf2##ptr = (char*)(((uint32_t)buf##ptr+PGSIZE)/PGSIZE*PGSIZE); \
        void* old##ptr = ptr; \
        if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
@@ -51,7 +64,7 @@
        }
 
 #define copyout_if_off_page(ptr,len) \
-       if((uint32_t)(old##ptr) % sizeof(uint32_t) != 0 || ((uint32_t)(old##ptr))/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
+       if((uint32_t)(old##ptr) % sizeof(uint32_t) != 0 || ((uint32_t)(old##ptr))/PGSIZE != ((uint32_t)(old##ptr)+len)/PGSIZE) \
        { \
                memcpy(old##ptr,buf2##ptr,len); \
        }
@@ -67,7 +80,7 @@ uint32_t newcore(void)
 mode_t
 umask (mode_t mask)
 {
-       assert(0);
+       return fe(umask,mask,0,0,0);
 }
 
 int
@@ -77,7 +90,8 @@ chmod (const char *name, mode_t mode)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       assert(0);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
+       return fe(chmod,name,mode,0,IN0);
 }
 
 int
@@ -87,7 +101,8 @@ access (const char *name, int mode)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       assert(0);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
+       return fe(access,name,mode,0,IN0);
 }
 
 char *
@@ -112,11 +127,40 @@ pathconf (const char *pathname, int name)
 int
 utime (const char *name, const struct utimbuf *buf)
 {
+       assert(sizeof(time_t) == sizeof(int));
+       time_t actime = buf == NULL ? time(NULL) : buf->actime;
+       time_t modtime = buf == NULL ? actime : buf->modtime;
+
        int len = strlen(name)+1;
        if(len > RAMP_MAXPATH)
                return -1;
+       strcpy_if_off_page(name,RAMP_MAXPATH);
 
-       assert(0);
+       return fe(utime,name,actime,modtime,IN0);
+}
+
+uid_t
+getuid()
+{
+       return 0;
+}
+
+uid_t
+geteuid()
+{
+       return 0;
+}
+
+gid_t
+getgid()
+{
+       return 0;
+}
+
+gid_t
+getegid()
+{
+       return 0;
 }
 
 int
@@ -153,7 +197,18 @@ rmdir (const char *name)
 long int 
 sysconf (int name)
 {
-       assert(0);
+       switch(name)
+       {
+               case _SC_CLK_TCK:
+                       return procinfo.tsc_freq;
+               case _SC_PAGESIZE:
+                       return PGSIZE;
+               case _SC_PHYS_PAGES:
+                       return 512*1024; // 2GB mem
+               default:
+                       printf("sysconf(%d) not supported!\n",name);
+                       abort();
+       }
 }
 
 typedef struct
@@ -172,7 +227,7 @@ DIR *opendir (const char *name)
        if(len > RAMP_MAXPATH)
                return NULL;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        dir->fd = fe(opendir,name,0,0,IN0);
        if(dir->fd < 0)
        {
@@ -215,6 +270,11 @@ int pipe (int __fildes[2])
        assert(0);
 }
 
+int dup (int __fildes)
+{
+       return fe(dup,__fildes,0,0,0);
+}
+
 int dup2 (int __fildes, int __fildes2)
 {
        return fe(dup2,__fildes,__fildes2,0,0);
@@ -230,19 +290,67 @@ unsigned alarm(unsigned __secs)
        assert(0);
 }
 
-int execvp(const char *__file, char * const __argv[])
+int execvp(const char *file, char * const argv[])
 {
-       assert(0);
+       if(file[0] == '/')
+               return execv(file,argv);
+
+       // this is technically incorrect, because we need to search PATH
+       const char* path = getenv("PATH");
+       if(path == NULL)
+               path = ":/bin:/usr/bin";
+       char* buf = (char*)malloc((strlen(path)+strlen(file)+2)*sizeof(char));
+
+       char* dir = path;
+       while(1)
+       {
+               char* end = strchr(dir,':');
+               int len = end ? end-dir : strlen(dir);
+               memcpy(buf,dir,len);
+               if(len && buf[len-1] != '/')
+                       buf[len++] = '/';
+               strcpy(buf+len,file);
+       
+               if(access(buf,X_OK) == 0)
+               {
+                       int ret = execv(buf,argv);
+                       free(buf);
+                       return ret;
+               }
+
+               if(!end)
+                       break;
+
+               dir = end+1;
+       }
+
+       free(buf);
+       errno = ENOENT;
+       return -1;
 }
 
 int execv(const char *path, char *const argv[])
 {
-       assert(0);
+       return execve(path,argv,environ);
 }
 
-int fcntl (int fd, int cmd, ...) 
+int fcntl (int fd, int cmd, ...)
 {
-       assert(0);
+       va_list vl;
+       va_start(vl,cmd);
+       int arg = va_arg(vl,int);
+       va_end(vl);
+
+       switch(cmd)
+       {
+               case F_DUPFD:
+               case F_GETFD:
+               case F_SETFD:
+                       return fe(fcntl,fd,cmd,arg,0);
+               default:
+                       printf("fcntl(%d,%d) not supported!\n",fd,cmd);
+                       abort();
+       }
 }
 
 int chdir(const char *name)
@@ -251,11 +359,18 @@ int chdir(const char *name)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        return fe(chdir,name,0,0,IN0);
 }
 
 int
+getppid(void)
+{
+       assert(0);
+//     return procinfo.ppid;
+}
+
+int
 getpid(void)
 {
        return procinfo.pid;
@@ -264,7 +379,7 @@ getpid(void)
 void
 _exit(int code)
 {
-       sys_proc_destroy(getpid());
+       sys_proc_destroy(getpid(),code);
        while(1);
 }
 
@@ -276,28 +391,176 @@ isatty(int fd)
        return ret < 0 ? -1 : ((s.st_mode & S_IFCHR) ? 1 : 0);
 }
 
+static hart_lock_t child_lock = HART_LOCK_INIT;
+static int* child_list = NULL;
+static int child_list_capacity = 0;
+static int child_list_size = 0;
+
 int
 fork(void)
 {
-       assert(0);
+       hart_lock_lock(&child_lock);
+       if(child_list_size == child_list_capacity)
+       {
+               child_list_capacity++;
+               int* tmp = realloc(child_list,child_list_capacity*sizeof(int));
+               if(tmp == NULL)
+               {
+                       child_list_capacity--;
+                       errno = ENOMEM;
+                       hart_lock_unlock(&child_lock);
+                       return -1;
+               }
+               child_list = tmp;
+       }
+
+       int ret = syscall(SYS_fork,0,0,0,0,0);
+
+       if(ret > 0)
+               child_list[child_list_size++] = ret;
+
+       hart_lock_unlock(&child_lock);
+       return ret;
+}
+
+static int
+pack_argv(const char* const argv[], char* buf, size_t bufsz)
+{
+       int argc = 0, size = sizeof(intreg_t);
+       while(argv[argc])
+       {
+               size += sizeof(intreg_t)+strlen(argv[argc])+1;
+               argc++;
+       }
+
+       if(size > bufsz)
+               return -1;
+
+       intreg_t* offset = (intreg_t*)buf;
+       offset[0] = (argc+1)*sizeof(intreg_t);
+       for(int i = 0; i < argc; i++)
+       {
+               int len = strlen(argv[i])+1;
+               memcpy(buf+offset[i],argv[i],len);
+               offset[i+1] = offset[i]+len;
+       }
+       offset[argc] = 0;
+
+       return 0;
+}
+
+static int
+readfile(const char* filename, void** binary, int* size)
+{
+       int fd = open(filename,O_RDONLY,0);
+       if(fd == -1)
+               return -1;
+
+       *size = 0;
+       *binary = NULL;
+       int bytes_read = 0;
+       int bufsz = 0;
+
+       int READ_SIZE = 1024;
+       int MALLOC_SIZE = 1024*1024;
+
+       while(1)
+       {
+               if(*size+READ_SIZE > bufsz)
+               {
+                       void* temp_buf = realloc(*binary,bufsz+MALLOC_SIZE);
+                       if(temp_buf == NULL)
+                       {
+                               close(fd);
+                               free(*binary);
+                               errno = ENOMEM;
+                               return -1;
+                       }
+
+                       *binary = temp_buf;
+                       bufsz += MALLOC_SIZE;
+               }
+
+               bytes_read = read(fd, *binary+*size, READ_SIZE);
+               *size += bytes_read;
+               if(bytes_read <= 0)
+               {
+                       close(fd);
+                       if(bytes_read < 0)
+                               free(*binary);
+                       return bytes_read;
+               }
+       }
 }
 
 int
 execve(const char* name, char* const argv[], char* const env[])
 {
-       assert(0);
+       char argv_buf[PROCINFO_MAX_ARGV_SIZE],env_buf[PROCINFO_MAX_ENV_SIZE];
+       if(pack_argv(argv,argv_buf,PROCINFO_MAX_ARGV_SIZE) ||
+          pack_argv(env,env_buf,PROCINFO_MAX_ENV_SIZE))
+       {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       void* binary;
+       size_t binarysz;
+       if(readfile(name,&binary,&binarysz))
+               return -1;
+
+       return syscall(SYS_exec,(intreg_t)binary,(intreg_t)binarysz,
+                       (intreg_t)argv_buf,(intreg_t)env_buf,0);
 }
 
 int
 kill(int pid, int sig)
 {
-       assert(0);
+       int ret = sys_proc_destroy(pid,0);
+       return ret < 0 ? -1 : ret;
+}
+
+int
+waitpid(int pid, int* status, int options)
+{
+       assert(options == 0);
+
+       int foo;
+       if(status == NULL)
+               status = &foo;
+
+       hart_lock_lock(&child_lock);
+
+       if(child_list_size) while(1)
+       {
+               for(int i = 0; i < child_list_size; i++)
+               {
+                       if(pid == -1 || child_list[i] == pid)
+                       {
+                               int ret = syscall(SYS_trywait,child_list[i],status,0,0,0);
+
+                               if(ret == 0)
+                               {
+                                       for(int j = i+1; j < child_list_size; j++)
+                                               child_list[j-1] = child_list[j];
+                                       child_list_size--;
+                                       hart_lock_unlock(&child_lock);
+                                       return 0;
+                               }
+                       }
+               }
+               sys_yield();
+       }
+
+       hart_lock_unlock(&child_lock);
+       errno = ECHILD;
+       return -1;
 }
 
 int
 wait(int* status)
 {
-       assert(0);
+       return waitpid(-1,status,0);
 }
 
 int
@@ -307,8 +570,8 @@ link(const char *old, const char *new)
        if(oldlen > RAMP_MAXPATH || newlen > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(old,oldlen);
-       copy_if_off_page(new,oldlen);
+       strcpy_if_off_page(old,RAMP_MAXPATH);
+       strcpy_if_off_page(new,RAMP_MAXPATH);
        return fe(link,old,new,0,IN0 | IN1);
 }
 
@@ -319,7 +582,7 @@ unlink(const char* name)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        return fe(unlink,name,0,0,IN0);
 }
 
@@ -339,7 +602,7 @@ lstat(const char* name, struct stat* st)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        buf_if_off_page(st,sizeof(*st));
        int ret = fe(lstat,name,st,0,IN0 | OUT1);
        copyout_if_off_page(st,sizeof(*st));
@@ -353,7 +616,7 @@ stat(const char* name, struct stat* st)
        if(len > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        buf_if_off_page(st,sizeof(*st));
        int ret = fe(stat,name,st,0,IN0 | OUT1);
        copyout_if_off_page(st,sizeof(*st));
@@ -370,7 +633,7 @@ ssize_t
 write(int fd, const void* ptr, size_t len)
 {
        len = MIN(PGSIZE,len);
-       copy_if_off_page(ptr,len);
+       memcpy_if_off_page(ptr,len);
        return fe(write,fd,ptr,len,IN1);
 }
 
@@ -385,13 +648,18 @@ read(int fd, void* ptr, size_t len)
 }
 
 int
-open(char* name, int flags, int mode)
+open(const char* name, int flags, ...)
 {
+       va_list vl;
+       va_start(vl,flags);
+       int mode = va_arg(vl,int);
+       va_end(vl);
+
        size_t len = strlen(name)+1;
        if(len > RAMP_MAXPATH)
                return -1;
 
-       copy_if_off_page(name,len);
+       strcpy_if_off_page(name,RAMP_MAXPATH);
        return fe(open,name,flags,mode,IN0);
 }
 
@@ -412,9 +680,9 @@ times(struct tms* buf)
        if(gettimeofday(&tp,NULL))
                return (clock_t)-1;
 
-       long long utime = (tp.tv_sec - timeval_start.tv_sec)*1000000;
+       unsigned long long utime = (tp.tv_sec - timeval_start.tv_sec)*1000000;
        utime += tp.tv_usec-timeval_start.tv_usec;
-       buf->tms_utime = buf->tms_cutime = utime*CLK_TCK/1000000;
+       buf->tms_utime = buf->tms_cutime = utime*procinfo.tsc_freq/1000000;
        buf->tms_stime = buf->tms_cstime = 0;
 
        return (clock_t)buf->tms_utime;