diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5fb8bd3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "gdb", + "request": "attach", + "name": "Attach to gdbserver", + "executable": "${fileDirname}/build/kernel.o", + "target": "localhost:1234", + "remote": true, + "cwd": "${workspaceRoot}", + "valuesFormatting": "parseText" + } + ] + } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3e21051 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,30 @@ +{ + "files.associations": { + "list.h": "c", + "synch.h": "c", + "syscall.h": "c", + "thread.h": "c", + "*.inc": "c", + "interrupt.h": "c", + "debug.h": "c", + "process.h": "c", + "stdint.h": "c", + "file.h": "c", + "init.h": "c", + "filesys.h": "c", + "inode.h": "c", + "directory.h": "c", + "types.h": "c", + "stat.h": "c", + "off_t.h": "c", + "fcntl.h": "c", + "intr-stubs.h": "c", + "intrinsic.h": "c", + "stdio.h": "c", + "syscall-nr.h": "c", + "stdbool.h": "c", + "malloc.h": "c", + "vaddr.h": "c", + "vm.h": "c" + } +} \ No newline at end of file diff --git a/devices/timer.c b/devices/timer.c index 796f5a8..255522b 100644 --- a/devices/timer.c +++ b/devices/timer.c @@ -88,13 +88,19 @@ timer_elapsed (int64_t then) { } /* Suspends execution for approximately TICKS timer ticks. */ -void -timer_sleep (int64_t ticks) { +void timer_sleep (int64_t ticks) { int64_t start = timer_ticks (); - ASSERT (intr_get_level () == INTR_ON); - while (timer_elapsed (start) < ticks) - thread_yield (); + + // ASSERT (intr_get_level () == INTR_ON); + // while (timer_elapsed (start) < ticks) + // thread_yield (); + if (ticks > 0) + thread_sleep(ticks + start); + else if (ticks == 0) + thread_yield(); + else + return; } /* Suspends execution for approximately MS milliseconds. */ @@ -126,6 +132,8 @@ static void timer_interrupt (struct intr_frame *args UNUSED) { ticks++; thread_tick (); + if (ticks >= get_next_tick_to_awake()) + thread_awake(ticks); } /* Returns true if LOOPS iterations waits for more than one timer diff --git a/filesys/file.c b/filesys/file.c index 7978d84..e0bb6f8 100644 --- a/filesys/file.c +++ b/filesys/file.c @@ -3,12 +3,12 @@ #include "filesys/inode.h" #include "threads/malloc.h" -/* An open file. */ -struct file { - struct inode *inode; /* File's inode. */ - off_t pos; /* Current position. */ - bool deny_write; /* Has file_deny_write() been called? */ -}; +// /* An open file. */ +// struct file { +// struct inode *inode; /* File's inode. */ +// off_t pos; /* Current position. */ +// bool deny_write; /* Has file_deny_write() been called? */ +// }; /* Opens a file for the given INODE, of which it takes ownership, * and returns the new file. Returns a null pointer if an diff --git a/filesys/inode.c b/filesys/inode.c index d88dcbd..1b63dfa 100644 --- a/filesys/inode.c +++ b/filesys/inode.c @@ -12,12 +12,12 @@ /* On-disk inode. * Must be exactly DISK_SECTOR_SIZE bytes long. */ -struct inode_disk { - disk_sector_t start; /* First data sector. */ - off_t length; /* File size in bytes. */ - unsigned magic; /* Magic number. */ - uint32_t unused[125]; /* Not used. */ -}; +// struct inode_disk { +// disk_sector_t start; /* First data sector. */ +// off_t length; /* File size in bytes. */ +// unsigned magic; /* Magic number. */ +// uint32_t unused[125]; /* Not used. */ +// }; /* Returns the number of sectors to allocate for an inode SIZE * bytes long. */ @@ -27,14 +27,14 @@ bytes_to_sectors (off_t size) { } /* In-memory inode. */ -struct inode { - struct list_elem elem; /* Element in inode list. */ - disk_sector_t sector; /* Sector number of disk location. */ - int open_cnt; /* Number of openers. */ - bool removed; /* True if deleted, false otherwise. */ - int deny_write_cnt; /* 0: writes ok, >0: deny writes. */ - struct inode_disk data; /* Inode content. */ -}; +// struct inode { +// struct list_elem elem; /* Element in inode list. */ +// disk_sector_t sector; /* Sector number of disk location. */ +// int open_cnt; /* Number of openers. */ +// bool removed; /* True if deleted, false otherwise. */ +// int deny_write_cnt; /* 0: writes ok, >0: deny writes. */ +// struct inode_disk data; /* Inode content. */ +// }; /* Returns the disk sector that contains byte offset POS within * INODE. diff --git a/include/filesys/file.h b/include/filesys/file.h index e6840ca..9dede52 100644 --- a/include/filesys/file.h +++ b/include/filesys/file.h @@ -2,9 +2,17 @@ #define FILESYS_FILE_H #include "filesys/off_t.h" +#include "filesys/inode.h" struct inode; +/* An open file. */ +struct file { + struct inode *inode; /* File's inode. */ + off_t pos; /* Current position. */ + bool deny_write; /* Has file_deny_write() been called? */ +}; + /* Opening and closing files. */ struct file *file_open (struct inode *); struct file *file_reopen (struct file *); diff --git a/include/filesys/inode.h b/include/filesys/inode.h index be7df63..ebaa3f5 100644 --- a/include/filesys/inode.h +++ b/include/filesys/inode.h @@ -4,9 +4,30 @@ #include #include "filesys/off_t.h" #include "devices/disk.h" +#include "kernel/list.h" struct bitmap; +/* On-disk inode. + * Must be exactly DISK_SECTOR_SIZE bytes long. */ +struct inode_disk { + disk_sector_t start; /* First data sector. */ + off_t length; /* File size in bytes. */ + unsigned magic; /* Magic number. */ + uint32_t unused[125]; /* Not used. */ +}; + +/* In-memory inode. */ +struct inode { + struct list_elem elem; /* Element in inode list. */ + disk_sector_t sector; /* Sector number of disk location. */ + int open_cnt; /* Number of openers. */ + bool removed; /* True if deleted, false otherwise. */ + int deny_write_cnt; /* 0: writes ok, >0: deny writes. */ + struct inode_disk data; /* Inode content. */ +}; + + void inode_init (void); bool inode_create (disk_sector_t, off_t); struct inode *inode_open (disk_sector_t); diff --git a/include/threads/synch.h b/include/threads/synch.h index 3d089fd..8bba9e4 100644 --- a/include/threads/synch.h +++ b/include/threads/synch.h @@ -10,7 +10,7 @@ struct semaphore { struct list waiters; /* List of waiting threads. */ }; -void sema_init (struct semaphore *, unsigned value); +void sema_init(struct semaphore *, unsigned value); void sema_down (struct semaphore *); bool sema_try_down (struct semaphore *); void sema_up (struct semaphore *); @@ -38,6 +38,8 @@ void cond_wait (struct condition *, struct lock *); void cond_signal (struct condition *, struct lock *); void cond_broadcast (struct condition *, struct lock *); +bool cmp_sem_priority(const struct list_elem *a, const struct list_elem *b, void *aux); + /* Optimization barrier. * * The compiler will not reorder operations across an diff --git a/include/threads/thread.h b/include/threads/thread.h index 33b46e6..0faafcb 100644 --- a/include/threads/thread.h +++ b/include/threads/thread.h @@ -5,6 +5,7 @@ #include #include #include "threads/interrupt.h" +#include "threads/synch.h" #ifdef VM #include "vm/vm.h" #endif @@ -28,6 +29,10 @@ typedef int tid_t; #define PRI_DEFAULT 31 /* Default priority. */ #define PRI_MAX 63 /* Highest priority. */ +#define ERROR_EXIT2 -2 +#define FDBASE 2 +#define FDLIMIT 32 + /* A kernel thread or user process. * * Each thread structure is stored in its own 4 kB page. The @@ -85,15 +90,36 @@ typedef int tid_t; * only because they are mutually exclusive: only a thread in the * ready state is on the run queue, whereas only a thread in the * blocked state is on a semaphore wait list. */ -struct thread { + +struct child_info{ + bool finished; + tid_t c_tid; + int c_exit_code; + struct list_elem c_elem; + struct semaphore c_sema; +}; + +struct thread +{ /* Owned by thread.c. */ tid_t tid; /* Thread identifier. */ - enum thread_status status; /* Thread state. */ + enum thread_status status; /* Thread state. 4가지 : ready, blocked, running, dying*/ char name[16]; /* Name (for debugging purposes). */ + int my_exit_code; int priority; /* Priority. */ - - /* Shared between thread.c and synch.c. */ + int init_priority; + + bool do_fork_error; + int64_t wakeup_tick; /* Shared between thread.c and synch.c. */ + struct lock* wait_on_lock; + struct thread *my_parent; + struct file *my_file; + struct child_info *my_info; + struct file* fd_table[FDLIMIT]; /* file descriptor(fd) table */ + struct list_elem donation_elem; struct list_elem elem; /* List element. */ + struct list child_list; + struct list donations; #ifdef USERPROG /* Owned by userprog/process.c. */ @@ -143,4 +169,22 @@ int thread_get_load_avg (void); void do_iret (struct intr_frame *tf); +void thread_sleep(int64_t ticks); +void thread_awake(int64_t ticks); +void update_next_tick_to_awake(int64_t ticks); +int64_t get_next_tick_to_awake(void); + +char is_readylist_empty(void); +int get_ready_list_max_priority(void); +int destruction_req_contains(tid_t child_tid); + +void test_max_priority(void); +bool cmp_priority(const struct list_elem *a_, const struct list_elem *b_, + void *aux UNUSED); +bool donate_cmp_priority(const struct list_elem *a_, const struct list_elem *b_, + void *aux UNUSED); + +void donate_priority(void); +void remove_with_lock(struct lock *lock); +void refresh_priority(void); #endif /* threads/thread.h */ diff --git a/include/userprog/process.h b/include/userprog/process.h index 4365424..2412fd9 100644 --- a/include/userprog/process.h +++ b/include/userprog/process.h @@ -3,6 +3,8 @@ #include "threads/thread.h" +/* ELF types. See [ELF1] 1-2. */ + tid_t process_create_initd (const char *file_name); tid_t process_fork (const char *name, struct intr_frame *if_); int process_exec (void *f_name); diff --git a/include/userprog/syscall.h b/include/userprog/syscall.h index 9059096..0019599 100644 --- a/include/userprog/syscall.h +++ b/include/userprog/syscall.h @@ -2,5 +2,6 @@ #define USERPROG_SYSCALL_H void syscall_init (void); +struct lock filesys_lock; #endif /* userprog/syscall.h */ diff --git a/lib/user/syscall.c b/lib/user/syscall.c index 99043f0..8effc83 100644 --- a/lib/user/syscall.c +++ b/lib/user/syscall.c @@ -1,6 +1,8 @@ #include #include #include "../syscall-nr.h" +#include "include/lib/stdbool.h" +#include "include/lib/user/syscall.h" __attribute__((always_inline)) static __inline int64_t syscall (uint64_t num_, uint64_t a1_, uint64_t a2_, diff --git a/tests/userprog/fork-recursive.c b/tests/userprog/fork-recursive.c index aa701a7..a2ffc51 100644 --- a/tests/userprog/fork-recursive.c +++ b/tests/userprog/fork-recursive.c @@ -14,7 +14,7 @@ fork_and_wait (void){ if (magic >= 10){ exit(magic); - } + } if ((pid = fork("child"))){ magic++; diff --git a/tests/userprog/no-vm/multi-oom.c b/tests/userprog/no-vm/multi-oom.c index 4cc7823..987453d 100644 --- a/tests/userprog/no-vm/multi-oom.c +++ b/tests/userprog/no-vm/multi-oom.c @@ -61,8 +61,8 @@ consume_some_resources (void) break; } #else - if (open (test_name) == -1) - break; + if (open(test_name) == -1) + break; #endif } } @@ -107,8 +107,9 @@ make_children (void) { int pid; char child_name[128]; for (; ; random_init (i), i++) { - if (i > EXPECTED_DEPTH_TO_PASS/2) { - snprintf (child_name, sizeof child_name, "%s_%d_%s", "child", i, "X"); + if (i > EXPECTED_DEPTH_TO_PASS / 2) + { + snprintf(child_name, sizeof child_name, "%s_%d_%s", "child", i, "X"); pid = fork(child_name); if (pid > 0 && wait (pid) != -1) { fail ("crashed child should return -1."); @@ -121,7 +122,7 @@ make_children (void) { snprintf (child_name, sizeof child_name, "%s_%d_%s", "child", i, "O"); pid = fork(child_name); if (pid < 0) { - exit (i); + exit(i); } else if (pid == 0) { consume_some_resources(); } else { @@ -142,8 +143,7 @@ make_children (void) { int main (int argc UNUSED, char *argv[] UNUSED) { msg ("begin"); - - int first_run_depth = make_children (); + int first_run_depth = make_children(); CHECK (first_run_depth >= EXPECTED_DEPTH_TO_PASS, "Spawned at least %d children.", EXPECTED_DEPTH_TO_PASS); for (int i = 0; i < EXPECTED_REPETITIONS; i++) { diff --git a/threads/init.c b/threads/init.c index aae924d..6e12be8 100644 --- a/threads/init.c +++ b/threads/init.c @@ -128,8 +128,7 @@ main (void) { } /* Clear BSS */ -static void -bss_init (void) { +static void bss_init (void) { /* The "BSS" is a segment that should be initialized to zeros. It isn't actually stored on disk or zeroed by the kernel loader, so we have to zero it ourselves. diff --git a/threads/synch.c b/threads/synch.c index 8ca3230..a850fe7 100644 --- a/threads/synch.c +++ b/threads/synch.c @@ -66,11 +66,11 @@ sema_down (struct semaphore *sema) { old_level = intr_disable (); while (sema->value == 0) { - list_push_back (&sema->waiters, &thread_current ()->elem); - thread_block (); + list_insert_ordered(&sema->waiters, &thread_current()->elem, cmp_priority, NULL); + thread_block(); } sema->value--; - intr_set_level (old_level); + intr_set_level(old_level); } /* Down or "P" operation on a semaphore, but only if the @@ -107,12 +107,18 @@ sema_up (struct semaphore *sema) { enum intr_level old_level; ASSERT (sema != NULL); + // ASSERT(!list_empty(&sema->waiters)); + old_level = intr_disable(); - old_level = intr_disable (); - if (!list_empty (&sema->waiters)) - thread_unblock (list_entry (list_pop_front (&sema->waiters), - struct thread, elem)); + if (!list_empty (&sema->waiters)){ + list_sort(&sema->waiters, cmp_priority, NULL); + thread_unblock(list_entry(list_pop_front(&sema->waiters), struct thread, elem)); + } sema->value++; + + if (!is_readylist_empty() && (thread_get_priority() < get_ready_list_max_priority())) + thread_yield(); + intr_set_level (old_level); } @@ -188,8 +194,15 @@ lock_acquire (struct lock *lock) { ASSERT (!intr_context ()); ASSERT (!lock_held_by_current_thread (lock)); - sema_down (&lock->semaphore); - lock->holder = thread_current (); + if(lock->holder != NULL){ + thread_current()->wait_on_lock = lock; + list_insert_ordered(&lock->holder->donations, &thread_current()->donation_elem, cmp_priority, NULL); + donate_priority(); + }; + + sema_down(&lock->semaphore); + thread_current()->wait_on_lock = NULL; + lock->holder = thread_current(); } /* Tries to acquires LOCK and returns true if successful or false @@ -220,10 +233,12 @@ lock_try_acquire (struct lock *lock) { void lock_release (struct lock *lock) { ASSERT (lock != NULL); - ASSERT (lock_held_by_current_thread (lock)); + ASSERT(lock_held_by_current_thread(lock)); lock->holder = NULL; - sema_up (&lock->semaphore); + remove_with_lock(lock); + refresh_priority(); + sema_up(&lock->semaphore); } /* Returns true if the current thread holds LOCK, false @@ -280,10 +295,10 @@ cond_wait (struct condition *cond, struct lock *lock) { ASSERT (lock != NULL); ASSERT (!intr_context ()); ASSERT (lock_held_by_current_thread (lock)); - - sema_init (&waiter.semaphore, 0); - list_push_back (&cond->waiters, &waiter.elem); - lock_release (lock); + + sema_init(&waiter.semaphore, 0); + list_insert_ordered(&cond->waiters, &waiter.elem, cmp_sem_priority, NULL); + lock_release(lock); sema_down (&waiter.semaphore); lock_acquire (lock); } @@ -302,9 +317,11 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED) { ASSERT (!intr_context ()); ASSERT (lock_held_by_current_thread (lock)); - if (!list_empty (&cond->waiters)) - sema_up (&list_entry (list_pop_front (&cond->waiters), - struct semaphore_elem, elem)->semaphore); + if (!list_empty (&cond->waiters)){ + list_sort(&cond->waiters, cmp_sem_priority, NULL); + sema_up(&list_entry(list_pop_front(&cond->waiters), + struct semaphore_elem, elem)->semaphore); + } } /* Wakes up all threads, if any, waiting on COND (protected by @@ -321,3 +338,14 @@ cond_broadcast (struct condition *cond, struct lock *lock) { while (!list_empty (&cond->waiters)) cond_signal (cond, lock); } + +bool +cmp_sem_priority(const struct list_elem *a, const struct list_elem *b, void *aux UNUSED){ + struct semaphore_elem *sa = list_entry(a, struct semaphore_elem, elem); + struct semaphore_elem *sb = list_entry(b, struct semaphore_elem, elem); + + struct thread *ta = list_entry(list_begin(&sa->semaphore.waiters), struct thread, elem); + struct thread *tb = list_entry(list_begin(&sb->semaphore.waiters), struct thread, elem); + + return ta->priority > tb->priority; +} \ No newline at end of file diff --git a/threads/thread.c b/threads/thread.c index bc9e260..54a9581 100644 --- a/threads/thread.c +++ b/threads/thread.c @@ -28,6 +28,9 @@ that are ready to run but not actually running. */ static struct list ready_list; +static struct list sleep_list; +int64_t next_tick_to_awake; + /* Idle thread. */ static struct thread *idle_thread; @@ -62,7 +65,6 @@ static void init_thread (struct thread *, const char *name, int priority); static void do_schedule(int status); static void schedule (void); static tid_t allocate_tid (void); - /* Returns true if T appears to point to a valid thread. */ #define is_thread(t) ((t) != NULL && (t)->magic == THREAD_MAGIC) @@ -108,7 +110,8 @@ thread_init (void) { /* Init the globla thread context */ lock_init (&tid_lock); list_init (&ready_list); - list_init (&destruction_req); + list_init(&sleep_list); + list_init(&destruction_req); /* Set up a thread structure for the running thread. */ initial_thread = running_thread (); @@ -179,6 +182,7 @@ thread_print_stats (void) { tid_t thread_create (const char *name, int priority, thread_func *function, void *aux) { + struct thread *t; tid_t tid; @@ -186,8 +190,9 @@ thread_create (const char *name, int priority, /* Allocate thread. */ t = palloc_get_page (PAL_ZERO); - if (t == NULL) + if (t == NULL){ return TID_ERROR; + } /* Initialize thread. */ init_thread (t, name, priority); @@ -204,9 +209,21 @@ thread_create (const char *name, int priority, t->tf.cs = SEL_KCSEG; t->tf.eflags = FLAG_IF; + t->my_parent = thread_current (); + struct child_info *my_info = (struct child_info *) malloc(sizeof (struct child_info)); + t->my_info = my_info; + t->my_info->finished = false; + t->my_info->c_tid = t->tid; + t->my_info->c_exit_code = t->my_exit_code; + sema_init(&t->my_info->c_sema, 0); + list_push_back(&thread_current ()->child_list, &my_info->c_elem); + /* Add to run queue. */ thread_unblock (t); + if (!(intr_context()) && priority > thread_get_priority()){ + thread_yield(); + } return tid; } @@ -240,9 +257,12 @@ thread_unblock (struct thread *t) { old_level = intr_disable (); ASSERT (t->status == THREAD_BLOCKED); - list_push_back (&ready_list, &t->elem); + + list_insert_ordered(&ready_list, &t->elem, cmp_priority, NULL); + t->status = THREAD_READY; - intr_set_level (old_level); + + intr_set_level(old_level); } /* Returns the name of the running thread. */ @@ -296,6 +316,7 @@ thread_exit (void) { may be scheduled again immediately at the scheduler's whim. */ void thread_yield (void) { + struct thread *curr = thread_current (); enum intr_level old_level; @@ -303,15 +324,18 @@ thread_yield (void) { old_level = intr_disable (); if (curr != idle_thread) - list_push_back (&ready_list, &curr->elem); - do_schedule (THREAD_READY); + list_insert_ordered(&ready_list, &curr->elem, cmp_priority, NULL); + do_schedule(THREAD_READY); intr_set_level (old_level); } /* Sets the current thread's priority to NEW_PRIORITY. */ void thread_set_priority (int new_priority) { - thread_current ()->priority = new_priority; + thread_current()->init_priority = new_priority; + + refresh_priority(); + test_max_priority(); } /* Returns the current thread's priority. */ @@ -320,13 +344,25 @@ thread_get_priority (void) { return thread_current ()->priority; } +void +test_max_priority(void){ + if (!list_empty(&ready_list)){ + struct list_elem *list_elem = list_begin(&ready_list); + struct thread *thread = list_entry(list_elem, struct thread, elem); + if (thread->priority > thread_get_priority()){ + if (!intr_context()) + thread_yield(); + } + } +} + /* Sets the current thread's nice value to NICE. */ void thread_set_nice (int nice UNUSED) { /* TODO: Your implementation goes here */ } -/* Returns the current thread's nice value. */ +/* Returns the current thread's nice intr_set_level (old_level);value. */ int thread_get_nice (void) { /* TODO: Your implementation goes here */ @@ -403,12 +439,27 @@ init_thread (struct thread *t, const char *name, int priority) { ASSERT (PRI_MIN <= priority && priority <= PRI_MAX); ASSERT (name != NULL); - memset (t, 0, sizeof *t); + memset(t, 0, sizeof *t); t->status = THREAD_BLOCKED; strlcpy (t->name, name, sizeof t->name); t->tf.rsp = (uint64_t) t + PGSIZE - sizeof (void *); t->priority = priority; + t->init_priority = priority; + t->wait_on_lock = NULL; + list_init(&t->donations); t->magic = THREAD_MAGIC; + + t->my_exit_code = -1; + t->my_parent = initial_thread; + t->my_file = NULL; + list_init(&t->child_list); + t->my_info = NULL; + t->do_fork_error = false; + + for (int i = FDBASE; i < FDLIMIT; i++) + { + t->fd_table[i] = NULL; + } } /* Chooses and returns the next thread to be scheduled. Should @@ -421,35 +472,38 @@ next_thread_to_run (void) { if (list_empty (&ready_list)) return idle_thread; else - return list_entry (list_pop_front (&ready_list), struct thread, elem); + return list_entry(list_pop_front(&ready_list), struct thread, elem); + } /* Use iretq to launch the thread */ void do_iret (struct intr_frame *tf) { __asm __volatile( - "movq %0, %%rsp\n" - "movq 0(%%rsp),%%r15\n" - "movq 8(%%rsp),%%r14\n" - "movq 16(%%rsp),%%r13\n" - "movq 24(%%rsp),%%r12\n" - "movq 32(%%rsp),%%r11\n" - "movq 40(%%rsp),%%r10\n" - "movq 48(%%rsp),%%r9\n" - "movq 56(%%rsp),%%r8\n" - "movq 64(%%rsp),%%rsi\n" - "movq 72(%%rsp),%%rdi\n" - "movq 80(%%rsp),%%rbp\n" - "movq 88(%%rsp),%%rdx\n" - "movq 96(%%rsp),%%rcx\n" - "movq 104(%%rsp),%%rbx\n" - "movq 112(%%rsp),%%rax\n" - "addq $120,%%rsp\n" - "movw 8(%%rsp),%%ds\n" - "movw (%%rsp),%%es\n" - "addq $32, %%rsp\n" - "iretq" - : : "g" ((uint64_t) tf) : "memory"); + "movq %0, %%rsp\n" + "movq 0(%%rsp),%%r15\n" + "movq 8(%%rsp),%%r14\n" + "movq 16(%%rsp),%%r13\n" + "movq 24(%%rsp),%%r12\n" + "movq 32(%%rsp),%%r11\n" + "movq 40(%%rsp),%%r10\n" + "movq 48(%%rsp),%%r9\n" + "movq 56(%%rsp),%%r8\n" + "movq 64(%%rsp),%%rsi\n" + "movq 72(%%rsp),%%rdi\n" + "movq 80(%%rsp),%%rbp\n" + "movq 88(%%rsp),%%rdx\n" + "movq 96(%%rsp),%%rcx\n" + "movq 104(%%rsp),%%rbx\n" + "movq 112(%%rsp),%%rax\n" + "addq $120,%%rsp\n" + "movw 8(%%rsp),%%ds\n" + "movw (%%rsp),%%es\n" + "addq $32, %%rsp\n" + "iretq" + : + : "g"((uint64_t)tf) + : "memory"); } /* Switching the thread by activating the new thread's page @@ -588,3 +642,141 @@ allocate_tid (void) { return tid; } + +void +thread_sleep(int64_t ticks){ + struct thread *curr = thread_current (); + enum intr_level old_level; + + ASSERT(is_thread(curr)); + ASSERT(curr != idle_thread); + old_level = intr_disable(); + + curr->wakeup_tick = ticks; + list_push_back(&sleep_list, &curr->elem); + + thread_block(); + if (ticks < get_next_tick_to_awake()) + update_next_tick_to_awake(ticks); + + intr_set_level (old_level); +} + +void +thread_awake(int64_t ticks){ + struct list_elem *list_elem; + struct thread *thread; + int64_t next_ticks = INT64_MAX; + + if (!list_empty(&sleep_list)){ + list_elem = list_begin(&sleep_list); + while (list_elem != list_end(&sleep_list)){ + thread = list_entry(list_elem, struct thread, elem); + if (thread->wakeup_tick <= ticks){ + list_elem = list_remove(list_elem); + thread_unblock(thread); + continue; + } + else if (next_ticks > thread->wakeup_tick){ + next_ticks = thread->wakeup_tick; + } + list_elem = list_next(list_elem); + } + update_next_tick_to_awake(next_ticks); + } +} + +void +update_next_tick_to_awake(int64_t ticks){ + next_tick_to_awake = ticks; +} + +int64_t +get_next_tick_to_awake(void){ + return next_tick_to_awake; +} + +char +is_readylist_empty(void){ + return !list_empty(&ready_list) ? 0 : 1; +} + +int +destruction_req_contains(tid_t child_tid){ + struct list_elem *list_elem; + if (!list_empty(&destruction_req)) + { + list_elem = list_begin(&destruction_req); + for (list_elem; list_elem != list_end(&destruction_req); list_elem = list_next(list_elem)){ + struct thread *thread = list_entry(list_elem, struct thread, elem); + if (thread->tid == child_tid) + return thread->my_exit_code; + } + } + return ERROR_EXIT2; +} + +int +get_ready_list_max_priority(){ + struct thread *thread = list_entry(list_begin(&ready_list), struct thread, elem); + return thread->priority; +} + +bool +cmp_priority (const struct list_elem *a_, const struct list_elem *b_, + void *aux UNUSED) +{ + const struct thread *ta = list_entry (a_, struct thread, elem); + const struct thread *tb = list_entry (b_, struct thread, elem); + + return ta->priority > tb->priority; +} + +bool +donate_cmp_priority(const struct list_elem *a_, const struct list_elem *b_, void *aux UNUSED){ + const struct thread *ta = list_entry (a_, struct thread, donation_elem); + const struct thread *tb = list_entry (b_, struct thread, donation_elem); + + return ta->priority > tb->priority; +} + +void donate_priority(void){ + int depth; + struct thread *curr = thread_current(); + + for (depth = 0; depth < 8; depth++){ + if(! curr->wait_on_lock) + break; + struct thread *holder = curr->wait_on_lock->holder; + holder->priority = thread_get_priority(); + curr = holder; + } +} + +void remove_with_lock(struct lock *lock){ + struct thread *curr = thread_current(); + struct list_elem *d_elem; + + if (!list_empty(&curr->donations)){ + d_elem = list_begin(&curr->donations); + while (d_elem != list_end(&curr->donations)){ + struct thread *thread = list_entry(d_elem, struct thread, donation_elem); + if (thread->wait_on_lock == lock){ + d_elem = list_remove(d_elem); + continue; + } + d_elem = list_next(d_elem); + } + } +} + +void refresh_priority(void){ + struct thread *curr = thread_current(); + curr->priority = curr->init_priority; + if (!list_empty(&curr->donations)){ + list_sort(&curr->donations, donate_cmp_priority, NULL); + struct thread *thread = list_entry(list_begin(&curr->donations), struct thread, donation_elem); + if (thread->priority > thread_get_priority()) + curr->priority = thread->priority; + } +} diff --git a/userprog/exception.c b/userprog/exception.c index bf4a9b2..8d3d0c3 100644 --- a/userprog/exception.c +++ b/userprog/exception.c @@ -139,7 +139,16 @@ page_fault (struct intr_frame *f) { not_present = (f->error_code & PF_P) == 0; write = (f->error_code & PF_W) != 0; user = (f->error_code & PF_U) != 0; + if(user) { + thread_current()->my_exit_code = -1; + thread_exit(); + } + if(write) { + thread_current()->my_exit_code = -1; + thread_exit(); + } + #ifdef VM /* For project 3 and later. */ if (vm_try_handle_fault (f, fault_addr, user, write, not_present)) diff --git a/userprog/process.c b/userprog/process.c index 9388e50..71f091f 100644 --- a/userprog/process.c +++ b/userprog/process.c @@ -1,4 +1,5 @@ #include "userprog/process.h" +#include "userprog/syscall.h" #include #include #include @@ -17,7 +18,13 @@ #include "threads/thread.h" #include "threads/mmu.h" #include "threads/vaddr.h" +#include "threads/malloc.h" +#include "threads/synch.h" #include "intrinsic.h" +#include "lib/string.h" +#include "lib/stdio.h" +#include "lib/user/syscall.h" + #ifdef VM #include "vm/vm.h" #endif @@ -25,12 +32,12 @@ static void process_cleanup (void); static bool load (const char *file_name, struct intr_frame *if_); static void initd (void *f_name); -static void __do_fork (void *); +static void __do_fork (void **); /* General process initializer for initd and other process. */ static void process_init (void) { - struct thread *current = thread_current (); + struct thread *current = thread_current (); } /* Starts the first userland program, called "initd", loaded from FILE_NAME. @@ -40,44 +47,63 @@ process_init (void) { * Notice that THIS SHOULD BE CALLED ONCE. */ tid_t process_create_initd (const char *file_name) { - char *fn_copy; - tid_t tid; - - /* Make a copy of FILE_NAME. - * Otherwise there's a race between the caller and load(). */ - fn_copy = palloc_get_page (0); - if (fn_copy == NULL) - return TID_ERROR; - strlcpy (fn_copy, file_name, PGSIZE); - - /* Create a new thread to execute FILE_NAME. */ - tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy); - if (tid == TID_ERROR) - palloc_free_page (fn_copy); - return tid; + char *fn_copy; + tid_t tid; + + /* Make a copy of FILE_NAME. + * Otherwise there's a race between the caller and load(). */ + fn_copy = palloc_get_page (0); + if (fn_copy == NULL) + return TID_ERROR; + strlcpy (fn_copy, file_name, PGSIZE); + char *tmp; + file_name = strtok_r(file_name, " ", &tmp); + + /* Create a new thread to execute FILE_NAME. */ + tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy); + if (tid == TID_ERROR) + palloc_free_page (fn_copy); + return tid; } /* A thread function that launches first user process. */ static void initd (void *f_name) { #ifdef VM - supplemental_page_table_init (&thread_current ()->spt); + supplemental_page_table_init (&thread_current ()->spt); #endif - process_init (); + process_init (); - if (process_exec (f_name) < 0) - PANIC("Fail to launch initd\n"); - NOT_REACHED (); + if (process_exec (f_name) < 0) + PANIC("Fail to launch initd\n"); + NOT_REACHED (); } /* Clones the current process as `name`. Returns the new process's thread id, or * TID_ERROR if the thread cannot be created. */ tid_t -process_fork (const char *name, struct intr_frame *if_ UNUSED) { - /* Clone current thread to new thread.*/ - return thread_create (name, - PRI_DEFAULT, __do_fork, thread_current ()); +process_fork (const char *name, struct intr_frame *if_) { + /* Clone current thread to new thread.*/ + struct semaphore fork_sema; + struct thread *p_thread = thread_current(); + void *arr[3] = {p_thread, if_, &fork_sema}; + tid_t child_pid; + + sema_init(&fork_sema, 0); + + child_pid = thread_create(name, PRI_DEFAULT, __do_fork, arr); + if (child_pid != TID_ERROR) + { + sema_down(&fork_sema); + if (p_thread -> do_fork_error == true){ + return -1; + } + return child_pid; + }else{ + return child_pid; + } + } #ifndef VM @@ -85,30 +111,36 @@ process_fork (const char *name, struct intr_frame *if_ UNUSED) { * pml4_for_each. This is only for the project 2. */ static bool duplicate_pte (uint64_t *pte, void *va, void *aux) { - struct thread *current = thread_current (); - struct thread *parent = (struct thread *) aux; - void *parent_page; - void *newpage; - bool writable; - - /* 1. TODO: If the parent_page is kernel page, then return immediately. */ - - /* 2. Resolve VA from the parent's page map level 4. */ - parent_page = pml4_get_page (parent->pml4, va); - - /* 3. TODO: Allocate new PAL_USER page for the child and set result to - * TODO: NEWPAGE. */ - - /* 4. TODO: Duplicate parent's page to the new page and - * TODO: check whether parent's page is writable or not (set WRITABLE - * TODO: according to the result). */ - - /* 5. Add new page to child's page table at address VA with WRITABLE - * permission. */ - if (!pml4_set_page (current->pml4, va, newpage, writable)) { - /* 6. TODO: if fail to insert page, do error handling. */ - } - return true; + struct thread *current = thread_current (); + struct thread *parent = (struct thread *) aux; + void *parent_page; + void *newpage; + bool writable; + + /* 1. TODO: If the parent_page is kernel page, then return immediately. */ + if (is_kernel_vaddr(va)) + return true; + + /* 2. Resolve VA from the parent's page map level 4. */ + parent_page = pml4_get_page (parent->pml4, va); + + /* 3. TODO: Allocate new PAL_USER page for the child and set result to + * TODO: NEWPAGE. */ + newpage = palloc_get_page(PAL_USER | PAL_ZERO); + + /* 4. TODO: Duplicate parent's page to the new page and + * TODO: check whether parent's page is writable or not (set WRITABLE + * TODO: according to the result). */ + memcpy(newpage, parent_page, PGSIZE); + if (is_writable(pte)) writable = true; + + /* 5. Add new page to child's page table at address VA with WRITABLE + * permission. */ + if (!pml4_set_page (current->pml4, va, newpage, writable)) { + /* 6. TODO: if fail to insert page, do error handling. */ + return false; + } + return true; } #endif @@ -117,76 +149,95 @@ duplicate_pte (uint64_t *pte, void *va, void *aux) { * That is, you are required to pass second argument of process_fork to * this function. */ static void -__do_fork (void *aux) { - struct intr_frame if_; - struct thread *parent = (struct thread *) aux; - struct thread *current = thread_current (); - /* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */ - struct intr_frame *parent_if; - bool succ = true; - - /* 1. Read the cpu context to local stack. */ - memcpy (&if_, parent_if, sizeof (struct intr_frame)); - - /* 2. Duplicate PT */ - current->pml4 = pml4_create(); - if (current->pml4 == NULL) - goto error; - - process_activate (current); +__do_fork (void **aux) { + struct thread *parent = (struct thread *)aux[0]; + struct thread *current = thread_current(); + struct intr_frame if_ = current->tf; + struct intr_frame *parent_if = (struct intr_frame *)aux[1]; + struct semaphore *fork_sema = (struct semaphore *)aux[2]; + bool succ = true; + + /* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */ + + /* 1. Read the cpu context to local stack. */ + memcpy (&if_, parent_if, sizeof (struct intr_frame)); + + /* 2. Duplicate PT */ + current->pml4 = pml4_create(); + if (current->pml4 == NULL){ + goto error; + } + + process_activate (current); #ifdef VM - supplemental_page_table_init (¤t->spt); - if (!supplemental_page_table_copy (¤t->spt, &parent->spt)) - goto error; + supplemental_page_table_init (¤t->spt); + if (!supplemental_page_table_copy (¤t->spt, &parent->spt)) + goto error; #else - if (!pml4_for_each (parent->pml4, duplicate_pte, parent)) - goto error; + if (!pml4_for_each (parent->pml4, duplicate_pte, parent)){ + goto error; + } #endif - /* TODO: Your code goes here. - * TODO: Hint) To duplicate the file object, use `file_duplicate` - * TODO: in include/filesys/file.h. Note that parent should not return - * TODO: from the fork() until this function successfully duplicates - * TODO: the resources of parent.*/ - - process_init (); - - /* Finally, switch to the newly created process. */ - if (succ) - do_iret (&if_); + /* TODO: Your code goes here. + * TODO: Hint) To duplicate the file object, use `file_duplicate` + * TODO: in include/filesys/file.h. Note that parent should not return + * TODO: from the fork() until this function successfully duplicates + * TODO: the resources of parent.*/ + + for (int fd = FDBASE; fd < FDLIMIT; fd++){ + if (parent->fd_table[fd]){ + current->fd_table[fd] = file_duplicate(parent->fd_table[fd]); + }else{ + current->fd_table[fd] = NULL; + } + } + + if_.R.rax = 0; + process_init(); + + /* Finally, switch to the newly created process. */ + if (succ){ + sema_up(fork_sema); + do_iret(&if_); + } error: - thread_exit (); + sema_up(fork_sema); + current->my_exit_code = -1; + parent->do_fork_error = true; + thread_exit(); } /* Switch the current execution context to the f_name. * Returns -1 on fail. */ int process_exec (void *f_name) { - char *file_name = f_name; - bool success; - - /* We cannot use the intr_frame in the thread structure. - * This is because when current thread rescheduled, - * it stores the execution information to the member. */ - struct intr_frame _if; - _if.ds = _if.es = _if.ss = SEL_UDSEG; - _if.cs = SEL_UCSEG; - _if.eflags = FLAG_IF | FLAG_MBS; - - /* We first kill the current context */ - process_cleanup (); - - /* And then load the binary */ - success = load (file_name, &_if); - - /* If load failed, quit. */ - palloc_free_page (file_name); - if (!success) - return -1; - - /* Start switched process. */ - do_iret (&_if); - NOT_REACHED (); + char *file_name = f_name; + bool success; + + /* We cannot use the intr_frame in the thread structure. + * This is because when current thread rescheduled, + * it stores the execution information to the member. */ + struct intr_frame _if; + _if.ds = _if.es = _if.ss = SEL_UDSEG; + _if.cs = SEL_UCSEG; + _if.eflags = FLAG_IF | FLAG_MBS; + + /* We first kill the current context */ + process_cleanup(); + + lock_acquire(&filesys_lock); + /* And then load the binary */ + success = load (file_name, &_if); + /* If load failed, quit. */ + lock_release(&filesys_lock); + palloc_free_page (file_name); + if (!success) + return -1; + + /* Start switched process. */ + do_iret (&_if); + NOT_REACHED (); } @@ -200,67 +251,125 @@ process_exec (void *f_name) { * This function will be implemented in problem 2-2. For now, it * does nothing. */ int -process_wait (tid_t child_tid UNUSED) { - /* XXX: Hint) The pintos exit if process_wait (initd), we recommend you - * XXX: to add infinite loop here before - * XXX: implementing the process_wait. */ - return -1; +process_wait (tid_t child_tid) { + /* XXX: Hint) The pintos exit if process_wait (initd), we recommend you + * XXX: to add infinite loop here before + * XXX: implementing the process_wait. */ + int result; + struct thread *curr = thread_current(); + struct list_elem *child_elem = list_begin(&curr->child_list); + struct child_info *c_info; + while (child_elem != list_end(&curr->child_list)) + { + c_info = list_entry(child_elem, struct child_info, c_elem); + if(c_info->c_tid == child_tid){ + while(!c_info->finished){ + sema_down(&c_info->c_sema); + } + result = c_info->c_exit_code; + list_remove(child_elem); + free(c_info); + c_info == NULL; + return result; + } + child_elem = list_next(child_elem); + } + return -1; } /* Exit the process. This function is called by thread_exit (). */ void process_exit (void) { - struct thread *curr = thread_current (); - /* TODO: Your code goes here. - * TODO: Implement process termination message (see - * TODO: project2/process_termination.html). - * TODO: We recommend you to implement process resource cleanup here. */ - - process_cleanup (); + struct thread *curr = thread_current (); + if (curr->my_file){ + file_close(curr->my_file); + curr->my_file = NULL; + } + + struct file **fd_table = curr->fd_table; + lock_acquire(&filesys_lock); + for (int i = FDBASE; i < FDLIMIT; i++) + { + if (fd_table[i]){ + file_close(fd_table[i]); + fd_table[i] = NULL; + } + } + lock_release(&filesys_lock); + + enum intr_level old_level; + old_level = intr_disable(); + + if (curr->my_info){ + curr->my_info->c_exit_code = curr->my_exit_code; + curr->my_info->finished = true; + sema_up(&curr->my_info->c_sema); + } + + struct list_elem *list_elem = list_begin(&curr->child_list); + while (list_elem != list_end(&curr->child_list)){ + struct child_info * c_info = list_entry(list_elem, struct child_info, c_elem); + + list_elem = list_next(list_elem); + if (c_info){ + free(c_info); + c_info = NULL; + } + } + + intr_set_level (old_level); + + /* TODO: Your code goes here. + * TODO: Implement process termination message (see + * TODO: project2/process_termination.html). + * TODO: We recommend you to implement process resource cleanup here. */ + if (curr->pml4 != NULL) + printf("%s: exit(%d)\n", curr->name, curr->my_exit_code); + process_cleanup(); } /* Free the current process's resources. */ static void process_cleanup (void) { - struct thread *curr = thread_current (); + struct thread *curr = thread_current (); #ifdef VM - supplemental_page_table_kill (&curr->spt); + supplemental_page_table_kill (&curr->spt); #endif - uint64_t *pml4; - /* Destroy the current process's page directory and switch back - * to the kernel-only page directory. */ - pml4 = curr->pml4; - if (pml4 != NULL) { - /* Correct ordering here is crucial. We must set - * cur->pagedir to NULL before switching page directories, - * so that a timer interrupt can't switch back to the - * process page directory. We must activate the base page - * directory before destroying the process's page - * directory, or our active page directory will be one - * that's been freed (and cleared). */ - curr->pml4 = NULL; - pml4_activate (NULL); - pml4_destroy (pml4); - } + uint64_t *pml4; + /* Destroy the current process's page directory and switch back + * to the kernel-only page directory. */ + pml4 = curr->pml4; + if (pml4 != NULL) { + /* Correct ordering here is crucial. We must set + * cur->pagedir to NULL before switching page directories, + * so that a timer interrupt can't switch back to the + * process page directory. We must activate the base page + * directory before destroying the process's page + * directory, or our active page directory will be one + * that's been freed (and cleared). */ + curr->pml4 = NULL; + pml4_activate (NULL); + pml4_destroy (pml4); + } } /* Sets up the CPU for running user code in the nest thread. * This function is called on every context switch. */ void process_activate (struct thread *next) { - /* Activate thread's page tables. */ - pml4_activate (next->pml4); + /* Activate thread's page tables. */ + pml4_activate (next->pml4); - /* Set thread's kernel stack for use in processing interrupts. */ - tss_update (next); + /* Set thread's kernel stack for use in processing interrupts. */ + tss_update (next); } /* We load ELF binaries. The following definitions are taken * from the ELF specification, [ELF1], more-or-less verbatim. */ -/* ELF types. See [ELF1] 1-2. */ +// /* ELF types. See [ELF1] 1-2. */ #define EI_NIDENT 16 #define PT_NULL 0 /* Ignore. */ @@ -279,31 +388,31 @@ process_activate (struct thread *next) { /* Executable header. See [ELF1] 1-4 to 1-8. * This appears at the very beginning of an ELF binary. */ struct ELF64_hdr { - unsigned char e_ident[EI_NIDENT]; - uint16_t e_type; - uint16_t e_machine; - uint32_t e_version; - uint64_t e_entry; - uint64_t e_phoff; - uint64_t e_shoff; - uint32_t e_flags; - uint16_t e_ehsize; - uint16_t e_phentsize; - uint16_t e_phnum; - uint16_t e_shentsize; - uint16_t e_shnum; - uint16_t e_shstrndx; + unsigned char e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; }; struct ELF64_PHDR { - uint32_t p_type; - uint32_t p_flags; - uint64_t p_offset; - uint64_t p_vaddr; - uint64_t p_paddr; - uint64_t p_filesz; - uint64_t p_memsz; - uint64_t p_align; + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; }; /* Abbreviations */ @@ -313,8 +422,8 @@ struct ELF64_PHDR { static bool setup_stack (struct intr_frame *if_); static bool validate_segment (const struct 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); + uint32_t read_bytes, uint32_t zero_bytes, + bool writable); /* Loads an ELF executable from FILE_NAME into the current thread. * Stores the executable's entry point into *RIP @@ -322,107 +431,164 @@ static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, * Returns true if successful, false otherwise. */ static bool load (const char *file_name, struct intr_frame *if_) { - struct thread *t = thread_current (); - struct ELF ehdr; - struct file *file = NULL; - off_t file_ofs; - bool success = false; - int i; - - /* Allocate and activate page directory. */ - t->pml4 = pml4_create (); - if (t->pml4 == NULL) - goto done; - process_activate (thread_current ()); - - /* Open executable file. */ - file = filesys_open (file_name); - if (file == NULL) { - printf ("load: %s: open failed\n", file_name); - goto done; - } - - /* Read and verify executable header. */ - if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr - || memcmp (ehdr.e_ident, "\177ELF\2\1\1", 7) - || ehdr.e_type != 2 - || ehdr.e_machine != 0x3E // amd64 - || ehdr.e_version != 1 - || ehdr.e_phentsize != sizeof (struct Phdr) - || ehdr.e_phnum > 1024) { - printf ("load: %s: error loading executable\n", file_name); - goto done; - } - - /* Read program headers. */ - file_ofs = ehdr.e_phoff; - for (i = 0; i < ehdr.e_phnum; i++) { - struct Phdr phdr; - - if (file_ofs < 0 || file_ofs > file_length (file)) - goto done; - file_seek (file, file_ofs); - - if (file_read (file, &phdr, sizeof phdr) != sizeof phdr) - goto done; - file_ofs += sizeof phdr; - switch (phdr.p_type) { - case PT_NULL: - case PT_NOTE: - case PT_PHDR: - case PT_STACK: - default: - /* Ignore this segment. */ - break; - case PT_DYNAMIC: - case PT_INTERP: - case PT_SHLIB: - goto done; - case PT_LOAD: - if (validate_segment (&phdr, file)) { - bool writable = (phdr.p_flags & PF_W) != 0; - uint64_t file_page = phdr.p_offset & ~PGMASK; - uint64_t mem_page = phdr.p_vaddr & ~PGMASK; - uint64_t page_offset = phdr.p_vaddr & PGMASK; - uint32_t read_bytes, zero_bytes; - if (phdr.p_filesz > 0) { - /* Normal segment. - * Read initial part from disk and zero the rest. */ - read_bytes = page_offset + phdr.p_filesz; - zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE) - - read_bytes); - } else { - /* Entirely zero. - * Don't read anything from disk. */ - read_bytes = 0; - zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE); - } - if (!load_segment (file, file_page, (void *) mem_page, - read_bytes, zero_bytes, writable)) - goto done; - } - else - goto done; - break; - } - } - - /* Set up stack. */ - if (!setup_stack (if_)) - goto done; - - /* Start address. */ - if_->rip = ehdr.e_entry; - - /* TODO: Your code goes here. - * TODO: Implement argument passing (see project2/argument_passing.html). */ - - success = true; + struct thread *t = thread_current(); + struct ELF ehdr; + struct file *file = NULL; + off_t file_ofs; + bool success = false; + int i; + + /* Allocate and activate page directory. */ + t->pml4 = pml4_create (); + if (t->pml4 == NULL) + goto done; + process_activate (thread_current ()); + + // Tokenize & put them to argv + char *token, *save_ptr; + char *argv[40]; + int argc = 0; + if (strchr(file_name, ' ')) + { + for (token = strtok_r (file_name, " ", &save_ptr); token != NULL; token = strtok_r (NULL, " ", &save_ptr)){ + argv[argc] = token; + argc++; + } + } + else + { + argv[argc] = file_name; + argc++; + } + file = filesys_open(file_name); + if (file == NULL) { + printf ("load: %s: open failed\n", file_name); + goto done; + } + + /* Read and verify executable header. */ + if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr + || memcmp (ehdr.e_ident, "\177ELF\2\1\1", 7) + || ehdr.e_type != 2 + || ehdr.e_machine != 0x3E // amd64 + || ehdr.e_version != 1 + || ehdr.e_phentsize != sizeof (struct Phdr) + || ehdr.e_phnum > 1024) { + printf ("load: %s: error loading executable\n", file_name); + goto done; + } + + /* Read program headers. */ + file_ofs = ehdr.e_phoff; + for (i = 0; i < ehdr.e_phnum; i++) { + struct Phdr phdr; + + if (file_ofs < 0 || file_ofs > file_length (file)) + goto done; + file_seek (file, file_ofs); + + if (file_read (file, &phdr, sizeof phdr) != sizeof phdr) + goto done; + file_ofs += sizeof phdr; + switch (phdr.p_type) { + case PT_NULL: + case PT_NOTE: + case PT_PHDR: + case PT_STACK: + default: + /* Ignore this segment. */ + break; + case PT_DYNAMIC: + case PT_INTERP: + case PT_SHLIB: + goto done; + case PT_LOAD: + if (validate_segment (&phdr, file)) { + bool writable = (phdr.p_flags && PF_W) != 0; + uint64_t file_page = phdr.p_offset & ~PGMASK; + uint64_t mem_page = phdr.p_vaddr & ~PGMASK; + uint64_t page_offset = phdr.p_vaddr & PGMASK; + uint32_t read_bytes, zero_bytes; + if (phdr.p_filesz > 0) { + /* Normal segment. + * Read initial part from disk and zero the rest. */ + read_bytes = page_offset + phdr.p_filesz; + zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE) + - read_bytes); + } else { + /* Entirely zero. + * Don't read anything from disk. */ + read_bytes = 0; + zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE); + } + if (!load_segment (file, file_page, (void *) mem_page, + read_bytes, zero_bytes, writable)) + goto done; + } + else + goto done; + break; + } + } + + /* Set up stack. */ + if (!setup_stack (if_)) + goto done; + + /* Start address. */ + if_->rip = ehdr.e_entry; + + /* TODO: Your code goes here. + * TODO: Implement argument passing (see project2/argument_passing.html). */ + + /* PROJECT 2: ARGUMENT PASSING */ + size_t sum = 0; + char *argv_address[40]; + + // step 3-1. Break the command into words + for(int i = argc-1; i >= 0; i--) { + size_t len = strlen(argv[i]) + 1; // '\0' 포함 + sum += len; + argv_address[i] = (if_->rsp - sum); + memcpy((if_->rsp - sum), argv[i], len); + } + + // step 3-2. Word-Align + while((if_->rsp - sum) % 8 != 0){ + sum++; + *(uint8_t *) (if_->rsp - sum) = 0; + } + + // step 2. Push the address of each string plus a null pointer sentinel + for (int i = argc; i >=0 ; i--){ + sum += 8; + if (i == argc) + memset(if_->rsp - sum, 0, sizeof(char **)); + else + memcpy(if_->rsp - sum, &argv_address[i], sizeof(char **)); + } + + // step 1-1. Point %rsi to argv (the address of argv[0]) and set %rdi to argc. + if_->rsp -= sum; + if_->R.rdi = argc; + if_->R.rsi = if_->rsp; + + // step 1-2. Push a fake "return address" + memset(if_->rsp - 8, 0, sizeof(void *)); + + success = true; done: - /* We arrive here whether the load is successful or not. */ - file_close (file); - return success; + /* We arrive here regardless of whether the loading works successful or not. */ + if (file){ + if (thread_current()->my_file){ + file_close(thread_current()->my_file); + thread_current()->my_file = NULL; + } + thread_current()->my_file = file; + file_deny_write(thread_current()->my_file); + } + return success; } @@ -430,44 +596,44 @@ load (const char *file_name, struct intr_frame *if_) { * FILE and returns true if so, false otherwise. */ static bool validate_segment (const struct Phdr *phdr, struct file *file) { - /* p_offset and p_vaddr must have the same page offset. */ - if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK)) - return false; - - /* p_offset must point within FILE. */ - if (phdr->p_offset > (uint64_t) file_length (file)) - return false; - - /* p_memsz must be at least as big as p_filesz. */ - if (phdr->p_memsz < phdr->p_filesz) - return false; - - /* The segment must not be empty. */ - if (phdr->p_memsz == 0) - return false; - - /* The virtual memory region must both start and end within the - user address space range. */ - if (!is_user_vaddr ((void *) phdr->p_vaddr)) - return false; - if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz))) - return false; - - /* The region cannot "wrap around" across the kernel virtual - address space. */ - if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr) - return false; - - /* Disallow mapping page 0. - Not only is it a bad idea to map page 0, but if we allowed - it then user code that passed a null pointer to system calls - could quite likely panic the kernel by way of null pointer - assertions in memcpy(), etc. */ - if (phdr->p_vaddr < PGSIZE) - return false; - - /* It's okay. */ - return true; + /* p_offset and p_vaddr must have the same page offset. */ + if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK)) + return false; + + /* p_offset must point within FILE. */ + if (phdr->p_offset > (uint64_t) file_length (file)) + return false; + + /* p_memsz must be at least as big as p_filesz. */ + if (phdr->p_memsz < phdr->p_filesz) + return false; + + /* The segment must not be empty. */ + if (phdr->p_memsz == 0) + return false; + + /* The virtual memory region must both start and end within the + user address space range. */ + if (!is_user_vaddr ((void *) phdr->p_vaddr)) + return false; + if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz))) + return false; + + /* The region cannot "wrap around" across the kernel virtual + address space. */ + if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr) + return false; + + /* Disallow mapping page 0. + Not only is it a bad idea to map page 0, but if we allowed + it then user code that passed a null pointer to system calls + could quite likely panic the kernel by way of null pointer + assertions in memcpy(), etc. */ + if (phdr->p_vaddr < PGSIZE) + return false; + + /* It's okay. */ + return true; } #ifndef VM @@ -494,61 +660,61 @@ static bool install_page (void *upage, void *kpage, bool writable); * or disk read error occurs. */ static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, - uint32_t read_bytes, uint32_t zero_bytes, bool writable) { - ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); - ASSERT (pg_ofs (upage) == 0); - ASSERT (ofs % PGSIZE == 0); - - file_seek (file, ofs); - while (read_bytes > 0 || zero_bytes > 0) { - /* Do calculate how to fill this page. - * We will read PAGE_READ_BYTES bytes from FILE - * and zero the final PAGE_ZERO_BYTES bytes. */ - size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; - size_t page_zero_bytes = PGSIZE - page_read_bytes; - - /* Get a page of memory. */ - uint8_t *kpage = palloc_get_page (PAL_USER); - if (kpage == NULL) - return false; - - /* Load this page. */ - if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes) { - palloc_free_page (kpage); - return false; - } - memset (kpage + page_read_bytes, 0, page_zero_bytes); - - /* Add the page to the process's address space. */ - if (!install_page (upage, kpage, writable)) { - printf("fail\n"); - palloc_free_page (kpage); - return false; - } - - /* Advance. */ - read_bytes -= page_read_bytes; - zero_bytes -= page_zero_bytes; - upage += PGSIZE; - } - return true; + uint32_t read_bytes, uint32_t zero_bytes, bool writable) { + ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); + ASSERT (pg_ofs (upage) == 0); + ASSERT (ofs % PGSIZE == 0); + + file_seek (file, ofs); + while (read_bytes > 0 || zero_bytes > 0) { + /* Do calculate how to fill this page. + * We will read PAGE_READ_BYTES bytes from FILE + * and zero the final PAGE_ZERO_BYTES bytes. */ + size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; + size_t page_zero_bytes = PGSIZE - page_read_bytes; + + /* Get a page of memory. */ + uint8_t *kpage = palloc_get_page (PAL_USER); + if (kpage == NULL) + return false; + + /* Load this page. */ + if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes) { + palloc_free_page (kpage); + return false; + } + memset (kpage + page_read_bytes, 0, page_zero_bytes); + + /* Add the page to the process's address space. */ + if (!install_page (upage, kpage, writable)) { + printf("fail\n"); + palloc_free_page (kpage); + return false; + } + + /* Advance. */ + read_bytes -= page_read_bytes; + zero_bytes -= page_zero_bytes; + upage += PGSIZE; + } + return true; } /* Create a minimal stack by mapping a zeroed page at the USER_STACK */ static bool setup_stack (struct intr_frame *if_) { - uint8_t *kpage; - bool success = false; - - kpage = palloc_get_page (PAL_USER | PAL_ZERO); - if (kpage != NULL) { - success = install_page (((uint8_t *) USER_STACK) - PGSIZE, kpage, true); - if (success) - if_->rsp = USER_STACK; - else - palloc_free_page (kpage); - } - return success; + uint8_t *kpage; + bool success = false; + + kpage = palloc_get_page (PAL_USER | PAL_ZERO); + if (kpage != NULL) { + success = install_page (((uint8_t *) USER_STACK) - PGSIZE, kpage, true); + if (success) + if_->rsp = USER_STACK; + else + palloc_free_page (kpage); + } + return success; } /* Adds a mapping from user virtual address UPAGE to kernel @@ -562,12 +728,12 @@ setup_stack (struct intr_frame *if_) { * if memory allocation fails. */ static bool install_page (void *upage, void *kpage, bool writable) { - struct thread *t = thread_current (); + struct thread *t = thread_current (); - /* Verify that there's not already a page at that virtual - * address, then map our page there. */ - return (pml4_get_page (t->pml4, upage) == NULL - && pml4_set_page (t->pml4, upage, kpage, writable)); + /* Verify that there's not already a page at that virtual + * address, then map our page there. */ + return (pml4_get_page (t->pml4, upage) == NULL + && pml4_set_page (t->pml4, upage, kpage, writable)); } #else /* From here, codes will be used after project 3. @@ -576,9 +742,9 @@ install_page (void *upage, void *kpage, bool writable) { static bool lazy_load_segment (struct page *page, void *aux) { - /* TODO: Load the segment from the file */ - /* TODO: This called when the first page fault occurs on address VA. */ - /* TODO: VA is available when calling this function. */ + /* TODO: Load the segment from the file */ + /* TODO: This called when the first page fault occurs on address VA. */ + /* TODO: VA is available when calling this function. */ } /* Loads a segment starting at offset OFS in FILE at address @@ -597,43 +763,43 @@ lazy_load_segment (struct page *page, void *aux) { * or disk read error occurs. */ static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, - uint32_t read_bytes, uint32_t zero_bytes, bool writable) { - ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); - ASSERT (pg_ofs (upage) == 0); - ASSERT (ofs % PGSIZE == 0); - - while (read_bytes > 0 || zero_bytes > 0) { - /* Do calculate how to fill this page. - * We will read PAGE_READ_BYTES bytes from FILE - * and zero the final PAGE_ZERO_BYTES bytes. */ - size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; - size_t page_zero_bytes = PGSIZE - page_read_bytes; - - /* TODO: Set up aux to pass information to the lazy_load_segment. */ - void *aux = NULL; - if (!vm_alloc_page_with_initializer (VM_ANON, upage, - writable, lazy_load_segment, aux)) - return false; - - /* Advance. */ - read_bytes -= page_read_bytes; - zero_bytes -= page_zero_bytes; - upage += PGSIZE; - } - return true; + uint32_t read_bytes, uint32_t zero_bytes, bool writable) { + ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); + ASSERT (pg_ofs (upage) == 0); + ASSERT (ofs % PGSIZE == 0); + + while (read_bytes > 0 || zero_bytes > 0) { + /* Do calculate how to fill this page. + * We will read PAGE_READ_BYTES bytes from FILE + * and zero the final PAGE_ZERO_BYTES bytes. */ + size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; + size_t page_zero_bytes = PGSIZE - page_read_bytes; + + /* TODO: Set up aux to pass information to the lazy_load_segment. */ + void *aux = NULL; + if (!vm_alloc_page_with_initializer (VM_ANON, upage, + writable, lazy_load_segment, aux)) + return false; + + /* Advance. */ + read_bytes -= page_read_bytes; + zero_bytes -= page_zero_bytes; + upage += PGSIZE; + } + return true; } /* Create a PAGE of stack at the USER_STACK. Return true on success. */ static bool setup_stack (struct intr_frame *if_) { - bool success = false; - void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE); + bool success = false; + void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE); - /* TODO: Map the stack on stack_bottom and claim the page immediately. - * TODO: If success, set the rsp accordingly. - * TODO: You should mark the page is stack. */ - /* TODO: Your code goes here */ + /* TODO: Map the stack on stack_bottom and claim the page immediately. + * TODO: If success, set the rsp accordingly. + * TODO: You should mark the page is stack. */ + /* TODO: Your code goes here */ - return success; + return success; } #endif /* VM */ diff --git a/userprog/syscall.c b/userprog/syscall.c index 1928191..8ed433b 100644 --- a/userprog/syscall.c +++ b/userprog/syscall.c @@ -1,5 +1,6 @@ #include "userprog/syscall.h" #include +#include #include #include "threads/interrupt.h" #include "threads/thread.h" @@ -7,6 +8,15 @@ #include "userprog/gdt.h" #include "threads/flags.h" #include "intrinsic.h" +#include "threads/init.h" +#include "filesys/filesys.h" +#include "filesys/file.h" +#include "filesys/inode.h" +#include "threads/synch.h" +#include "userprog/process.h" +#include "lib/string.h" +#include "threads/palloc.h" + void syscall_entry (void); void syscall_handler (struct intr_frame *); @@ -35,12 +45,225 @@ syscall_init (void) { * mode stack. Therefore, we masked the FLAG_FL. */ write_msr(MSR_SYSCALL_MASK, FLAG_IF | FLAG_TF | FLAG_DF | FLAG_IOPL | FLAG_AC | FLAG_NT); + + lock_init(&filesys_lock); +} + +void +sys_halt_handler(){ + power_off(); +} + +void +sys_exit_handler(int arg1){ + thread_current()->my_exit_code = arg1; + thread_exit(); +} + +bool sys_create_handler(char *filename, unsigned intial_size){ + bool result; + struct thread *curr = thread_current(); + if (!(filename + && is_user_vaddr(filename) + && pml4_get_page(curr->pml4, filename))) + { + curr->my_exit_code = -1; + thread_exit(); + } + lock_acquire(&filesys_lock); + result = filesys_create(filename, intial_size); + lock_release(&filesys_lock); + return result; +} + +bool sys_remove_handler(char *filename){ + bool result; + lock_acquire(&filesys_lock); + result = filesys_remove(filename); + lock_release(&filesys_lock); + return result; +} + +int sys_open_handler(char *filename){ + // return -1; + struct thread *curr = thread_current(); + if (!(filename + && is_user_vaddr(filename) + && pml4_get_page(curr->pml4, filename))) + { + curr->my_exit_code = -1; + thread_exit(); + } + lock_acquire(&filesys_lock); + struct file *file = filesys_open(filename); + lock_release(&filesys_lock); + if (!file) + return -1; + + struct file **f_table = curr->fd_table; + int i = FDBASE; + for (i; i < FDLIMIT; i++) + { + if (f_table[i] == NULL){ + f_table[i] = file; + return i; + } + } + lock_acquire(&filesys_lock); + file_close(file); + lock_release(&filesys_lock); + return -1; +} + +int sys_close_handler(int fd){ + struct file **f_table = thread_current()->fd_table; + if (fd < FDBASE || fd >= FDLIMIT){ + thread_current()->my_exit_code = -1; + thread_exit(); + } + else if (f_table[fd]){ + lock_acquire(&filesys_lock); + file_close(f_table[fd]); + lock_release(&filesys_lock); + f_table[fd] = NULL; + } + else{ + thread_current()->my_exit_code = -1; + thread_exit(); + } +} + +int sys_filesize_handler(int fd){ + int result; + struct thread *curr = thread_current(); + struct file **f_table = curr->fd_table; + struct file *f = f_table[fd]; + lock_acquire(&filesys_lock); + result = file_length(f); + lock_release(&filesys_lock); + return result; +} + +int sys_read_handler(int fd, void* buffer, unsigned size){ + struct thread *curr = thread_current(); + int result; + if (fd < FDBASE || fd >= FDLIMIT || curr->fd_table[fd] == NULL || buffer == NULL || is_kernel_vaddr(buffer) || !pml4_get_page(curr->pml4, buffer)) + { + thread_current()->my_exit_code = -1; + thread_exit(); + } + struct file *f = curr->fd_table[fd]; + lock_acquire(&filesys_lock); + result = file_read(f, buffer, size); + lock_release(&filesys_lock); + return result; +} + +int sys_write_handler(int fd, void *buffer, unsigned size){ + struct thread *curr = thread_current(); + int result; + if (fd == 1) + { + putbuf(buffer, size); + return size; + } + if (fd < FDBASE || fd >= FDLIMIT || curr->fd_table[fd] == NULL || buffer == NULL || is_kernel_vaddr(buffer) || !pml4_get_page(curr->pml4, buffer)) + { + curr->my_exit_code = -1; + thread_exit(); + } + struct file *f = curr->fd_table[fd]; + lock_acquire(&filesys_lock); + result = file_write(f, buffer, size); + lock_release(&filesys_lock); + return result; +} + +int sys_fork_handler(char *thread_name, struct intr_frame *f){ + return process_fork(thread_name, f); +} + +int sys_wait_handler(int pid){ + return process_wait(pid); +} + +int sys_exec_handler(char * cmd_line){ + struct thread *curr = thread_current(); + if (!(cmd_line + && is_user_vaddr(cmd_line) + && pml4_get_page(curr->pml4, cmd_line))) + { + curr->my_exit_code = -1; + thread_exit(); + } + char *fn_copy = palloc_get_page (0); + strlcpy (fn_copy, cmd_line, PGSIZE); + return process_exec(fn_copy); +} + +void +sys_seek_handler(int fd, unsigned position){ + struct thread *curr = thread_current (); + struct file **f_table = curr->fd_table; + if (fd < FDBASE || fd >= FDLIMIT || curr->fd_table[fd] == NULL) { + curr->my_exit_code = -1; + thread_exit(); + } + struct file *f = f_table[fd]; + lock_acquire(&filesys_lock); + file_seek(f, position); + lock_release(&filesys_lock); + } /* The main system call interface */ void -syscall_handler (struct intr_frame *f UNUSED) { +syscall_handler (struct intr_frame *f) { // TODO: Your implementation goes here. - printf ("system call!\n"); - thread_exit (); + int syscall_n = f->R.rax; + switch (syscall_n) + { + case SYS_HALT: + sys_halt_handler(); + break; + case SYS_EXIT: + sys_exit_handler(f->R.rdi); + break; + case SYS_CREATE: + f->R.rax = sys_create_handler(f->R.rdi, f->R.rsi); + break; + case SYS_REMOVE: + f->R.rax = sys_remove_handler(f->R.rdi); + break; + case SYS_OPEN: + f->R.rax = sys_open_handler(f->R.rdi); + break; + case SYS_FILESIZE: + f->R.rax = sys_filesize_handler(f->R.rdi); + break; + case SYS_CLOSE: + f->R.rax = sys_close_handler(f->R.rdi); + break; + case SYS_READ: + f->R.rax = sys_read_handler(f->R.rdi, f->R.rsi, f->R.rdx); + break; + case SYS_WRITE: + f->R.rax = sys_write_handler(f->R.rdi, f->R.rsi, f->R.rdx); + break; + case SYS_FORK: + f->R.rax = sys_fork_handler(f->R.rdi, f); + break; + case SYS_WAIT: + f->R.rax = sys_wait_handler(f->R.rdi); + break; + case SYS_EXEC: + sys_exec_handler(f->R.rdi); + break; + case SYS_SEEK: + sys_seek_handler(f->R.rdi,f->R.rsi); + break; + + default: + break; + } }