diff --git a/pintos/include/threads/thread.h b/pintos/include/threads/thread.h index d863fa6..c025b47 100644 --- a/pintos/include/threads/thread.h +++ b/pintos/include/threads/thread.h @@ -117,6 +117,7 @@ struct thread { struct list fdt_block_list; struct file* current_file; + void* user_rsp; #endif #ifdef VM /* Table for whole virtual memory owned by thread. */ diff --git a/pintos/include/vm/vm.h b/pintos/include/vm/vm.h index 3d0da9e..7c2c277 100644 --- a/pintos/include/vm/vm.h +++ b/pintos/include/vm/vm.h @@ -1,8 +1,12 @@ #ifndef VM_VM_H #define VM_VM_H #include +#include #include "threads/palloc.h" +#include "threads/synch.h" #include "lib/kernel/hash.h" +#include "lib/kernel/list.h" +#include "lib/kernel/bitmap.h" #include "filesys/off_t.h" enum vm_type { @@ -36,6 +40,7 @@ enum vm_type { struct page_operations; struct thread; struct file; +struct disk; #define VM_TYPE(type) ((type) & 7) @@ -47,10 +52,11 @@ struct page { const struct page_operations *operations; void *va; /* Address in terms of user space */ struct frame *frame; /* Back reference for frame */ - /* Your implementation */ struct hash_elem hash_elem; bool writable; + struct thread *owner; + bool cow; /* Copy-on-write marker for shared pages */ /* Per-type data are binded into the union. * Each function automatically detects the current union */ @@ -64,10 +70,26 @@ struct page { }; }; -/* The representation of "frame" */ struct frame { void *kva; struct page *page; + struct list_elem frame_elem; + bool pinned; + bool on_table; + size_t refs; /* How many shared users reference this frame */ +}; + +struct frame_table { + struct list frames; + struct lock lock; + struct list_elem *clock_hand; +}; + +struct swap_table { + struct bitmap *slots; + struct lock lock; + struct disk *disk; + size_t sectors_per_slot; }; /* The function table for page operations. @@ -106,6 +128,7 @@ bool spt_remove_page (struct supplemental_page_table *spt, struct page *page); void vm_init (void); bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool write, bool not_present); +void vm_frame_free (struct frame *frame); #define vm_alloc_page(type, upage, writable) \ vm_alloc_page_with_initializer ((type), (upage), (writable), NULL, NULL) diff --git a/pintos/threads/thread.c b/pintos/threads/thread.c index 869f1ae..d0e5e90 100644 --- a/pintos/threads/thread.c +++ b/pintos/threads/thread.c @@ -501,6 +501,7 @@ static void init_thread(struct thread* t, const char* name, int priority) { #ifdef USERPROG list_init(&t->child_list); list_init(&t->fdt_block_list); + t->user_rsp = NULL; #endif #ifdef VM list_init(&t->mmap_list); diff --git a/pintos/userprog/exception.c b/pintos/userprog/exception.c index 5ca101b..012fb36 100644 --- a/pintos/userprog/exception.c +++ b/pintos/userprog/exception.c @@ -138,8 +138,11 @@ page_fault (struct intr_frame *f) { /* Determine cause. */ not_present = (f->error_code & PF_P) == 0; - write = (f->error_code & PF_W) != 0; - user = (f->error_code & PF_U) != 0; + write = (f->error_code & PF_W) != 0; + user = (f->error_code & PF_U) != 0; + + if (user) + thread_current()->user_rsp = f->rsp; #ifdef VM /* For project 3 and later. */ diff --git a/pintos/userprog/syscall.c b/pintos/userprog/syscall.c index a6e7e15..39e33b9 100644 --- a/pintos/userprog/syscall.c +++ b/pintos/userprog/syscall.c @@ -73,6 +73,7 @@ void syscall_init(void) { /* The main system call interface */ void syscall_handler(struct intr_frame* f) { + thread_current()->user_rsp = f->rsp; thread_current()->tf = *f; uint64_t arg1 = f->R.rdi, arg2 = f->R.rsi, arg3 = f->R.rdx, arg4 = f->R.r10, arg5 = f->R.r8; switch (f->R.rax) { diff --git a/pintos/userprog/validate.c b/pintos/userprog/validate.c index 934d562..022788f 100644 --- a/pintos/userprog/validate.c +++ b/pintos/userprog/validate.c @@ -1,6 +1,9 @@ #include "userprog/validate.h" #include "threads/thread.h" + +#include "vm/vm.h" + #include "threads/vaddr.h" static int64_t get_user(const uint8_t* uaddr); @@ -20,7 +23,16 @@ bool valid_address(const void* uaddr, bool write) { return true; page_addr = pg_round_down (uaddr); - return pml4_is_writable (t->pml4, page_addr); + if (pml4_is_writable (t->pml4, page_addr)) + return true; + +#ifdef VM + struct page *page = spt_find_page (&t->spt, page_addr); + if (page != NULL && page->cow) + return true; +#endif + + return false; } static int64_t get_user(const uint8_t* uaddr) { @@ -43,4 +55,4 @@ static int64_t put_user(uint8_t* udst, uint8_t byte) { : "=&a"(error_code), "=m"(*udst) : "q"(byte)); return error_code; -} \ No newline at end of file +} diff --git a/pintos/vm/anon.c b/pintos/vm/anon.c index 75a24c3..cee59b0 100644 --- a/pintos/vm/anon.c +++ b/pintos/vm/anon.c @@ -1,10 +1,15 @@ /* anon.c: Implementation of page for non-disk image (a.k.a. anonymous page). */ #include "vm/vm.h" +#include "threads/vaddr.h" #include "devices/disk.h" +#define SECTORS_PER_PAGE (PGSIZE / DISK_SECTOR_SIZE) + /* DO NOT MODIFY BELOW LINE */ static struct disk *swap_disk; +static struct bitmap *swap_bitmap; +static struct lock swap_lock; static bool anon_swap_in (struct page *page, void *kva); static bool anon_swap_out (struct page *page); static void anon_destroy (struct page *page); @@ -20,44 +25,113 @@ static const struct page_operations anon_ops = { /* Initialize the data for anonymous pages */ void vm_anon_init (void) { swap_disk = disk_get (1, 1); + + ASSERT(swap_disk != NULL); + + size_t swap_size = disk_size (swap_disk) / SECTORS_PER_PAGE; + swap_bitmap = bitmap_create (swap_size); + + ASSERT(swap_bitmap != NULL); + + lock_init(&swap_lock); } /* Initialize the file mapping */ bool anon_initializer (struct page *page, enum vm_type type, void *kva) { - struct anon_page *anon_page; + struct anon_page *anon_page = &page->anon; + page->operations = &anon_ops; - // anon_page = &page->anon; - // anon_page->swap_idx = BITMAP_ERROR; + anon_page->swap_idx = BITMAP_ERROR; return true; } /* Swap in the page by read contents from the swap disk. */ -static bool -anon_swap_in (struct page *page, void *kva) { +static bool anon_swap_in (struct page *page, void *kva) { + struct anon_page *anon_page = &page->anon; + size_t swap_idx; + disk_sector_t start_sector; + ASSERT (page != NULL); ASSERT (page->frame != NULL); + + if (kva == NULL) + return false; + + swap_idx = anon_page->swap_idx; + + if (swap_idx == BITMAP_ERROR) + return true; + + lock_acquire (&swap_lock); + + if (!bitmap_test (swap_bitmap, swap_idx)) { + lock_release (&swap_lock); + return false; + } + + start_sector = swap_idx * SECTORS_PER_PAGE; + + for (size_t i = 0; i < SECTORS_PER_PAGE; i++) { + disk_read (swap_disk, start_sector + i, kva + DISK_SECTOR_SIZE * i); + } + + bitmap_reset (swap_bitmap, swap_idx); + anon_page->swap_idx = BITMAP_ERROR; + + lock_release (&swap_lock); return true; } /* Swap out the page by writing contents to the swap disk. */ -static bool -anon_swap_out (struct page *page) { +static bool anon_swap_out (struct page *page) { + struct anon_page *anon_page = &page->anon; + struct frame *frame = page->frame; + size_t swap_idx; + disk_sector_t start_sector; + struct thread *t = page->owner; + ASSERT (page != NULL); + ASSERT (frame != NULL); + ASSERT (frame->refs == 0); + + if (t == NULL) + t = thread_current (); + + lock_acquire (&swap_lock); + swap_idx = bitmap_scan_and_flip (swap_bitmap, 0, 1, false); + + if (swap_idx == BITMAP_ERROR) { + lock_release (&swap_lock); + PANIC ("full"); + } + + start_sector = swap_idx * SECTORS_PER_PAGE; + + for (size_t i = 0; i < SECTORS_PER_PAGE; i++) { + disk_write (swap_disk, start_sector + i, frame->kva + DISK_SECTOR_SIZE * i); + } + + anon_page->swap_idx = swap_idx; + lock_release (&swap_lock); + + pml4_clear_page (t->pml4, page->va); + frame->page = NULL; + page->frame = NULL; return true; } /* Destroy the anonymous page. PAGE will be freed by the caller. */ static void anon_destroy (struct page *page) { - struct anon_page *anon_page = &page->anon; - ASSERT (page != NULL); if (page->frame != NULL) { - struct thread *t = thread_current (); + struct thread *t = page->owner; + + if (t == NULL) + t = thread_current (); pml4_clear_page (t->pml4, page->va); - palloc_free_page (page->frame->kva); - free (page->frame); + vm_frame_free (page->frame); page->frame = NULL; } } diff --git a/pintos/vm/file.c b/pintos/vm/file.c index 01d368e..b40257e 100644 --- a/pintos/vm/file.c +++ b/pintos/vm/file.c @@ -61,12 +61,15 @@ static bool file_backed_swap_in (struct page *page, void *kva) { /* Swap out the page by writeback contents to the file. */ static bool file_backed_swap_out (struct page *page) { struct frame *frame = page->frame; - struct thread *t = thread_current (); + struct thread *t = page->owner; struct file_page *file_page = &page->file; if (frame == NULL) return true; + if (t == NULL) + t = thread_current (); + if (file_page->read_bytes > 0 && pml4_is_dirty (t->pml4, page->va)) { lock_acquire (&file_lock); file_write_at (file_page->file, frame->kva, file_page->read_bytes, file_page->ofs); @@ -74,8 +77,7 @@ static bool file_backed_swap_out (struct page *page) { } pml4_clear_page (t->pml4, page->va); - // palloc_free_page (frame->kva); - // free (frame); + frame->page = NULL; page->frame = NULL; return true; } diff --git a/pintos/vm/frame.c b/pintos/vm/frame.c new file mode 100644 index 0000000..adc4889 --- /dev/null +++ b/pintos/vm/frame.c @@ -0,0 +1,68 @@ +#include "vm/vm.h" +#include "threads/malloc.h" + +extern struct frame_table frame_table; + +void frame_table_add (struct frame *frame) { + if (frame == NULL || frame->on_table) + return; + + lock_acquire (&frame_table.lock); + + if (!frame->on_table) { + list_push_back (&frame_table.frames, &frame->frame_elem); + frame->on_table = true; + + if (frame_table.clock_hand == NULL) + frame_table.clock_hand = &frame->frame_elem; + } + + lock_release (&frame_table.lock); +} + +static void frame_table_remove (struct frame *frame) { + bool hand; + struct list_elem *next; + bool empty; + + if (frame == NULL) + return; + + lock_acquire (&frame_table.lock); + + if (!frame->on_table) { + lock_release (&frame_table.lock); + return; + } + + hand = frame_table.clock_hand == &frame->frame_elem; + next = list_next (&frame->frame_elem); + list_remove (&frame->frame_elem); + + empty = list_empty (&frame_table.frames); + + if (empty) + frame_table.clock_hand = NULL; + + if (!empty && hand) { + frame_table.clock_hand = (next == list_end (&frame_table.frames)) + ? list_begin (&frame_table.frames) : next; + } + + frame->on_table = false; + lock_release (&frame_table.lock); +} + +void vm_frame_free (struct frame *frame) { + if (frame == NULL) + return; + + if (frame->refs > 0) { + frame->refs--; + return; + } + + frame_table_remove (frame); + palloc_free_page (frame->kva); + free (frame); +} diff --git a/pintos/vm/targets.mk b/pintos/vm/targets.mk index 9ff6f2c..5ab6f8f 100644 --- a/pintos/vm/targets.mk +++ b/pintos/vm/targets.mk @@ -3,3 +3,4 @@ vm_SRC += vm/uninit.c # Uninitialized page vm_SRC += vm/anon.c # Anonymous page vm_SRC += vm/file.c # File mapped page vm_SRC += vm/inspect.c # Testing utility +vm_SRC += vm/frame.c # Frame helpers diff --git a/pintos/vm/vm.c b/pintos/vm/vm.c index c6c07b8..9388821 100644 --- a/pintos/vm/vm.c +++ b/pintos/vm/vm.c @@ -2,14 +2,21 @@ #include #include "threads/malloc.h" +#include "threads/palloc.h" #include "threads/vaddr.h" #include "threads/mmu.h" #include "threads/thread.h" +#include "threads/synch.h" #include "userprog/process.h" #include "vm/vm.h" #include "vm/inspect.h" #include "filesys/file.h" #include "vm/file.h" + +struct frame_table frame_table; + +void frame_table_add (struct frame *frame); +void vm_frame_free (struct frame *frame); /* Initializes the virtual memory subsystem by invoking each subsystem's * intialize codes. */ void @@ -21,7 +28,9 @@ vm_init (void) { #endif register_inspect_intr (); /* DO NOT MODIFY UPPER LINES. */ - /* TODO: Your code goes here. */ + list_init (&frame_table.frames); + lock_init (&frame_table.lock); + frame_table.clock_hand = NULL; } /* Get the type of the page. This function is useful if you want to know the @@ -80,6 +89,8 @@ bool vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writab uninit_new (page, upage, init, type, aux, initializer); page->writable = writable; + page->cow = false; + page->owner = thread_current (); if (!spt_insert_page (spt, page)) { free (page); @@ -133,39 +144,84 @@ bool spt_remove_page (struct supplemental_page_table *spt, struct page *page) { return false; } -/* Get the struct frame, that will be evicted. */ -static struct frame * -vm_get_victim (void) { - struct frame *victim = NULL; - /* TODO: The policy for eviction is up to you. */ +static struct frame *vm_get_victim (void) { + struct frame *victim = NULL; - return victim; + lock_acquire(&frame_table.lock); + + if (list_empty(&frame_table.frames)) { + lock_release(&frame_table.lock); + return NULL; + } + + if (frame_table.clock_hand == NULL || frame_table.clock_hand == list_end(&frame_table.frames)) + frame_table.clock_hand = list_begin(&frame_table.frames); + + struct list_elem *cur = frame_table.clock_hand; + size_t n = list_size(&frame_table.frames); + + for (size_t i = 0; i < n; i++) { + struct frame *frame = list_entry(cur, struct frame, frame_elem); + + if (!frame->pinned && frame->refs == 0) { + victim = frame; + cur = list_next(cur); + if (cur == list_end(&frame_table.frames)) + cur = list_begin(&frame_table.frames); + + frame_table.clock_hand = cur; + break; + } + + cur = list_next(cur); + + if (cur == list_end(&frame_table.frames)) + cur = list_begin(&frame_table.frames); + } + + lock_release(&frame_table.lock); + return victim; } /* Evict one page and return the corresponding frame. * Return NULL on error.*/ -static struct frame * -vm_evict_frame (void) { - struct frame *victim UNUSED = vm_get_victim (); - /* TODO: swap out the victim and return the evicted frame. */ +static struct frame *vm_evict_frame (void) { + struct frame *victim = vm_get_victim (); + struct page *page; + + if (victim == NULL) + PANIC ("no frame"); + + page = victim->page; + + if (page != NULL) { + if (!swap_out (page)) + PANIC ("swap out failed"); - return NULL; + page->frame = NULL; + } + + victim->page = NULL; + victim->pinned = false; + + return victim; } -/* palloc() and get frame. If there is no available page, evict the page - * and return it. This always return valid address. That is, if the user pool - * memory is full, this function evicts the frame to get the available memory - * space.*/ static struct frame * vm_get_frame (void) { struct frame *frame = NULL; void *kva; kva = palloc_get_page(PAL_USER); if (kva == NULL) { - PANIC("todo"); + frame = vm_evict_frame (); + if (frame == NULL) + PANIC ("cannot evict frame"); + frame->refs = 0; + return frame; } frame = malloc(sizeof(struct frame)); + if (frame == NULL) { palloc_free_page(kva); return NULL; @@ -173,6 +229,9 @@ static struct frame * vm_get_frame (void) { frame->kva = kva; frame->page = NULL; + frame->pinned = false; + frame->on_table = false; + frame->refs = 0; ASSERT (frame != NULL); ASSERT (frame->page == NULL); @@ -203,7 +262,7 @@ bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool writ spt = &thread_current ()->spt; - if (addr == NULL || is_kernel_vaddr (addr) || !not_present) + if (addr == NULL || is_kernel_vaddr (addr)) return false; page_addr = pg_round_down (addr); @@ -219,7 +278,46 @@ bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool writ return false; } - if (write && !page->writable) + if (write && !page->writable) { + struct frame *frame = page->frame; + struct frame *new_frame; + + if (frame == NULL || !page->cow) + return false; + + new_frame = vm_get_frame (); + if (new_frame == NULL) + return false; + + memcpy (new_frame->kva, frame->kva, PGSIZE); + new_frame->page = page; + new_frame->pinned = true; + + if (!pml4_set_page (thread_current ()->pml4, page->va, new_frame->kva, true)) { + goto fail; + } + + frame_table_add (new_frame); + new_frame->pinned = false; + + if (frame->refs > 0) + frame->refs--; + + page->frame = new_frame; + page->writable = true; + page->cow = false; + + return true; + +fail: + new_frame->page = NULL; + new_frame->on_table = false; + new_frame->refs = 0; + vm_frame_free (new_frame); + return false; + } + + if (!not_present) return false; return vm_do_claim_page (page); @@ -228,8 +326,7 @@ bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool writ /* Free the page. * DO NOT MODIFY THIS FUNCTION. */ -void -vm_dealloc_page (struct page *page) { +void vm_dealloc_page (struct page *page) { destroy (page); free (page); } @@ -243,28 +340,63 @@ bool vm_claim_page (void *va) { if (page == NULL) return false; + page->cow = false; return vm_do_claim_page (page); } /* Claim the PAGE and set up the mmu. */ static bool vm_do_claim_page (struct page *page) { struct frame *frame; + struct thread *owner; + bool new_frame = false; if (page == NULL) return false; - - frame = vm_get_frame (); - frame->page = page; - page->frame = frame; - if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable)){ - palloc_free_page(frame->kva); - free(frame); - page->frame = NULL; + frame = page->frame; + if (frame == NULL) { + frame = vm_get_frame (); + if (frame == NULL) + return false; + new_frame = true; + frame->page = page; + page->frame = frame; + } + + owner = page->owner; + if (owner == NULL) { + owner = thread_current (); + page->owner = owner; + } + + frame->pinned = true; + + if (!pml4_set_page(owner->pml4, page->va, frame->kva, page->writable)){ + frame->pinned = false; + if (new_frame) { + frame->page = NULL; + page->frame = NULL; + vm_frame_free (frame); + } return false; } - return swap_in (page, frame->kva); + if (!swap_in (page, frame->kva)) { + pml4_clear_page (owner->pml4, page->va); + frame->pinned = false; + if (new_frame) { + frame->page = NULL; + page->frame = NULL; + vm_frame_free (frame); + } + return false; + } + + if (!frame->on_table) + frame_table_add (frame); + + frame->pinned = false; + return true; } /* Initialize new supplemental page table */ @@ -310,6 +442,36 @@ bool supplemental_page_table_copy (struct supplemental_page_table *dst, struct s return true; } +static void share_page_frame (struct page *dst_page, struct page *src_page) { + struct frame *frame = src_page->frame; + + if (frame == NULL) + return; + + frame->refs++; + dst_page->frame = frame; + dst_page->operations = src_page->operations; + dst_page->cow = true; + dst_page->writable = false; + src_page->cow = true; + src_page->writable = false; + if (src_page->owner != NULL) + pml4_set_page (src_page->owner->pml4, src_page->va, frame->kva, false); + + switch (page_get_type (src_page)) { + case VM_ANON: + dst_page->anon = src_page->anon; + break; + + case VM_FILE: + dst_page->file = src_page->file; + break; + + default: + break; + } +} + static bool copy_uninit_page (struct supplemental_page_table *dst, struct page *src_page) { struct uninit_page *uninit = &src_page->uninit; void *aux = uninit->aux; @@ -356,11 +518,11 @@ static bool copy_anon_page (struct supplemental_page_table *dst, struct page *sr if (src_page->frame == NULL) return true; - if (!vm_claim_page (src_page->va)) + dst_page = spt_find_page (dst, src_page->va); + if (dst_page == NULL) return false; - dst_page = spt_find_page (dst, src_page->va); - memcpy (dst_page->frame->kva, src_page->frame->kva, PGSIZE); + share_page_frame (dst_page, src_page); return true; } @@ -384,12 +546,8 @@ static bool copy_file_page(struct supplemental_page_table *dst_spt, struct page if (child_page == NULL) return false; - if (src_page->frame != NULL) { - if (!vm_claim_page(va)) - return false; - - memcpy(child_page->frame->kva, src_page->frame->kva, PGSIZE); - } + if (src_page->frame != NULL) + share_page_frame (child_page, src_page); return true; } @@ -424,7 +582,13 @@ static bool should_grow_stack (struct intr_frame *f, void *addr, bool user) { uint8_t *rsp = NULL; uint8_t *fault_addr = addr; - rsp = user ? (uint8_t *) f->rsp : (uint8_t *) thread_current ()->tf.rsp; + if (user) + rsp = (uint8_t *) f->rsp; + else + rsp = (uint8_t *) thread_current ()->user_rsp; + + if (rsp == NULL) + return false; if (fault_addr >= (uint8_t *) USER_STACK) return false; diff --git a/vm.c b/vm.c deleted file mode 100644 index c6c07b8..0000000 --- a/vm.c +++ /dev/null @@ -1,439 +0,0 @@ -/* vm.c: Generic interface for virtual memory objects. */ - -#include -#include "threads/malloc.h" -#include "threads/vaddr.h" -#include "threads/mmu.h" -#include "threads/thread.h" -#include "userprog/process.h" -#include "vm/vm.h" -#include "vm/inspect.h" -#include "filesys/file.h" -#include "vm/file.h" -/* Initializes the virtual memory subsystem by invoking each subsystem's - * intialize codes. */ -void -vm_init (void) { - vm_anon_init (); - vm_file_init (); -#ifdef EFILESYS /* For project 4 */ - pagecache_init (); -#endif - register_inspect_intr (); - /* DO NOT MODIFY UPPER LINES. */ - /* TODO: Your code goes here. */ -} - -/* Get the type of the page. This function is useful if you want to know the - * type of the page after it will be initialized. - * This function is fully implemented now. */ -enum vm_type -page_get_type (struct page *page) { - int ty = VM_TYPE (page->operations->type); - switch (ty) { - case VM_UNINIT: - return VM_TYPE (page->uninit.type); - default: - return ty; - } -} - -/* Helpers */ -static struct frame *vm_get_victim (void); -static bool vm_do_claim_page (struct page *page); -static struct frame *vm_evict_frame (void); -static uint64_t page_hash (const struct hash_elem *e, void *aux); -static bool page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux); -static bool should_grow_stack (struct intr_frame *f, void *addr, bool user); -static bool vm_stack_growth (void *addr); -static void spt_destroy_page (struct hash_elem *elem, void *aux); -static bool copy_uninit_page (struct supplemental_page_table *dst, struct page *src_page); -static bool copy_anon_page (struct supplemental_page_table *dst, struct page *src_page); -static bool copy_file_page(struct supplemental_page_table *dst_spt, struct page *src_page); - -#define STACK_LIMIT (1 << 20) -#define STACK_HEURISTIC 8 - -bool vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writable, vm_initializer *init, void *aux) { - ASSERT (VM_TYPE(type) != VM_UNINIT) - - struct supplemental_page_table *spt = &thread_current ()->spt; - - if (spt_find_page (spt, upage) == NULL) { - struct page *page = malloc (sizeof *page); - bool (*initializer) (struct page *, enum vm_type, void *) = NULL; - - if (page == NULL) - goto err; - - switch (VM_TYPE (type)) { - case VM_ANON: - initializer = anon_initializer; - break; - case VM_FILE: - initializer = file_backed_initializer; - break; - default: - free (page); - goto err; - } - - uninit_new (page, upage, init, type, aux, initializer); - page->writable = writable; - - if (!spt_insert_page (spt, page)) { - free (page); - goto err; - } - return true; - } -err: - return false; -} - -/* Find VA from spt and return page. On error, return NULL. */ -struct page *spt_find_page (struct supplemental_page_table *spt, void *va) { - /* TODO: Fill this function. */ - struct page dummy_page; - struct hash_elem *elem; - - if (spt == NULL || va == NULL) - return NULL; - - dummy_page.va = pg_round_down (va); - elem = hash_find (&spt->hash_table, &dummy_page.hash_elem); - - if (elem == NULL) - return NULL; - - return hash_entry(elem, struct page, hash_elem); -} - -/* Insert PAGE into spt with validation. */ -bool spt_insert_page (struct supplemental_page_table *spt, struct page *page) { - if (spt == NULL || page == NULL || page->va == NULL) - return false; - - // page->va = pg_round_down (page->va); - return hash_insert (&spt->hash_table, &page->hash_elem) == NULL; -} - -bool spt_remove_page (struct supplemental_page_table *spt, struct page *page) { - struct hash_elem *result; - - if (spt == NULL || page == NULL) - return false; - - result = hash_delete(&spt->hash_table, &page->hash_elem); - - if (result != NULL) { - vm_dealloc_page (page); - return true; - } - return false; -} - -/* Get the struct frame, that will be evicted. */ -static struct frame * -vm_get_victim (void) { - struct frame *victim = NULL; - /* TODO: The policy for eviction is up to you. */ - - return victim; -} - -/* Evict one page and return the corresponding frame. - * Return NULL on error.*/ -static struct frame * -vm_evict_frame (void) { - struct frame *victim UNUSED = vm_get_victim (); - /* TODO: swap out the victim and return the evicted frame. */ - - return NULL; -} - -/* palloc() and get frame. If there is no available page, evict the page - * and return it. This always return valid address. That is, if the user pool - * memory is full, this function evicts the frame to get the available memory - * space.*/ -static struct frame * vm_get_frame (void) { - struct frame *frame = NULL; - void *kva; - - kva = palloc_get_page(PAL_USER); - if (kva == NULL) { - PANIC("todo"); - } - - frame = malloc(sizeof(struct frame)); - if (frame == NULL) { - palloc_free_page(kva); - return NULL; - } - - frame->kva = kva; - frame->page = NULL; - - ASSERT (frame != NULL); - ASSERT (frame->page == NULL); - return frame; -} - -static bool vm_stack_growth (void *addr) { - struct supplemental_page_table *spt; - void *stack_bottom = pg_round_down (addr); - - spt = &thread_current ()->spt; - - if (spt_find_page (spt, stack_bottom) != NULL) - return true; - - return vm_alloc_page(VM_ANON | VM_MARKER_0, stack_bottom, true); -} - -/* Handle the fault on write_protected page */ -static bool -vm_handle_wp (struct page *page UNUSED) { -} - -bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, bool write, bool not_present) { - struct supplemental_page_table *spt; - struct page *page; - void *page_addr; - - spt = &thread_current ()->spt; - - if (addr == NULL || is_kernel_vaddr (addr) || !not_present) - return false; - - page_addr = pg_round_down (addr); - page = spt_find_page (spt, page_addr); - - if (page == NULL) { - if (!should_grow_stack (f, addr, user) || !vm_stack_growth (page_addr)) - return false; - - page = spt_find_page (spt, page_addr); - - if (page == NULL) - return false; - } - - if (write && !page->writable) - return false; - - return vm_do_claim_page (page); -} - - -/* Free the page. - * DO NOT MODIFY THIS FUNCTION. */ -void -vm_dealloc_page (struct page *page) { - destroy (page); - free (page); -} - -/* Claim the page that allocate on VA. */ -bool vm_claim_page (void *va) { - struct page *page = NULL; - - page = spt_find_page(&thread_current()->spt, va); - - if (page == NULL) - return false; - - return vm_do_claim_page (page); -} - -/* Claim the PAGE and set up the mmu. */ -static bool vm_do_claim_page (struct page *page) { - struct frame *frame; - - if (page == NULL) - return false; - - frame = vm_get_frame (); - frame->page = page; - page->frame = frame; - - if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable)){ - palloc_free_page(frame->kva); - free(frame); - page->frame = NULL; - return false; - } - - return swap_in (page, frame->kva); -} - -/* Initialize new supplemental page table */ -void supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) { - hash_init(&spt->hash_table, page_hash, page_less, NULL); -} - -/* Copy supplemental page table from src to dst */ -bool supplemental_page_table_copy (struct supplemental_page_table *dst, struct supplemental_page_table *src) { - struct hash_iterator i; - - hash_first (&i, &src->hash_table); - - while (hash_next (&i)) { - struct page *src_page; - enum vm_type type; - - src_page = hash_entry (hash_cur (&i), struct page, hash_elem); - type = page_get_type (src_page); - - switch (type) { - - case VM_UNINIT: - if (!copy_uninit_page (dst, src_page)) - return false; - break; - - case VM_ANON: - if (!copy_anon_page (dst, src_page)) - return false; - break; - - case VM_FILE: - if (!copy_file_page(dst, src_page)) - return false; - break; - - default: - break; - } - } - - return true; -} - -static bool copy_uninit_page (struct supplemental_page_table *dst, struct page *src_page) { - struct uninit_page *uninit = &src_page->uninit; - void *aux = uninit->aux; - lazy_load_info *dst_info = NULL; - - if (aux != NULL) { - lazy_load_info *src_info = aux; - dst_info = malloc (sizeof *dst_info); - - if (dst_info == NULL) - return false; - - memcpy (dst_info, src_info, sizeof *dst_info); - - if (src_info->file != NULL) { - dst_info->file = file_reopen (src_info->file); - if (dst_info->file == NULL) - goto fail; - } - aux = dst_info; - } - - if (!vm_alloc_page_with_initializer (uninit->type, src_page->va, src_page->writable, uninit->init, aux)) - goto fail; - - return true; - -fail: - if (dst_info != NULL) { - if (dst_info->file != NULL) - file_close (dst_info->file); - free (dst_info); - } - return false; -} - - -static bool copy_anon_page (struct supplemental_page_table *dst, struct page *src_page) { - struct page *dst_page; - - if (!vm_alloc_page (VM_ANON, src_page->va, src_page->writable)) - return false; - - if (src_page->frame == NULL) - return true; - - if (!vm_claim_page (src_page->va)) - return false; - - dst_page = spt_find_page (dst, src_page->va); - memcpy (dst_page->frame->kva, src_page->frame->kva, PGSIZE); - return true; -} - -static bool copy_file_page(struct supplemental_page_table *dst_spt, struct page *src_page){ - void *va = src_page->va; - bool writable = src_page->writable; - struct file_page *src_fp = &src_page->file; - - struct file_page *aux = malloc(sizeof *aux); - if (aux == NULL) - return false; - - *aux = *src_fp; - - if (!vm_alloc_page_with_initializer(VM_FILE, va, writable, lazy_load_file, aux)) { - free(aux); - return false; - } - - struct page *child_page = spt_find_page(dst_spt, va); - if (child_page == NULL) - return false; - - if (src_page->frame != NULL) { - if (!vm_claim_page(va)) - return false; - - memcpy(child_page->frame->kva, src_page->frame->kva, PGSIZE); - } - - return true; -} - -void supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) { - if (spt == NULL) - return; - - hash_destroy (&spt->hash_table, spt_destroy_page); -} - -static void spt_destroy_page (struct hash_elem *elem, void *aux UNUSED) { - struct page *page = hash_entry (elem, struct page, hash_elem); - - vm_dealloc_page (page); -} - -static uint64_t page_hash (const struct hash_elem *e, void *aux UNUSED) { - const struct page *page; - page = hash_entry (e, struct page, hash_elem); - return hash_bytes (&page->va, sizeof page->va); -} - -static bool page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) { - const struct page *page_a, *page_b; - page_a = hash_entry (a, struct page, hash_elem); - page_b = hash_entry (b, struct page, hash_elem); - return page_a->va < page_b->va; -} - -static bool should_grow_stack (struct intr_frame *f, void *addr, bool user) { - uint8_t *rsp = NULL; - uint8_t *fault_addr = addr; - - rsp = user ? (uint8_t *) f->rsp : (uint8_t *) thread_current ()->tf.rsp; - - if (fault_addr >= (uint8_t *) USER_STACK) - return false; - - if (fault_addr < (uint8_t *) USER_STACK - STACK_LIMIT) - return false; - - if (fault_addr < rsp - STACK_HEURISTIC) - return false; - - return true; -}