Added fork, exec, wait
[akaros.git] / user / parlib / src / sparc / newlib_backend.c
1 /* See COPYRIGHT for copyright information. */
2 /* Andrew Waterman <waterman@eecs.bekeley.edu> */
3
4 #include <sys/fcntl.h>
5 #include <stdio.h>
6 #include <arch/arch.h>
7 #include <arch/frontend.h>
8 #include <parlib.h>
9 #include <sys/stat.h>
10 #include <sys/unistd.h>
11 #include <sys/times.h>
12 #include <sys/wait.h>
13 #include <sys/time.h>
14 #include <debug.h>
15 #include <hart.h>
16 #include <utime.h>
17 #include <dirent.h>
18 #include <assert.h>
19 #include <stdlib.h>
20
21 // should kernel do V->P translation on these args?
22 #define IN0  1
23 #define IN1  2
24 #define IN2  4
25 #define OUT0 8
26 #define OUT1 16
27 #define OUT2 32
28
29 #define fe(n,x,y,z,trans) syscall(SYS_frontend,RAMP_SYSCALL_ ## n,(int)(x),(int)(y),(int)(z),trans)
30
31 #define getbuf(name,len) \
32         assert(len <= PGSIZE); \
33         char name##_blah[2*PGSIZE] __attribute__((aligned(8))); \
34         char* name = (char*)(((uint32_t)name##_blah+PGSIZE)/PGSIZE*PGSIZE)
35         
36 #define memcpy_if_off_page(ptr,len) \
37         assert(len <= PGSIZE); \
38         char buf##ptr[2*PGSIZE] __attribute__((aligned(8))); \
39         if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
40         { \
41                 char* buf2##ptr = (char*)(((uint32_t)buf##ptr+PGSIZE)/PGSIZE*PGSIZE); \
42                 memcpy(buf2##ptr,ptr,len); \
43                 ptr = buf2##ptr; \
44         }
45
46 #define strcpy_if_off_page(ptr,len) \
47         assert(len <= PGSIZE); \
48         char buf##ptr[2*PGSIZE] __attribute__((aligned(8))); \
49         if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
50         { \
51                 char* buf2##ptr = (char*)(((uint32_t)buf##ptr+PGSIZE)/PGSIZE*PGSIZE); \
52                 strcpy(buf2##ptr,ptr); \
53                 ptr = buf2##ptr; \
54         }
55
56 #define buf_if_off_page(ptr,len) \
57         assert(len <= PGSIZE); \
58         char buf##ptr [2*PGSIZE] __attribute__((aligned(8))); \
59         char* buf2##ptr = (char*)(((uint32_t)buf##ptr+PGSIZE)/PGSIZE*PGSIZE); \
60         void* old##ptr = ptr; \
61         if((uint32_t)ptr % sizeof(uint32_t) != 0 || ((uint32_t)ptr)/PGSIZE != ((uint32_t)ptr+len)/PGSIZE) \
62         { \
63                 ptr = (typeof(ptr))(buf2##ptr); \
64         }
65
66 #define copyout_if_off_page(ptr,len) \
67         if((uint32_t)(old##ptr) % sizeof(uint32_t) != 0 || ((uint32_t)(old##ptr))/PGSIZE != ((uint32_t)(old##ptr)+len)/PGSIZE) \
68         { \
69                 memcpy(old##ptr,buf2##ptr,len); \
70         }
71
72 /* Return the vcoreid, which is set in entry.S right before calling libmain.
73  * This should only be used in libmain() and main(), before any code that might
74  * use a register.  It just returns eax. */
75 uint32_t newcore(void)
76 {
77         return hart_self();
78 }
79
80 mode_t
81 umask (mode_t mask)
82 {
83         return fe(umask,mask,0,0,0);
84 }
85
86 int
87 chmod (const char *name, mode_t mode)
88 {
89         int len = strlen(name)+1;
90         if(len > RAMP_MAXPATH)
91                 return -1;
92
93         strcpy_if_off_page(name,RAMP_MAXPATH);
94         return fe(chmod,name,mode,0,IN0);
95 }
96
97 int
98 access (const char *name, int mode)
99 {
100         int len = strlen(name)+1;
101         if(len > RAMP_MAXPATH)
102                 return -1;
103
104         strcpy_if_off_page(name,RAMP_MAXPATH);
105         return fe(access,name,mode,0,IN0);
106 }
107
108 char *
109 getwd (char *pwd)
110 {
111         buf_if_off_page(pwd,RAMP_MAXPATH);
112         int32_t ret = fe(getcwd,pwd,RAMP_MAXPATH,0,OUT0);
113         copyout_if_off_page(pwd,RAMP_MAXPATH);
114         return (char*)ret;
115 }
116
117 long int
118 pathconf (const char *pathname, int name)
119 {
120         int len = strlen(pathname)+1;
121         if(len > RAMP_MAXPATH)
122                 return -1;
123
124         assert(0);
125 }
126
127 int
128 utime (const char *name, const struct utimbuf *buf)
129 {
130         assert(sizeof(time_t) == sizeof(int));
131         time_t actime = buf == NULL ? time(NULL) : buf->actime;
132         time_t modtime = buf == NULL ? actime : buf->modtime;
133
134         int len = strlen(name)+1;
135         if(len > RAMP_MAXPATH)
136                 return -1;
137         strcpy_if_off_page(name,RAMP_MAXPATH);
138
139         return fe(utime,name,actime,modtime,IN0);
140 }
141
142 uid_t
143 getuid()
144 {
145         return 0;
146 }
147
148 uid_t
149 geteuid()
150 {
151         return 0;
152 }
153
154 gid_t
155 getgid()
156 {
157         return 0;
158 }
159
160 gid_t
161 getegid()
162 {
163         return 0;
164 }
165
166 int
167 chown (const char *name, uid_t owner, gid_t group)
168 {
169         int len = strlen(name)+1;
170         if(len > RAMP_MAXPATH)
171                 return -1;
172
173         assert(0);
174 }
175
176 int
177 mkdir (const char *name, mode_t mode)
178 {
179         int len = strlen(name)+1;
180         if(len > RAMP_MAXPATH)
181                 return -1;
182
183         assert(0);
184 }
185
186
187 int
188 rmdir (const char *name)
189 {
190         int len = strlen(name)+1;
191         if(len > RAMP_MAXPATH)
192                 return -1;
193
194         assert(0);
195 }
196
197 long int 
198 sysconf (int name)
199 {
200         switch(name)
201         {
202                 case _SC_CLK_TCK:
203                         return procinfo.tsc_freq;
204                 case _SC_PAGESIZE:
205                         return PGSIZE;
206                 case _SC_PHYS_PAGES:
207                         return 512*1024; // 2GB mem
208                 default:
209                         printf("sysconf(%d) not supported!\n",name);
210                         abort();
211         }
212 }
213
214 typedef struct
215 {
216         int fd;
217         struct dirent ent;
218 } __dir;
219
220 DIR *opendir (const char *name)
221 {
222         __dir* dir = (__dir*)malloc(sizeof(__dir));
223         if(dir == NULL)
224                 return NULL;
225
226         int len = strlen(name)+1;
227         if(len > RAMP_MAXPATH)
228                 return NULL;
229
230         strcpy_if_off_page(name,RAMP_MAXPATH);
231         dir->fd = fe(opendir,name,0,0,IN0);
232         if(dir->fd < 0)
233         {
234                 free(dir);
235                 return NULL;
236         }
237
238         return (DIR*)((char*)dir+1); // make dereferencing fail loudly
239 }
240
241 struct dirent *readdir (DIR *d)
242 {
243         __dir* dir = (__dir*)((char*)d-1);
244         struct dirent* dep = &dir->ent;
245
246         buf_if_off_page(dep,sizeof(struct dirent));
247         int ret = fe(readdir,dir->fd,dep,0,OUT1);
248         copyout_if_off_page(dep,sizeof(struct dirent));
249
250         return ret == 0 ? dep : 0;
251 }
252
253 void rewinddir (DIR *d)
254 {
255         __dir* dir = (__dir*)((char*)d-1);
256         fe(rewinddir,dir->fd,0,0,0);
257 }
258
259 int closedir (DIR *d)
260 {
261         __dir* dir = (__dir*)((char*)d-1);
262         int ret = fe(closedir,dir->fd,0,0,0);
263         if(ret == 0)
264                 free(dir);
265         return ret;
266 }
267
268 int pipe (int __fildes[2])
269 {
270         assert(0);
271 }
272
273 int dup (int __fildes)
274 {
275         return fe(dup,__fildes,0,0,0);
276 }
277
278 int dup2 (int __fildes, int __fildes2)
279 {
280         return fe(dup2,__fildes,__fildes2,0,0);
281 }
282
283 unsigned sleep (unsigned int __seconds)
284 {
285         assert(0);
286 }
287
288 unsigned alarm(unsigned __secs)
289 {
290         assert(0);
291 }
292
293 int execvp(const char *file, char * const argv[])
294 {
295         if(file[0] == '/')
296                 return execv(file,argv);
297
298         // this is technically incorrect, because we need to search PATH
299         const char* path = getenv("PATH");
300         if(path == NULL)
301                 path = ":/bin:/usr/bin";
302         char* buf = (char*)malloc((strlen(path)+strlen(file)+2)*sizeof(char));
303
304         char* dir = path;
305         while(1)
306         {
307                 char* end = strchr(dir,':');
308                 int len = end ? end-dir : strlen(dir);
309                 memcpy(buf,dir,len);
310                 if(len && buf[len-1] != '/')
311                         buf[len++] = '/';
312                 strcpy(buf+len,file);
313         
314                 if(access(buf,X_OK) == 0)
315                 {
316                         int ret = execv(buf,argv);
317                         free(buf);
318                         return ret;
319                 }
320
321                 if(!end)
322                         break;
323
324                 dir = end+1;
325         }
326
327         free(buf);
328         errno = ENOENT;
329         return -1;
330 }
331
332 int execv(const char *path, char *const argv[])
333 {
334         return execve(path,argv,environ);
335 }
336
337 int fcntl (int fd, int cmd, ...)
338 {
339         va_list vl;
340         va_start(vl,cmd);
341         int arg = va_arg(vl,int);
342         va_end(vl);
343
344         switch(cmd)
345         {
346                 case F_DUPFD:
347                 case F_GETFD:
348                 case F_SETFD:
349                         return fe(fcntl,fd,cmd,arg,0);
350                 default:
351                         printf("fcntl(%d,%d) not supported!\n",fd,cmd);
352                         abort();
353         }
354 }
355
356 int chdir(const char *name)
357 {
358         size_t len = strlen(name)+1;
359         if(len > RAMP_MAXPATH)
360                 return -1;
361
362         strcpy_if_off_page(name,RAMP_MAXPATH);
363         return fe(chdir,name,0,0,IN0);
364 }
365
366 int
367 getppid(void)
368 {
369         return procinfo.ppid;
370 }
371
372 int
373 getpid(void)
374 {
375         return procinfo.pid;
376 }
377
378 void
379 _exit(int code)
380 {
381         sys_proc_destroy(getpid(),code);
382         while(1);
383 }
384
385 int
386 isatty(int fd)
387 {
388         struct stat s;
389         int ret = fstat(fd,&s);
390         return ret < 0 ? -1 : ((s.st_mode & S_IFCHR) ? 1 : 0);
391 }
392
393 static hart_lock_t child_lock = HART_LOCK_INIT;
394 static int* child_list = NULL;
395 static int child_list_capacity = 0;
396 static int child_list_size = 0;
397
398 int
399 fork(void)
400 {
401         hart_lock_lock(&child_lock);
402         if(child_list_size == child_list_capacity)
403         {
404                 child_list_capacity++;
405                 int* tmp = realloc(child_list,child_list_capacity*sizeof(int));
406                 if(tmp == NULL)
407                 {
408                         child_list_capacity--;
409                         errno = ENOMEM;
410                         hart_lock_unlock(&child_lock);
411                         return -1;
412                 }
413                 child_list = tmp;
414         }
415
416         int ret = syscall(SYS_fork,0,0,0,0,0);
417
418         if(ret > 0)
419                 child_list[child_list_size++] = ret;
420
421         hart_lock_unlock(&child_lock);
422         return ret;
423 }
424
425 static int
426 pack_argv(const char* const argv[], char* buf, size_t bufsz)
427 {
428         int argc = 0, size = sizeof(intreg_t);
429         while(argv[argc])
430         {
431                 size += sizeof(intreg_t)+strlen(argv[argc])+1;
432                 argc++;
433         }
434
435         if(size > bufsz)
436                 return -1;
437
438         intreg_t* offset = (intreg_t*)buf;
439         offset[0] = (argc+1)*sizeof(intreg_t);
440         for(int i = 0; i < argc; i++)
441         {
442                 int len = strlen(argv[i])+1;
443                 memcpy(buf+offset[i],argv[i],len);
444                 offset[i+1] = offset[i]+len;
445         }
446         offset[argc] = 0;
447
448         return 0;
449 }
450
451 static int
452 readfile(const char* filename, void** binary, int* size)
453 {
454         int fd = open(filename,O_RDONLY,0);
455         if(fd == -1)
456                 return -1;
457
458         *size = 0;
459         *binary = NULL;
460         int bytes_read = 0;
461         int bufsz = 0;
462
463         int READ_SIZE = 1024;
464         int MALLOC_SIZE = 1024*1024;
465
466         while(1)
467         {
468                 if(*size+READ_SIZE > bufsz)
469                 {
470                         void* temp_buf = realloc(*binary,bufsz+MALLOC_SIZE);
471                         if(temp_buf == NULL)
472                         {
473                                 close(fd);
474                                 free(*binary);
475                                 errno = ENOMEM;
476                                 return -1;
477                         }
478
479                         *binary = temp_buf;
480                         bufsz += MALLOC_SIZE;
481                 }
482
483                 bytes_read = read(fd, *binary+*size, READ_SIZE);
484                 *size += bytes_read;
485                 if(bytes_read <= 0)
486                 {
487                         close(fd);
488                         if(bytes_read < 0)
489                                 free(*binary);
490                         return bytes_read;
491                 }
492         }
493 }
494
495 int
496 execve(const char* name, char* const argv[], char* const env[])
497 {
498         char argv_buf[PROCINFO_MAX_ARGV_SIZE],env_buf[PROCINFO_MAX_ENV_SIZE];
499         if(pack_argv(argv,argv_buf,PROCINFO_MAX_ARGV_SIZE) ||
500            pack_argv(env,env_buf,PROCINFO_MAX_ENV_SIZE))
501         {
502                 errno = ENOMEM;
503                 return -1;
504         }
505
506         void* binary;
507         size_t binarysz;
508         if(readfile(name,&binary,&binarysz))
509                 return -1;
510
511         return syscall(SYS_exec,(intreg_t)binary,(intreg_t)binarysz,
512                        (intreg_t)argv_buf,(intreg_t)env_buf,0);
513 }
514
515 int
516 kill(int pid, int sig)
517 {
518         int ret = sys_proc_destroy(pid,0);
519         return ret < 0 ? -1 : ret;
520 }
521
522 int
523 waitpid(int pid, int* status, int options)
524 {
525         assert(options == 0);
526
527         int foo;
528         if(status == NULL)
529                 status = &foo;
530
531         hart_lock_lock(&child_lock);
532
533         if(child_list_size) while(1)
534         {
535                 for(int i = 0; i < child_list_size; i++)
536                 {
537                         if(pid == -1 || child_list[i] == pid)
538                         {
539                                 int ret = syscall(SYS_trywait,child_list[i],status,0,0,0);
540
541                                 if(ret == 0)
542                                 {
543                                         for(int j = i+1; j < child_list_size; j++)
544                                                 child_list[j-1] = child_list[j];
545                                         child_list_size--;
546                                         hart_lock_unlock(&child_lock);
547                                         return 0;
548                                 }
549                         }
550                 }
551                 sys_yield();
552         }
553
554         hart_lock_unlock(&child_lock);
555         errno = ECHILD;
556         return -1;
557 }
558
559 int
560 wait(int* status)
561 {
562         return waitpid(-1,status,0);
563 }
564
565 int
566 link(const char *old, const char *new)
567 {
568         int oldlen = strlen(old)+1, newlen = strlen(new)+1;
569         if(oldlen > RAMP_MAXPATH || newlen > RAMP_MAXPATH)
570                 return -1;
571
572         strcpy_if_off_page(old,RAMP_MAXPATH);
573         strcpy_if_off_page(new,RAMP_MAXPATH);
574         return fe(link,old,new,0,IN0 | IN1);
575 }
576
577 int
578 unlink(const char* name)
579 {
580         int len = strlen(name)+1;
581         if(len > RAMP_MAXPATH)
582                 return -1;
583
584         strcpy_if_off_page(name,RAMP_MAXPATH);
585         return fe(unlink,name,0,0,IN0);
586 }
587
588 int
589 fstat(int fd, struct stat* st)
590 {
591         buf_if_off_page(st,sizeof(*st));
592         int ret = fe(fstat,fd,st,0,OUT1);
593         copyout_if_off_page(st,sizeof(*st));
594         return ret;
595 }
596
597 int
598 lstat(const char* name, struct stat* st)
599 {
600         int len = strlen(name)+1;
601         if(len > RAMP_MAXPATH)
602                 return -1;
603
604         strcpy_if_off_page(name,RAMP_MAXPATH);
605         buf_if_off_page(st,sizeof(*st));
606         int ret = fe(lstat,name,st,0,IN0 | OUT1);
607         copyout_if_off_page(st,sizeof(*st));
608         return ret;
609 }
610
611 int
612 stat(const char* name, struct stat* st)
613 {
614         int len = strlen(name)+1;
615         if(len > RAMP_MAXPATH)
616                 return -1;
617
618         strcpy_if_off_page(name,RAMP_MAXPATH);
619         buf_if_off_page(st,sizeof(*st));
620         int ret = fe(stat,name,st,0,IN0 | OUT1);
621         copyout_if_off_page(st,sizeof(*st));
622         return ret;
623 }
624
625 off_t
626 lseek(int fd, off_t ptr, int dir)
627 {
628         return fe(lseek,fd,ptr,dir,0);
629 }
630
631 ssize_t
632 write(int fd, const void* ptr, size_t len)
633 {
634         len = MIN(PGSIZE,len);
635         memcpy_if_off_page(ptr,len);
636         return fe(write,fd,ptr,len,IN1);
637 }
638
639 ssize_t
640 read(int fd, void* ptr, size_t len)
641 {
642         len = MIN(PGSIZE,len);
643         buf_if_off_page(ptr,len);
644         int ret = fe(read,fd,ptr,len,OUT1);
645         copyout_if_off_page(ptr,len);
646         return ret;
647 }
648
649 int
650 open(const char* name, int flags, ...)
651 {
652         va_list vl;
653         va_start(vl,flags);
654         int mode = va_arg(vl,int);
655         va_end(vl);
656
657         size_t len = strlen(name)+1;
658         if(len > RAMP_MAXPATH)
659                 return -1;
660
661         strcpy_if_off_page(name,RAMP_MAXPATH);
662         return fe(open,name,flags,mode,IN0);
663 }
664
665 int
666 close(int fd)
667 {
668         return fe(close,fd,0,0,0);
669 }
670
671 clock_t
672 times(struct tms* buf)
673 {
674         extern struct timeval timeval_start;
675         if(timeval_start.tv_sec == 0)
676                 return (clock_t)-1;
677
678         struct timeval tp;
679         if(gettimeofday(&tp,NULL))
680                 return (clock_t)-1;
681
682         unsigned long long utime = (tp.tv_sec - timeval_start.tv_sec)*1000000;
683         utime += tp.tv_usec-timeval_start.tv_usec;
684         buf->tms_utime = buf->tms_cutime = utime*procinfo.tsc_freq/1000000;
685         buf->tms_stime = buf->tms_cstime = 0;
686
687         return (clock_t)buf->tms_utime;
688 }
689
690 int
691 gettimeofday(struct timeval* tp, void* tzp)
692 {
693         static struct timeval tp0 __attribute__((aligned(sizeof(*tp))));
694         if(tp0.tv_sec == 0)
695         {
696                 int ret = fe(gettimeofday,&tp0,0,0,OUT0);
697                 if(ret)
698                         return ret;
699         }
700
701         long long dt = read_tsc();
702         tp->tv_sec = tp0.tv_sec + dt/procinfo.tsc_freq;
703         tp->tv_usec = (dt % procinfo.tsc_freq)*1000000/procinfo.tsc_freq;
704
705         return 0;
706 }
707