|
|
|
|
@@ -1,5 +1,6 @@
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include "stackgrowth.h"
|
|
|
|
|
#include "frame.h"
|
|
|
|
|
#include "threads/palloc.h"
|
|
|
|
|
#include "threads/thread.h"
|
|
|
|
|
#include "threads/vaddr.h"
|
|
|
|
|
@@ -7,44 +8,53 @@
|
|
|
|
|
|
|
|
|
|
#define MAX_STACK_ACCESS_DIST 32
|
|
|
|
|
|
|
|
|
|
static bool needs_new_page (const void *addr, const void *esp);
|
|
|
|
|
static bool is_stack_fault (const void *addr, const void *esp);
|
|
|
|
|
static bool grow_stack (const void *addr);
|
|
|
|
|
|
|
|
|
|
/* Determine whether a particular page fault occured due to a stack
|
|
|
|
|
access below the stack pointer that should induce stack growth, and
|
|
|
|
|
if so grow the stack by a single page (capped at MAX_STACK_SIZE). */
|
|
|
|
|
bool
|
|
|
|
|
try_alloc_new_page (const void *ptr, const void *esp)
|
|
|
|
|
handle_stack_fault (const void *ptr, const void *esp)
|
|
|
|
|
{
|
|
|
|
|
return needs_new_page (ptr, esp) && grow_stack (ptr);
|
|
|
|
|
return is_stack_fault (ptr, esp) && grow_stack (ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validates a given address for being a stack query and not a generic erroneous
|
|
|
|
|
address
|
|
|
|
|
*/
|
|
|
|
|
/* Determines whether a particular page fault appears to be caused by
|
|
|
|
|
a stack access that should induce dynamic stack growth. Stack size
|
|
|
|
|
is capped at MAX_STACK_SIZE. */
|
|
|
|
|
static bool
|
|
|
|
|
needs_new_page (const void *addr, const void *esp)
|
|
|
|
|
is_stack_fault (const void *addr, const void *esp)
|
|
|
|
|
{
|
|
|
|
|
return (is_user_vaddr (addr) &&
|
|
|
|
|
(uint32_t*)addr >= ((uint32_t*)esp - MAX_STACK_ACCESS_DIST) &&
|
|
|
|
|
((PHYS_BASE - pg_round_down (addr))
|
|
|
|
|
<= MAX_STACK_SIZE));
|
|
|
|
|
return (is_user_vaddr (addr) &&
|
|
|
|
|
(uint32_t*)addr >= ((uint32_t*)esp - MAX_STACK_ACCESS_DIST) &&
|
|
|
|
|
((PHYS_BASE - pg_round_down (addr)) <= MAX_STACK_SIZE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Extends the stack by the necessary number of pages */
|
|
|
|
|
/* Grows the stack of the process running inside the current thread by a single
|
|
|
|
|
page given a user virtual address inside of the page wherein the new section
|
|
|
|
|
of the stack should be allocated. */
|
|
|
|
|
static bool
|
|
|
|
|
grow_stack (const void *addr)
|
|
|
|
|
{
|
|
|
|
|
struct thread *t = thread_current ();
|
|
|
|
|
void *last_page = pg_round_down (addr);
|
|
|
|
|
|
|
|
|
|
uint8_t *new_page = palloc_get_page (PAL_USER | PAL_ZERO);
|
|
|
|
|
if ( new_page == NULL)
|
|
|
|
|
/* This function should only be called when dealing with a faulting stack
|
|
|
|
|
access that induces stack growth, so the provided address shouldn't be
|
|
|
|
|
present in a page within the current thread's page directory. */
|
|
|
|
|
ASSERT (pagedir_get_page (t->pagedir, last_page) == NULL);
|
|
|
|
|
|
|
|
|
|
uint8_t *new_page = frame_alloc (PAL_ZERO, last_page, t);
|
|
|
|
|
if (new_page == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
bool added_page = pagedir_get_page (t->pagedir, last_page) == NULL
|
|
|
|
|
&& pagedir_set_page (t->pagedir, last_page, new_page, true);
|
|
|
|
|
|
|
|
|
|
if (!added_page) {
|
|
|
|
|
palloc_free_page (new_page);
|
|
|
|
|
if (!pagedir_set_page (t->pagedir, last_page, new_page, true))
|
|
|
|
|
{
|
|
|
|
|
frame_free (new_page);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|