Merge branch 'vm/stack-growth/saleh' into vm/virtual-memory/saleh

# Conflicts:
#	src/userprog/exception.c
#	src/userprog/process.c
#	src/userprog/syscall.c
#	src/vm/frame.c
#	src/vm/page.c
#	src/vm/page.h
This commit is contained in:
sBubshait
2024-12-05 00:51:03 +00:00
3 changed files with 149 additions and 64 deletions

View File

@@ -3,16 +3,24 @@
#include <stdio.h>
#include "userprog/gdt.h"
#include "userprog/pagedir.h"
#include "userprog/process.h"
#include "threads/interrupt.h"
#include "threads/palloc.h"
#include "threads/thread.h"
#include "threads/vaddr.h"
#include "vm/page.h"
#define MAX_STACK_SIZE (8 * 1024 * 1024) // 8MB
#define MAX_STACK_OFFSET 32 // 32 bytes offset below stack pointer (ESP)
/* Number of page faults processed. */
static long long page_fault_cnt;
static void kill (struct intr_frame *);
static void page_fault (struct intr_frame *);
static bool is_valid_stack_access (const void *fault_addr, const void *esp);
static bool grow_stack (void *upage);
bool try_fetch_page (void *upage, bool write);
/* Registers handlers for interrupts that can be caused by user
@@ -149,20 +157,26 @@ page_fault (struct intr_frame *f)
write = (f->error_code & PF_W) != 0;
user = (f->error_code & PF_U) != 0;
if (!user)
if (!user || !not_present)
{
f->eip = (void *)f->eax;
f->eax = 0xffffffff;
return;
}
/* If the fault address is in a user page that is not present, then it might
just need to be lazily loaded. So, we check our SPT to see if the page
be just that the stack needs to grow or that it needs to be lazily loaded.
So we attempt to grow the stack. If this does not work, we check our SPT to see if the page
is expected to have data loaded in memory. */
void *upage = pg_round_down (fault_addr);
if (not_present && is_user_vaddr (upage) && upage != NULL)
{
if (is_valid_stack_access (fault_addr, f->esp))
{
if (grow_stack (upage))
return;
}
if (try_fetch_page (upage, write))
return;
}
@@ -178,6 +192,54 @@ page_fault (struct intr_frame *f)
kill (f);
}
/* Validates whether the fault address is a valid stack access. Access is a
valid stack access under the following two conditions:
1. The fault address must be within MAX_STACK_OFFSET (32) bytes below
the current stack pointer. (Accounts for both PUSH and PUSHA instructions)
2. Growing this stack to this address does not cause it to exceed the
MAX_STACK_SIZE (8MB) limit.
Returns true if both conditions are met, false otherwise.
Pre: fault_addr is a valid user virtual address (so also not NULL). */
static bool
is_valid_stack_access (const void *fault_addr, const void *esp)
{
uint32_t new_stack_size = PHYS_BASE - pg_round_down (fault_addr);
uint32_t *lowest_valid_push_addr = (uint32_t *)esp - MAX_STACK_OFFSET;
bool is_within_push_range = (uint32_t *)fault_addr >= lowest_valid_push_addr;
return is_within_push_range && new_stack_size <= MAX_STACK_SIZE;
}
/* Attempts to grow the stack by allocating and mapping a new page.
This involves:
1. Allocating a zeroed page from the user pool
2. Installing it into the page table with write permissions
Returns true if the stack was successfully grown, false if either
allocation or installation fails.
Pre: upage is a valid page-aligned address (so also not NULL). */
static bool
grow_stack (void *upage)
{
/* Allocate new page for stack */
void *kpage = palloc_get_page (PAL_USER | PAL_ZERO);
if (kpage == NULL)
return false;
/* Install the page into user page table */
if (!install_page (upage, kpage, true))
{
palloc_free_page (kpage);
return false;
}
return true;
}
bool
try_fetch_page (void *upage, bool write)
{