summaryrefslogtreecommitdiffstats
path: root/pintos-progos/userprog/syscall.c
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/userprog/syscall.c')
-rw-r--r--pintos-progos/userprog/syscall.c563
1 files changed, 0 insertions, 563 deletions
diff --git a/pintos-progos/userprog/syscall.c b/pintos-progos/userprog/syscall.c
deleted file mode 100644
index f8e0197..0000000
--- a/pintos-progos/userprog/syscall.c
+++ /dev/null
@@ -1,563 +0,0 @@
1#include <stdio.h>
2#include <syscall-nr.h>
3#include "devices/input.h"
4#include "devices/shutdown.h"
5#include "filesys/file.h"
6#include "filesys/filesys.h"
7#include "filesys/inode.h"
8#include "lib/string.h"
9#include "threads/interrupt.h"
10#include "threads/palloc.h"
11#include "threads/synch.h"
12#include "threads/thread.h"
13#include "threads/vaddr.h"
14#include "userprog/pagedir.h"
15#include "userprog/process.h"
16#include "userprog/syscall.h"
17
18#define STACK_SLOT_SIZE sizeof(int)
19
20/* Prototypes for Utilities */
21static int get_user (const uint8_t *uaddr);
22static bool put_user (uint8_t *udst, uint8_t byte);
23static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes);
24static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes);
25static void* copy_string_arg (void *usp, bool *segfault);
26static void free_string_arg_buf (void *kbuf);
27
28/* Reads a byte at user virtual address UADDR.
29 UADDR must be below PHYS_BASE.
30 Returns the byte value if successful, -1 if a segfault
31 occurred. */
32static int
33get_user (const uint8_t *uaddr)
34{
35 int result;
36 asm ("movl $1f, %0; " /* save eip in eax */
37 "movzbl %1, %0; " /* read byte from user memory into eax */
38 "1:" /* continue here on page fault, with eax set to -1 */
39 : "=&a" (result) : "m" (*uaddr));
40 return result;
41}
42
43/* Writes BYTE to user address UDST.
44 UDST must be below PHYS_BASE.
45 Returns true if successful, false if a segfault occurred. */
46static bool
47put_user (uint8_t *udst, uint8_t byte)
48{
49 int error_code;
50 asm ("movl $1f, %0;" /* save EIP in EAX */
51 "movb %b2, %1;" /* write byte to user memory */
52 "1:" /* continue here on page fault, with eax set to -1 */
53 : "=&a" (error_code), "=m" (*udst) : "q" (byte));
54 return error_code != -1;
55}
56
57/* Copy bytes from user space; returns NULL if a segfault
58 occured, and kaddr otherwise */
59static void*
60memcpy_from_user (void *kaddr, void *uaddr, size_t bytes)
61{
62 uint8_t* kp = kaddr;
63 size_t i;
64 if (! is_user_vaddr (uaddr) ||
65 ! is_user_vaddr (uaddr + bytes - 1))
66 return false;
67 for (i=0;i<bytes;i++) {
68 int b = get_user (uaddr+i);
69 if (b < 0)
70 break; /* segfault */
71 else
72 kp[i] = b;
73 }
74 if (i != bytes)
75 return NULL; /* segfault */
76 return kaddr;
77}
78
79/* Copy bytes to user space; returns NULL if a segfault
80 occured, and uaddr otherwise */
81static void*
82memcpy_to_user (void *uaddr, void *kaddr, size_t bytes)
83{
84 uint8_t* kp = kaddr;
85 size_t i;
86 if (! is_user_vaddr (uaddr) ||
87 ! is_user_vaddr (uaddr + bytes - 1))
88 return false;
89 for (i=0;i<bytes;i++) {
90 if (! put_user (uaddr+i, kp[i]))
91 break; /* segfault */
92 }
93 if (i != bytes)
94 return NULL; /* segfault */
95 return uaddr;
96}
97
98/* Copy string (at most cnt bytes) from user memory to the kernel address
99 `kaddr`. The number of bytes copied is returned; 0 indicates a segfault. */
100static size_t
101strncpy_from_user (void *kaddr, void *uaddr, size_t cnt)
102{
103 uint8_t *kp = (uint8_t*) kaddr;
104 size_t i;
105 int c = 1;
106 for (i = 0; i < cnt && c; i++) {
107 if (! is_user_vaddr (uaddr+i))
108 return 0; /* sefault */
109 c = get_user (uaddr+i);
110 if (c < 0)
111 return 0; /* segfault */
112 kp[i] = c;
113 }
114 return i;
115}
116
117/* Copy a value of scalar type `typeof(*kdst)` from user space pointer
118 `usrc` to kernel space variable pointed to by `kdst`. Returns
119 false on segfault, and true otherwise. */
120#define copy_from_user(kdst, usrc) \
121 (memcpy_from_user (kdst, usrc, sizeof (*kdst)) != NULL)
122
123/* Takes user space stack pointer, which points to a string
124 in user space. Copies that string (at most PGSIZE bytes)
125 to a freshly allocated buffer in kernel space. Reurns NULL on
126 error; on a segfault, additionally sets `segfault` to true */
127static void*
128copy_string_arg (void *usp, bool *segfault)
129{
130 size_t bytes_copied;
131 char *uptr;
132 char *kpage;
133 if (! copy_from_user (&uptr, usp)) {
134 *segfault = true;
135 return NULL;
136 }
137 kpage = palloc_get_page (PAL_ZERO);
138 if (kpage == NULL)
139 return NULL;
140 bytes_copied = strncpy_from_user (kpage, uptr, PGSIZE);
141 if (bytes_copied == 0) {
142 *segfault = true;
143 palloc_free_page (kpage);
144 return NULL;
145 }
146 if (kpage[bytes_copied-1] != '\0') {
147 palloc_free_page (kpage);
148 return NULL;
149 };
150 return kpage;
151}
152
153/* free buffer allocated by `copy_string_arg` */
154static void free_string_arg_buf (void *kbuf)
155{
156 palloc_free_page (kbuf);
157}
158
159/* Stack slot address */
160#define STACK_ADDR(sp, slot) (sp + STACK_SLOT_SIZE*slot)
161
162/* syscall handler prototype */
163static void syscall_handler (struct intr_frame *);
164
165typedef int (handler)(void *sp, bool *segfault);
166
167/* Prototypes for syscall handlers */
168static handler
169 syscall_halt,
170 syscall_exit,
171 syscall_write,
172 syscall_wait ,
173 syscall_exec,
174 syscall_create,
175 syscall_remove,
176 syscall_open,
177 syscall_filesize,
178 syscall_read,
179 syscall_seek,
180 syscall_tell,
181 syscall_close;
182
183/* Register syscall_handler for interrupt 0x30 */
184void
185syscall_init (void)
186{
187 intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
188}
189
190/* Syscall handler, delegating known syscalls to the respective implementations */
191static void
192syscall_handler (struct intr_frame *f)
193{
194 int syscall_nr;
195 handler* fp;
196 bool segfault = false;
197 int result;
198 void *sp = f->esp;
199
200 /* The system call number and the arguments are on the stack */
201 if (! copy_from_user (&syscall_nr,sp))
202 goto fail;
203 switch (syscall_nr) {
204 case SYS_HALT: fp = syscall_halt; break;
205 case SYS_EXIT: fp = syscall_exit; break;
206 case SYS_EXEC: fp = syscall_exec; break;
207 case SYS_WAIT: fp = syscall_wait; break;
208 case SYS_CREATE: fp = syscall_create; break;
209 case SYS_REMOVE: fp = syscall_remove; break;
210 case SYS_OPEN: fp = syscall_open; break;
211 case SYS_FILESIZE: fp = syscall_filesize; break;
212 case SYS_READ: fp = syscall_read; break;
213 case SYS_WRITE: fp = syscall_write; break;
214 case SYS_SEEK: fp = syscall_seek; break;
215 case SYS_TELL: fp = syscall_tell; break;
216 case SYS_CLOSE: fp = syscall_close; break;
217 default:
218 goto fail;
219 }
220 result = fp (sp, &segfault);
221 if (segfault)
222 goto fail;
223 f->eax = result;
224 return;
225
226 fail:
227 process_current()->exit_status = -1;
228 thread_exit ();
229}
230
231/* Shutdown machine */
232static int
233syscall_halt (void *sp UNUSED, bool *segfault UNUSED)
234{
235 shutdown ();
236 NOT_REACHED ();
237}
238
239/* Exit current process with given exit code */
240static int
241syscall_exit (void *sp, bool *segfault)
242{
243 int exit_status;
244 if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) {
245 *segfault = true;
246 return -1;
247 }
248 process_current()->exit_status = exit_status;
249 thread_exit ();
250 NOT_REACHED ();
251}
252
253/* Spawn new process executing the supplied command */
254static int
255syscall_exec (void *sp, bool *segfault)
256{
257 char *kbuf;
258 int result = TID_ERROR;
259 if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) {
260 result = process_execute (kbuf);
261 free_string_arg_buf (kbuf);
262 }
263 return result;
264}
265
266/* Wait until specified process exits */
267static int
268syscall_wait (void *sp, bool *segfault)
269{
270 tid_t arg;
271 if (! copy_from_user (&arg, STACK_ADDR (sp,1))) {
272 *segfault = true;
273 return 0;
274 }
275 return process_wait (arg);
276}
277
278/* Create a new file with given initial size */
279static int
280syscall_create (void *sp, bool *segfault)
281{
282 bool success = false;
283 char *fname;
284 int initial_size;
285
286 if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) {
287 *segfault = true;
288 return false;
289 }
290 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
291 return false;
292
293 process_lock_filesys ();
294 success = filesys_create (fname, initial_size);
295 process_unlock_filesys ();
296 free_string_arg_buf (fname);
297 return success;
298}
299
300/* Remove name file, returns true if successful */
301static int
302syscall_remove (void *sp, bool *segfault)
303{
304 bool success;
305 char *fname;
306
307 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
308 return false;
309 process_lock_filesys ();
310 success = filesys_remove (fname);
311 process_unlock_filesys ();
312 free_string_arg_buf (fname);
313 return (int)success;
314}
315
316/* Open file, returning non-negative file descriptor if successful */
317static int
318syscall_open (void *sp, bool *segfault)
319{
320 char *fname;
321 int fd;
322 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
323 return false;
324 fd = process_open_file (fname);
325 free_string_arg_buf (fname);
326 return fd;
327}
328
329/* Return size of file described by file descriptor */
330static int
331syscall_filesize (void *sp, bool *segfault)
332{
333 int fd;
334 struct file *f;
335 int size;
336
337 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
338 *segfault = true;
339 return -1;
340 }
341 if ((f = process_get_file (fd)) == NULL)
342 return -1;
343 process_lock_filesys ();
344 size = inode_length (file_get_inode (f));
345 process_unlock_filesys ();
346 return size;
347}
348
349/* Read bytes from the file referenced by the given file
350 descriptor into the supplied user space buffer, returning
351 number of bytes read. */
352static int
353syscall_read (void *sp, bool *segfault)
354{
355 int fd;
356 uint8_t *user_buffer;
357 size_t size, bytes_to_read;
358
359 /* get arguments */
360 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
361 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
362 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
363 *segfault = true;
364 return -1;
365 }
366
367 /* ensure buffer is in user space */
368 if (! is_user_vaddr (user_buffer) ||
369 ! is_user_vaddr (user_buffer + size - 1)) {
370 *segfault = true;
371 return -1;
372 }
373
374 bytes_to_read = size;
375 /* handle stdin */
376 if (fd == STDIN_FILENO) {
377 char c;
378 while (bytes_to_read--) {
379 c = input_getc ();
380 if (! put_user (user_buffer++, c)) {
381 *segfault = true;
382 return -1;
383 }
384 }
385 return size;
386 }
387 /* get file */
388 struct file *file = process_get_file (fd);
389 if (file == NULL)
390 return -1;
391
392 char *kbuf = palloc_get_page (0);
393 if (kbuf == NULL)
394 return -1;
395
396 /* read loop */
397 do {
398 int bytes_read;
399 int blocksize = bytes_to_read;
400 if (bytes_to_read > PGSIZE)
401 blocksize = PGSIZE;
402
403 /* read bytes */
404 process_lock_filesys ();
405 bytes_read = file_read (file, kbuf, blocksize);
406 process_unlock_filesys ();
407
408 /* Stop when EOF has been reached */
409 if (bytes_read == 0)
410 break;
411 bytes_to_read -= bytes_read;
412 if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) {
413 *segfault = true;
414 break;
415 }
416 user_buffer += bytes_read;
417 } while (bytes_to_read > 0);
418
419 palloc_free_page (kbuf);
420 return size - bytes_to_read;
421}
422
423/* Write bytes from user buffer into the specified
424 file, returning number of bytes written. */
425static int
426syscall_write (void *sp, bool *segfault)
427{
428 int fd;
429 size_t size, bytes_to_write;
430 char *user_buffer;
431
432 /* get arguments */
433 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
434 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
435 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
436 *segfault = true;
437 return -1;
438 }
439
440 /* ensure buffer is in user space */
441 if (! is_user_vaddr (user_buffer) ||
442 ! is_user_vaddr (user_buffer + size - 1)) {
443 *segfault = true;
444 return -1;
445 }
446
447 /* get file handle */
448 struct file *file = NULL;
449 if (fd != STDOUT_FILENO) {
450 file = process_get_file (fd);
451 if (file == NULL)
452 return -1;
453 }
454
455 /* allocate kernel buffer */
456 char *kbuf = palloc_get_page (0);
457 if (kbuf == NULL)
458 return -1;
459
460 /* write loop */
461 bytes_to_write = size;
462 do {
463 int blocksize = bytes_to_write;
464 if (bytes_to_write > PGSIZE)
465 blocksize = PGSIZE;
466 if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) {
467 *segfault = true;
468 break;
469 }
470 if (fd == STDOUT_FILENO) {
471 putbuf (kbuf, blocksize);
472 bytes_to_write -= blocksize;
473 } else {
474 int bytes_written = 0;
475 int bytes_left_filesys = blocksize;
476
477 process_lock_filesys ();
478 while (bytes_left_filesys > 0) {
479 bytes_written = file_write (file, kbuf, bytes_left_filesys);
480 if (bytes_written <= 0) {
481 break;
482 }
483 bytes_left_filesys -= bytes_written;
484 }
485 process_unlock_filesys ();
486
487 if (bytes_written <= 0)
488 break;
489 bytes_to_write -= blocksize;
490 }
491 user_buffer += blocksize;
492 } while (bytes_to_write > 0);
493
494 /* return bytes written */
495 palloc_free_page (kbuf);
496 return size - bytes_to_write;
497}
498
499/* Change the position where the next byte will be read or written */
500static int
501syscall_seek (void *sp, bool *segfault)
502{
503 int fd;
504 off_t new_pos;
505
506 /* get arguments */
507 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
508 ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) {
509 *segfault = true;
510 return 0;
511 }
512
513 /* no way to return something sensible (void function) */
514 struct file *file = process_get_file (fd);
515 if (file == NULL)
516 return 0;
517
518 process_lock_filesys ();
519 file_seek (file, new_pos);
520 process_unlock_filesys ();
521 return 0;
522}
523
524/* Returns the position of the next byte to be read or written */
525static int
526syscall_tell (void *sp, bool *segfault)
527{
528 int fd;
529 unsigned r = 0;
530
531 /* get arguments */
532 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
533 *segfault = true;
534 return 0;
535 }
536
537 /* no way to return something sensible function */
538 struct file *file = process_get_file (fd);
539 if (file == NULL)
540 return 0;
541
542 process_lock_filesys ();
543 r = file_tell (file);
544 process_unlock_filesys ();
545 return r;
546}
547
548/* Close the given file */
549static int
550syscall_close (void *sp, bool *segfault)
551{
552 int fd;
553
554 /* get arguments */
555 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
556 *segfault = true;
557 return 0;
558 }
559
560 /* no way to return something sensible function (void) */
561 (void) process_close_file (fd);
562 return 0;
563}