From b5f0874cd96ee2a62aabc645b9626c2749cb6a01 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 26 Mar 2012 12:54:45 +0200 Subject: initial pintos checkin --- pintos-progos/userprog/syscall.c | 563 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 pintos-progos/userprog/syscall.c (limited to 'pintos-progos/userprog/syscall.c') diff --git a/pintos-progos/userprog/syscall.c b/pintos-progos/userprog/syscall.c new file mode 100644 index 0000000..f8e0197 --- /dev/null +++ b/pintos-progos/userprog/syscall.c @@ -0,0 +1,563 @@ +#include +#include +#include "devices/input.h" +#include "devices/shutdown.h" +#include "filesys/file.h" +#include "filesys/filesys.h" +#include "filesys/inode.h" +#include "lib/string.h" +#include "threads/interrupt.h" +#include "threads/palloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "threads/vaddr.h" +#include "userprog/pagedir.h" +#include "userprog/process.h" +#include "userprog/syscall.h" + +#define STACK_SLOT_SIZE sizeof(int) + +/* Prototypes for Utilities */ +static int get_user (const uint8_t *uaddr); +static bool put_user (uint8_t *udst, uint8_t byte); +static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes); +static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes); +static void* copy_string_arg (void *usp, bool *segfault); +static void free_string_arg_buf (void *kbuf); + +/* Reads a byte at user virtual address UADDR. + UADDR must be below PHYS_BASE. + Returns the byte value if successful, -1 if a segfault + occurred. */ +static int +get_user (const uint8_t *uaddr) +{ + int result; + asm ("movl $1f, %0; " /* save eip in eax */ + "movzbl %1, %0; " /* read byte from user memory into eax */ + "1:" /* continue here on page fault, with eax set to -1 */ + : "=&a" (result) : "m" (*uaddr)); + return result; +} + +/* Writes BYTE to user address UDST. + UDST must be below PHYS_BASE. + Returns true if successful, false if a segfault occurred. */ +static bool +put_user (uint8_t *udst, uint8_t byte) +{ + int error_code; + asm ("movl $1f, %0;" /* save EIP in EAX */ + "movb %b2, %1;" /* write byte to user memory */ + "1:" /* continue here on page fault, with eax set to -1 */ + : "=&a" (error_code), "=m" (*udst) : "q" (byte)); + return error_code != -1; +} + +/* Copy bytes from user space; returns NULL if a segfault + occured, and kaddr otherwise */ +static void* +memcpy_from_user (void *kaddr, void *uaddr, size_t bytes) +{ + uint8_t* kp = kaddr; + size_t i; + if (! is_user_vaddr (uaddr) || + ! is_user_vaddr (uaddr + bytes - 1)) + return false; + for (i=0;iesp; + + /* The system call number and the arguments are on the stack */ + if (! copy_from_user (&syscall_nr,sp)) + goto fail; + switch (syscall_nr) { + case SYS_HALT: fp = syscall_halt; break; + case SYS_EXIT: fp = syscall_exit; break; + case SYS_EXEC: fp = syscall_exec; break; + case SYS_WAIT: fp = syscall_wait; break; + case SYS_CREATE: fp = syscall_create; break; + case SYS_REMOVE: fp = syscall_remove; break; + case SYS_OPEN: fp = syscall_open; break; + case SYS_FILESIZE: fp = syscall_filesize; break; + case SYS_READ: fp = syscall_read; break; + case SYS_WRITE: fp = syscall_write; break; + case SYS_SEEK: fp = syscall_seek; break; + case SYS_TELL: fp = syscall_tell; break; + case SYS_CLOSE: fp = syscall_close; break; + default: + goto fail; + } + result = fp (sp, &segfault); + if (segfault) + goto fail; + f->eax = result; + return; + + fail: + process_current()->exit_status = -1; + thread_exit (); +} + +/* Shutdown machine */ +static int +syscall_halt (void *sp UNUSED, bool *segfault UNUSED) +{ + shutdown (); + NOT_REACHED (); +} + +/* Exit current process with given exit code */ +static int +syscall_exit (void *sp, bool *segfault) +{ + int exit_status; + if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) { + *segfault = true; + return -1; + } + process_current()->exit_status = exit_status; + thread_exit (); + NOT_REACHED (); +} + +/* Spawn new process executing the supplied command */ +static int +syscall_exec (void *sp, bool *segfault) +{ + char *kbuf; + int result = TID_ERROR; + if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) { + result = process_execute (kbuf); + free_string_arg_buf (kbuf); + } + return result; +} + +/* Wait until specified process exits */ +static int +syscall_wait (void *sp, bool *segfault) +{ + tid_t arg; + if (! copy_from_user (&arg, STACK_ADDR (sp,1))) { + *segfault = true; + return 0; + } + return process_wait (arg); +} + +/* Create a new file with given initial size */ +static int +syscall_create (void *sp, bool *segfault) +{ + bool success = false; + char *fname; + int initial_size; + + if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) { + *segfault = true; + return false; + } + if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) + return false; + + process_lock_filesys (); + success = filesys_create (fname, initial_size); + process_unlock_filesys (); + free_string_arg_buf (fname); + return success; +} + +/* Remove name file, returns true if successful */ +static int +syscall_remove (void *sp, bool *segfault) +{ + bool success; + char *fname; + + if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) + return false; + process_lock_filesys (); + success = filesys_remove (fname); + process_unlock_filesys (); + free_string_arg_buf (fname); + return (int)success; +} + +/* Open file, returning non-negative file descriptor if successful */ +static int +syscall_open (void *sp, bool *segfault) +{ + char *fname; + int fd; + if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) + return false; + fd = process_open_file (fname); + free_string_arg_buf (fname); + return fd; +} + +/* Return size of file described by file descriptor */ +static int +syscall_filesize (void *sp, bool *segfault) +{ + int fd; + struct file *f; + int size; + + if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { + *segfault = true; + return -1; + } + if ((f = process_get_file (fd)) == NULL) + return -1; + process_lock_filesys (); + size = inode_length (file_get_inode (f)); + process_unlock_filesys (); + return size; +} + +/* Read bytes from the file referenced by the given file + descriptor into the supplied user space buffer, returning + number of bytes read. */ +static int +syscall_read (void *sp, bool *segfault) +{ + int fd; + uint8_t *user_buffer; + size_t size, bytes_to_read; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || + ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || + ! copy_from_user (&size, STACK_ADDR (sp,3))) { + *segfault = true; + return -1; + } + + /* ensure buffer is in user space */ + if (! is_user_vaddr (user_buffer) || + ! is_user_vaddr (user_buffer + size - 1)) { + *segfault = true; + return -1; + } + + bytes_to_read = size; + /* handle stdin */ + if (fd == STDIN_FILENO) { + char c; + while (bytes_to_read--) { + c = input_getc (); + if (! put_user (user_buffer++, c)) { + *segfault = true; + return -1; + } + } + return size; + } + /* get file */ + struct file *file = process_get_file (fd); + if (file == NULL) + return -1; + + char *kbuf = palloc_get_page (0); + if (kbuf == NULL) + return -1; + + /* read loop */ + do { + int bytes_read; + int blocksize = bytes_to_read; + if (bytes_to_read > PGSIZE) + blocksize = PGSIZE; + + /* read bytes */ + process_lock_filesys (); + bytes_read = file_read (file, kbuf, blocksize); + process_unlock_filesys (); + + /* Stop when EOF has been reached */ + if (bytes_read == 0) + break; + bytes_to_read -= bytes_read; + if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) { + *segfault = true; + break; + } + user_buffer += bytes_read; + } while (bytes_to_read > 0); + + palloc_free_page (kbuf); + return size - bytes_to_read; +} + +/* Write bytes from user buffer into the specified + file, returning number of bytes written. */ +static int +syscall_write (void *sp, bool *segfault) +{ + int fd; + size_t size, bytes_to_write; + char *user_buffer; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || + ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || + ! copy_from_user (&size, STACK_ADDR (sp,3))) { + *segfault = true; + return -1; + } + + /* ensure buffer is in user space */ + if (! is_user_vaddr (user_buffer) || + ! is_user_vaddr (user_buffer + size - 1)) { + *segfault = true; + return -1; + } + + /* get file handle */ + struct file *file = NULL; + if (fd != STDOUT_FILENO) { + file = process_get_file (fd); + if (file == NULL) + return -1; + } + + /* allocate kernel buffer */ + char *kbuf = palloc_get_page (0); + if (kbuf == NULL) + return -1; + + /* write loop */ + bytes_to_write = size; + do { + int blocksize = bytes_to_write; + if (bytes_to_write > PGSIZE) + blocksize = PGSIZE; + if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) { + *segfault = true; + break; + } + if (fd == STDOUT_FILENO) { + putbuf (kbuf, blocksize); + bytes_to_write -= blocksize; + } else { + int bytes_written = 0; + int bytes_left_filesys = blocksize; + + process_lock_filesys (); + while (bytes_left_filesys > 0) { + bytes_written = file_write (file, kbuf, bytes_left_filesys); + if (bytes_written <= 0) { + break; + } + bytes_left_filesys -= bytes_written; + } + process_unlock_filesys (); + + if (bytes_written <= 0) + break; + bytes_to_write -= blocksize; + } + user_buffer += blocksize; + } while (bytes_to_write > 0); + + /* return bytes written */ + palloc_free_page (kbuf); + return size - bytes_to_write; +} + +/* Change the position where the next byte will be read or written */ +static int +syscall_seek (void *sp, bool *segfault) +{ + int fd; + off_t new_pos; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || + ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) { + *segfault = true; + return 0; + } + + /* no way to return something sensible (void function) */ + struct file *file = process_get_file (fd); + if (file == NULL) + return 0; + + process_lock_filesys (); + file_seek (file, new_pos); + process_unlock_filesys (); + return 0; +} + +/* Returns the position of the next byte to be read or written */ +static int +syscall_tell (void *sp, bool *segfault) +{ + int fd; + unsigned r = 0; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { + *segfault = true; + return 0; + } + + /* no way to return something sensible function */ + struct file *file = process_get_file (fd); + if (file == NULL) + return 0; + + process_lock_filesys (); + r = file_tell (file); + process_unlock_filesys (); + return r; +} + +/* Close the given file */ +static int +syscall_close (void *sp, bool *segfault) +{ + int fd; + + /* get arguments */ + if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { + *segfault = true; + return 0; + } + + /* no way to return something sensible function (void) */ + (void) process_close_file (fd); + return 0; +} -- cgit v1.2.3