552baffd844f6a94f7b93dcc0b29b6785a564eef
[akaros.git] / user / parlib / src / i386 / newlib_backend.c
1 /* See COPYRIGHT for copyright information. */
2
3 /** @file
4  * @brief Backend file for newlib functionality
5  *
6  * This file is responsible for defining the syscalls needed by newlib to 
7  * impliment the newlib library
8  * *
9  * @author Paul Pearce <pearce@eecs.berkeley.edu>
10  * @author Kevin Klues <klueska@cs.berkeley.edu>
11  *
12  */
13
14 #include <parlib.h>
15 #include <unistd.h>
16 #include <newlib_backend.h>
17 #include <string.h>
18 #include <malloc.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <debug.h>
22 #include <sys/times.h>
23 #include <assert.h>
24 #include <dirent.h>
25
26 /* These are stubs for newlib syscalls that 
27    exist on the sparc platform, but not x86.
28
29    Paul TODO: Ship or do locally?
30 */
31
32 DIR *opendir (const char *name) { assert(0); }
33 struct dirent *readdir (DIR *d) { assert(0); }
34 void rewinddir (DIR *d) { assert(0); }
35 int closedir (DIR *d) { assert(0); }
36 int access (const char *name, int mode) { return 0; }
37 int gettimeofday(struct timeval* tp, void* tzp) { return 0; }
38
39 /* environ
40  * A pointer to a list of environment variables and their values. 
41  * For a minimal environment, this empty list is adequate.
42  */
43 /* These are defined inside of newlib.
44 char *__env[1] = { 0 };
45 char **environ = __env;
46 */
47
48 /* _exit()
49  * Exit a program without cleaning up files. 
50  * If your system doesn't provide this, it is best to avoid linking 
51  * with subroutines that require it (exit, system).
52  */
53 void _exit(int __status)
54 {
55         sys_proc_destroy(sys_getpid(),__status); // TODO: can run getpid and cache it
56         while(1); //Should never get here...
57 }
58     
59 /* close()
60  * Close a file. 
61  */
62 int close(int file) {
63         close_subheader_t msg;
64         debug_in_out("CLOSE\n");
65
66         // If trying to close stdin/out/err just return
67         if ((file == STDIN_FILENO) || (file == STDERR_FILENO) 
68                                    || (file == STDOUT_FILENO))
69                 return 0;
70         
71         msg.id = CLOSE_ID;
72         msg.fd = file;
73         
74         // Send message
75         response_t *result = send_message((char*)&msg, sizeof(close_subheader_t),CLOSE_ID);
76
77         int return_val;
78
79         if (result == NULL) {
80                 errno = ECHANNEL;
81                 return_val = -1;
82         } else {
83                 // Read result
84                 return_val = result->ret;
85                 if (return_val == -1) errno = result->err;
86                 free(result);
87         }
88         
89         return return_val;
90 }
91
92 /* execve()
93  * Transfer control to a new process. 
94  * Minimal implementation (for a system without processes).
95  */
96
97 int execve(const char *name, char * const argv[], char * const env[]) 
98 {
99         debug_in_out("EXECVE\n");
100         errno = ENOMEM;
101         return -1;
102 }
103
104 /* fork()
105  * Create a new process. 
106  * Minimal implementation (for a system without processes).
107  */
108 pid_t fork(void) 
109 {
110         debug_in_out("FORK\n");
111         errno = EAGAIN;
112     return -1;
113 }
114
115 /* fstat()
116  * Status of an open file. 
117  * For consistency with other minimal implementations in these stubs, 
118  * all files are regarded as character special devices. 
119  * The sys/stat.h msg file required is distributed in the include 
120  * subdirectory for the newlib C library.
121  */
122 int fstat(int file, struct stat *st) 
123 {
124         fstat_subheader_t msg;
125         debug_in_out("FSTAT\n");        
126
127         // Kevin added this. I believe its needed for the serial branch
128         // Not used with the stdout hack. Review for removing stdout hack.
129         //st->st_mode = S_IFCHR;
130         
131         // stdout hack
132         if (file == 1) {
133                 st->st_mode = 8592;
134                 return 0;
135         }
136         
137         msg.id = FSTAT_ID;
138         msg.fd = file;
139
140         // Send message
141         response_t *result = send_message((char*)&msg, sizeof(fstat_subheader_t),FSTAT_ID);
142
143         // Read result
144         int return_val;
145
146         if (result == NULL) {
147                 errno = ECHANNEL;
148                 return_val = -1;
149         } else {
150                 return_val = result->ret;
151                 if (return_val == -1)
152                         errno = result->err;
153                 else
154                         memcpy(st, result->buf, sizeof(struct stat));
155                         
156                 free(result);
157         }
158
159         return return_val;
160 }
161
162 /* getpid()
163  * Process-ID; this is sometimes used to generate strings unlikely to 
164  * conflict with other processes. Minimal implementation, for a system 
165  * without processes.
166  */
167 pid_t getpid(void) 
168 {
169         return sys_getpid(); // TODO: can run getpid and cache it
170 }
171
172 /* isatty()
173  * Query whether output stream is a terminal. 
174  * For consistency with the other minimal implementations, 
175  * which only support output to stdout, this minimal 
176  * implementation is suggested.
177  */
178 int isatty(int file) 
179 {
180         isatty_subheader_t msg;
181         debug_in_out("ISATTY\n");
182
183         // Cheap hack to avoid sending serial comm for stuff we know
184         if ((STDIN_FILENO == file) || (STDOUT_FILENO == file) 
185                                || (STDERR_FILENO == file))
186                 return 1;
187         
188         msg.id = ISATTY_ID;
189         msg.fd = file;
190
191         // Send message
192         response_t *result = send_message((char*)&msg, sizeof(isatty_subheader_t),ISATTY_ID);
193
194         int return_val; 
195
196         // Note: Ret val of 0 defined to be an error, not < 0. Go figure.
197         if (result == NULL) {
198                 errno = ECHANNEL;
199                 return_val = 0;
200         } else {
201                 // Read result
202                 return_val = result->ret;
203                 if (return_val == 0) errno = result->err;
204                 free(result);
205         } 
206         
207         return return_val;
208 }
209
210 /* kill()
211  * Send a signal. 
212  * Minimal implementation.
213  */
214 int kill(int pid, int sig) 
215 {
216         debug_in_out("KILL\n");
217         errno = EINVAL;
218     return -1;
219 }
220
221 /* link()
222  * Establish a new name for an existing file. 
223  * Minimal implementation.
224  */
225 int link(const char *old, const char *new) 
226 {
227         link_subheader_t *msg;
228         response_t *result;
229         debug_in_out("LINK\n");
230         
231         int s_len_old = strlen(old) + 1; // Null terminator
232         int s_len_new = strlen(new) + 1; // Null terminator
233
234         msg = malloc(sizeof(link_subheader_t) + s_len_old + s_len_new);
235         if (msg == NULL)
236                 return -1;
237         
238         msg->id = LINK_ID;
239         msg->old_len = s_len_old;
240         msg->new_len = s_len_new;
241         msg->total_len = s_len_old + s_len_new;
242         
243         memcpy(msg->buf, old, s_len_old - 1);
244         msg->buf[s_len_old - 1] = '\0';
245         memcpy(msg->buf + s_len_old, new, s_len_new - 1);
246         msg->buf[s_len_old + s_len_new - 1] = '\0';
247
248         // Send message
249         result =
250                 send_message((char *CT(sizeof(link_subheader_t)+s_len_old+s_len_new))TC(msg),
251                          sizeof(link_subheader_t) + msg->total_len,LINK_ID);
252
253         free(msg);
254
255         // Read result
256         int return_val;
257
258         if (result == NULL) {
259                 errno = ECHANNEL;
260                 return_val = -1;
261         } else {
262                 return_val = result->ret;
263                 if (return_val == -1) errno = result->err;
264                 free(result);
265         }
266
267         return return_val;
268 }
269
270 /* lseek()
271  * Set position in a file. 
272  * Minimal implementation.
273  */
274 off_t lseek(int file, off_t ptr, int dir) 
275 {
276         lseek_subheader_t msg;
277         debug_in_out("LSEEK\n");        
278         
279         msg.id = LSEEK_ID;
280         msg.fd = file;
281         msg.ptr = ptr;
282         msg.dir = dir;
283
284         // Send message
285         response_t *result = send_message((char*)&msg, sizeof(lseek_subheader_t),LSEEK_ID);
286
287         int return_val;
288
289         if (result == NULL) {
290                 errno = ECHANNEL;
291                 return_val = -1;
292         } else {
293                 // Read result
294                 return_val = result->ret;
295                 if (return_val == -1) errno = result->err;
296                 free(result);
297         }
298
299         return return_val;
300 }
301
302 /* open()
303  * Open a file. 
304  */
305 int open(const char *name, int flags, int mode) 
306 {
307         open_subheader_t *msg;
308         debug_in_out("OPEN\n");
309
310         int s_len = strlen(name) + 1; // Null terminator
311
312         msg = malloc(sizeof(open_subheader_t) + s_len);
313         if (msg == NULL)
314                 return -1;
315
316         msg->id = OPEN_ID;
317         msg->flags = flags;
318         msg->mode = mode;
319         msg->len = s_len;
320
321         memcpy(msg->buf, name, s_len - 1);
322         msg->buf[s_len - 1] = '\0';
323
324         // Send message
325         response_t *result =
326                 send_message((char *CT(sizeof(open_subheader_t)+s_len))TC(msg),
327                          sizeof(open_subheader_t) + s_len,OPEN_ID);
328
329         free(msg);
330
331         // Read result
332         int return_val;
333
334         if (result == NULL) {
335                 errno = ECHANNEL;
336                 return_val = -1;
337         } else {
338                 return_val = result->ret;
339                 if (return_val == -1) errno = result->err;
340                 free(result);
341         } 
342
343         return return_val;
344 }
345
346 /* read()
347  * Read from a file. 
348  */
349 ssize_t read(int file, void *ptr, size_t len) 
350 {
351         read_subheader_t msg;
352         uint8_t *CT(len) _ptr = ptr;
353         debug_in_out("READ\n");
354
355         // Console hack.
356         if (file == STDIN_FILENO) {
357                 for(int i=0; i<len; i++)        
358                         _ptr[i] = (uint8_t)sys_cgetc();
359                 return len;
360         }
361         
362         msg.id = READ_ID;
363         msg.fd = file;
364         msg.len = len;
365         
366         // Send message
367         response_t *result = send_message((char*)&msg, sizeof(read_subheader_t),READ_ID);
368
369         // Read result
370         int return_val;
371
372         if (result == NULL) {
373                 errno = ECHANNEL;
374                 return_val = -1;
375         } else {
376                 return_val = result->ret;
377                 if (return_val < 0)
378                         errno = result->err;
379                 else
380                         memcpy(_ptr, result->buf, return_val);
381
382                 free(result);
383         }
384
385         return return_val;
386 }
387
388 /* Read len bytes from the given channel to the buffer.
389  * If peek is NO_PEEK, will wait indefinitely until that much data is read.
390  * If peek is PEEK, if no data is available, will return immediately.
391  *              However once some data is available, 
392  *                      will block until the entire amount is available.
393  */
394 int read_from_channel(char * buf, int len, int peek)
395 {
396         // TODO: NEED TO IMPLIMENT A TIMEOUT
397         //                      Also, watch out for CONNECTION TERMINATED
398         int total_read = 0;
399
400         #ifdef __NETWORK__
401                 int just_read = sys_eth_read(buf, len);
402         #else
403                 int just_read = sys_serial_read(buf, len);
404         #endif
405
406         if (just_read < 0) return just_read;
407         if (just_read == 0 && peek) return just_read;
408
409         total_read += just_read;
410
411         while (total_read != len) {
412                 
413                 #ifdef __NETWORK__
414                         just_read = sys_eth_read(buf + total_read, len - total_read);
415                 #else
416                         just_read = sys_serial_read(buf + total_read, len - total_read);
417                 #endif
418         
419                 if (just_read == -1) return -1;
420                 total_read += just_read;
421         }
422
423         return total_read;
424 }
425
426 int read_response_from_channel(response_t *response) 
427 {
428         return read_from_channel(       (char*CT(sizeof(response_t))) TC(response), 
429                                                 sizeof(response_t), 
430                                                                 NO_PEEK);
431 }
432
433 int read_buffer_from_channel(char *buf, int len) 
434 {
435         return read_from_channel(       buf, 
436                                                 len, 
437                                                                 NO_PEEK);
438 }
439
440 /* send_message()
441  * Write the message in buffer out on the channel, and wait for a response.
442  * Caller is responsible for management of buffer passed in and buffer returned.
443  */
444 response_t *send_message(char *msg, int len, syscall_id_t this_call_id)
445 {
446         if (write_to_channel(msg, len) != len)
447                 return NULL;
448
449         int ret_value, buffer_size;
450         
451         response_t temp_response;
452         response_t *return_response = NULL;
453
454         // Pull the response from the server out of the channel.
455         if (read_response_from_channel( &temp_response ) == -1) 
456                 return NULL;
457
458         ret_value = temp_response.ret;
459         
460         char* buffer = NULL;
461
462         if (   this_call_id == READ_ID 
463             || this_call_id == FSTAT_ID 
464             || this_call_id == STAT_ID)
465         {
466                 if (ret_value < 0)
467                         buffer_size = 0;
468                 else {
469                         if(this_call_id == READ_ID) 
470                                 buffer_size = ret_value;
471                         else
472                                 buffer_size = sizeof(struct stat);
473                 }
474                         
475                 return_response = malloc(sizeof(response_t) + buffer_size);
476
477                 if (return_response == NULL)
478                         return NULL;
479                 
480                 if (read_buffer_from_channel(return_response->buf, buffer_size) == -1) {
481                         free(return_response);
482                         return NULL;
483                 }
484         } 
485         else 
486         {
487                         
488                 return_response = malloc(sizeof(response_t));
489                 
490                 if (return_response == NULL)
491                         return NULL;
492         }
493
494         *return_response = temp_response;
495
496         return return_response;
497 }
498
499
500 /* stat()
501  * Status of a file (by name). 
502  * Minimal implementation.
503  */
504 int stat(const char *file, struct stat *st) 
505 {
506         stat_subheader_t *msg;
507         debug_in_out("STAT\n");
508         
509         int s_len = strlen(file) + 1; // Null terminator
510
511         // Allocate a new buffer of proper size and pack
512         msg = malloc(sizeof(stat_subheader_t) + s_len);
513         if (msg == NULL)
514                 return -1;
515         
516         msg->id = STAT_ID;
517         msg->len = s_len;
518
519         memcpy(msg->buf, file, s_len - 1);
520         msg->buf[s_len - 1] = '\0';
521
522         // Send message
523         response_t *result =
524                 send_message((char *CT(sizeof(stat_subheader_t) + s_len))TC(msg),
525                              sizeof(stat_subheader_t) + s_len, STAT_ID);
526
527         free(msg);
528
529         // Read result
530         int return_val;
531
532         if (result == NULL) {
533                 errno = ECHANNEL;
534                 return_val = -1;
535         } else {
536                 return_val = result->ret;
537
538                 if (return_val == -1)
539                         errno = result->err;
540                 else
541                         memcpy(st, result->buf, sizeof(struct stat));
542
543                 free(result);
544
545         }
546
547         return return_val;
548 }
549
550 /* times()
551  * Timing information for current process. 
552  * Minimal implementation.
553  */
554 clock_t times(struct tms *buf) 
555 {
556         debug_in_out("TIMES");
557         return -1;
558 }
559
560 /* unlink()
561  * Remove a file's directory entry. 
562  * Minimal implementation.
563  */
564 int unlink(const char *name) 
565 {
566         unlink_subheader_t *msg;
567         debug_in_out("UNLINK\n");
568         
569         int s_len = strlen(name) + 1; // Null terminator
570
571         // Allocate a new buffer of proper size and pack
572         msg = malloc(sizeof(unlink_subheader_t) + s_len);
573         if (msg == NULL)
574                 return -1;
575         
576         msg->id = UNLINK_ID;
577         msg->len = s_len;
578         
579         memcpy(msg->buf, name, s_len - 1);
580         msg->buf[s_len - 1] = '\0';
581
582         // Send message
583         response_t *result =
584                 send_message((char *CT(sizeof(unlink_subheader_t) + s_len))TC(msg),
585                              sizeof(unlink_subheader_t) + s_len,UNLINK_ID);
586
587         free(msg);
588
589         // Read result
590         int return_val;
591
592         if (result == NULL) {
593                 errno = ECHANNEL;
594                 return_val = -1;
595         } else {
596                 return_val = result->ret;
597                 if (return_val == -1) errno = result->err;
598                 free(result);
599         } 
600         
601         return return_val;
602 }
603
604 /* wait()
605  * Wait for a child process. 
606  * Minimal implementation.
607  */
608 int wait(int *status) 
609 {
610         debug_in_out("WAIT\n");
611         errno = ECHILD;
612         return -1;
613 }
614
615 /* write()
616  * Write to a file. 
617  */
618 ssize_t write(int file, const void *ptr, size_t len)
619 {
620         write_subheader_t *msg;
621         debug_in_out("WRITE\n");        
622         debug_in_out("\tFILE: %u\n", file);
623
624         debug_write_check("Writing len: %d\n", len);
625
626         if ((file == STDIN_FILENO) || (file == STDERR_FILENO) 
627                                    || (file == STDOUT_FILENO))
628                 return sys_cputs(ptr, len);
629         
630         // Allocate a new buffer of proper size and pack
631         msg = malloc(sizeof(write_subheader_t) + len);
632         if (msg == NULL)
633                 return -1;
634         
635         msg->id = WRITE_ID;
636         msg->fd = file;
637         msg->len = len;
638
639         memcpy(msg->buf, ptr, len);
640
641         // Send message
642         response_t *result =
643                 send_message((char *CT(sizeof(write_subheader_t) + len))TC(msg),
644                              sizeof(write_subheader_t) + len,WRITE_ID);
645
646         free(msg);
647
648         // Read result
649         int return_val;
650
651         if (result == NULL) {
652                 errno = ECHANNEL;
653                 return_val = -1;
654         } else {
655                 return_val = result->ret;
656                 if (return_val == -1) errno = result->err;
657                 free(result);
658         } 
659
660         return return_val;
661 }
662
663
664 /* write_to_channel()
665  * Send a message out over the channel, defined by msg, of length len
666  */
667 int write_to_channel(char * msg, int len)
668 {
669         #ifdef __NETWORK__
670                 return sys_eth_write(msg, len);
671         #else
672                 return sys_serial_write(msg, len);
673         #endif
674 }