diff --git a/src/userprog/process.c b/src/userprog/process.c index ce55d5c..42e1bca 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -17,9 +17,23 @@ #include "threads/palloc.h" #include "threads/thread.h" #include "threads/vaddr.h" +#include "threads/malloc.h" + +#define WORD_SIZE sizeof(uintptr_t) + +/* Structure for argument, which saves arguments in argv, + and count the number of arguments. */ +struct arguments + { + int argc; + char **argv; + }; + static thread_func start_process NO_RETURN; -static bool load (const char *cmdline, void (**eip) (void), void **esp); +static bool load (struct arguments *args, void (**eip) (void), void **esp); + +static void argument_parser(char *str, struct arguments *args); /* Starts a new thread running a user program loaded from FILENAME. The new thread may be scheduled (and may even exit) @@ -29,6 +43,7 @@ tid_t process_execute (const char *file_name) { char *fn_copy; + struct arguments *args = malloc (sizeof *args); tid_t tid; /* Make a copy of FILE_NAME. @@ -38,34 +53,66 @@ process_execute (const char *file_name) return TID_ERROR; strlcpy (fn_copy, file_name, PGSIZE); + argument_parser (fn_copy, args); + /* Create a new thread to execute FILE_NAME. */ - tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy); + tid = thread_create (args->argv[0], PRI_DEFAULT, start_process, args); if (tid == TID_ERROR) - palloc_free_page (fn_copy); + { + palloc_free_page (fn_copy); + free(args->argv); + free(args); + } + return tid; } +/* Starts string tokenizer, which cuts given input string into tokens + divided by delimeter, and stores it in given argument struct by pointer. */ +static void +argument_parser (char *string_input, struct arguments *args) +{ + char *leftover; + char *token; + char *curr; + + args->argc = 0; + args->argv = (char **) calloc ((strlen (string_input) + 1), sizeof(char *)); + + token = " "; + curr = strtok_r (string_input, token, &leftover); + + while(curr != NULL) + { + args->argv[args->argc] = curr; + args->argc ++; + curr = strtok_r (NULL, token, &leftover); + } +} + /* A thread function that loads a user process and makes it start running. */ static void -start_process (void *f_name) +start_process (void *arg) { - char *file_name = f_name; + struct argument *args = arg; struct intr_frame if_; bool success; + /* Initialize interrupt frame and load executable. */ memset (&if_, 0, sizeof if_); if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.cs = SEL_UCSEG; if_.eflags = FLAG_IF | FLAG_MBS; - success = load (file_name, &if_.eip, &if_.esp); - - /* If load failed, quit. */ - palloc_free_page (file_name); - if (!success) - thread_exit (); + success = load (args, &if_.eip, &if_.esp); + /* If load failed, quit. */ + if (!success) + { + free(args); + thread_exit (); + } /* Start the user process by simulating a return from an interrupt, implemented by intr_exit (in threads/intr-stubs.S). Because intr_exit takes all of its @@ -201,17 +248,19 @@ static bool validate_segment (const struct Elf32_Phdr *, struct file *); static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable); +static void push_args_on_stack(struct arguments *args, void **esp); /* Loads an ELF executable from FILE_NAME into the current thread. Stores the executable's entry point into *EIP and its initial stack pointer into *ESP. Returns true if successful, false otherwise. */ bool -load (const char *file_name, void (**eip) (void), void **esp) +load (struct arguments *args, void (**eip) (void), void **esp) { struct thread *t = thread_current (); struct Elf32_Ehdr ehdr; struct file *file = NULL; + char *file_name = args->argv[0]; off_t file_ofs; bool success = false; int i; @@ -306,6 +355,7 @@ load (const char *file_name, void (**eip) (void), void **esp) if (!setup_stack (esp)) goto done; + push_args_on_stack (args, esp); /* Start address. */ *eip = (void (*) (void)) ehdr.e_entry; @@ -438,7 +488,7 @@ setup_stack (void **esp) { success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true); if (success) - *esp = PHYS_BASE - 12; // Temporary measure + *esp = PHYS_BASE; else palloc_free_page (kpage); } @@ -464,3 +514,62 @@ install_page (void *upage, void *kpage, bool writable) return (pagedir_get_page (t->pagedir, upage) == NULL && pagedir_set_page (t->pagedir, upage, kpage, writable)); } + +/* Push arguments on newly initialized stack, following calling conventions. + This invention does not use esp dynamically, just making it point bottom of + stack frame after all push is done.*/ + +static void +push_args_on_stack(struct arguments *args, void **esp) +{ + void **arg_ptrs = calloc (args->argc, sizeof(uintptr_t)); + void *curr = (char *) PHYS_BASE; + + int arg_len; + int alignment; + int zero = 0; + int i; + void *dest; + + /* Pushing arguments on stack, saving their address in arg_ptrs. */ + for(i = (args->argc - 1); i >= 0; i--) + { + arg_len = strlen (args->argv[i]) + 1; + dest = curr - arg_len; + memcpy (dest, (args->argv[i]), arg_len); + arg_ptrs[i] = dest; + curr = dest; + } + + alignment = ((uint32_t) curr) % WORD_SIZE; + + /* Aligning, and push bytes required for aligning. */ + for(i = 0; i < alignment ; i++) + memcpy (--curr, &zero, 1); + + curr -= WORD_SIZE; + memcpy (curr, &zero, WORD_SIZE); + + for(i = (args->argc -1); i >= 0; i--) + { + curr -= WORD_SIZE; + memcpy (curr, &arg_ptrs[i], WORD_SIZE); + } + + free (arg_ptrs); + + /* Push address of argv on stack. */ + curr -= WORD_SIZE; + memcpy (curr, &curr, WORD_SIZE); + + /* Push argument count on stack. */ + curr -= WORD_SIZE; + memcpy (curr, &(args->argc), WORD_SIZE); + + /* Push return address as 0 values. */ + curr -= WORD_SIZE; + memcpy (curr, &zero, WORD_SIZE); + + /* esp pointing bottom of the stack */ + *esp = (void *) curr; +}