add guard pages to stack; dynamically allocate user stacks; zero non-preserved registers before returning from syscalls

This commit is contained in:
Benji Dial 2026-01-02 22:39:57 -05:00
parent 5821f51f02
commit 085ae6ba53
7 changed files with 113 additions and 31 deletions

View file

@ -76,7 +76,23 @@ isr_exception_common:
mov rax, cr3
push rax
mov rdi, rsp
jmp isr_exception_c
call isr_exception_c
add rsp, 48
pop r11
pop r10
pop r9
pop r8
add rsp, 8
pop rsi
pop rdi
pop rdx
pop rcx
add rsp, 8
pop rax
add rsp, 16
iretq
isr_irq_common:
push rax

View file

@ -22,6 +22,8 @@
// https://osdev.wiki/wiki/Task_State_Segment
#include "interrupts.h"
#include "process.h"
#include "paging.h"
#include "debug.h"
//interrupts.asm depends on layout
@ -63,7 +65,7 @@ const char *exception_names[32] = {
"hypervisor injection exception", "vmm communication exception", "security exception", 0
};
[[noreturn]] static void unhandled_exception(struct exception_parameter *parameter) {
[[noreturn]] static void unhandled_exception(const struct exception_parameter *parameter) {
assert(parameter->exception_number < 32 && exception_names[parameter->exception_number] != 0)
@ -101,11 +103,25 @@ const char *exception_names[32] = {
}
//referenced in interrupts.asm
void isr_exception_c(struct exception_parameter *parameter) {
void isr_exception_c(const struct exception_parameter *parameter) {
//eventually some exceptions will be handled in here
if (running_thread != 0 &&
parameter->exception_number == 0x0e &&
(parameter->error_code & 0x1) == 0x0 &&
parameter->cr2 >= (uint64_t)running_thread->stack_bottom + 4096 &&
parameter->cr2 < (uint64_t)running_thread->stack_top) {
unhandled_exception(parameter);
void *vma = (void *)(parameter->cr2 & 0xfffffffffffff000);
unmap_page_for_process(running_thread->process, vma);
uint64_t pma = take_free_physical_page();
map_page_for_process(
running_thread->process, pma,
vma, 1, 0, 1);
}
else
unhandled_exception(parameter);
}

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/paging.c
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -104,9 +104,7 @@ void mark_physical_memory_free(uint64_t base, uint64_t length) {
//defined in paging.asm
void invlpg(void *address);
void map_in_kernel_page_table(
uint64_t physical_base, void *virtual_base,
int writable, int executable) {
void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int writable, int executable) {
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
assert(virtual_base_u64 >= 0xffffffffc0000000);
@ -122,6 +120,18 @@ void map_in_kernel_page_table(
}
void create_kernel_guard_page(void *virtual_base) {
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
assert(virtual_base_u64 >= 0xffffffffc0000000);
uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
assert(kernel_p1s[p1s_index] == 0);
kernel_p1s[p1s_index] = 0x2;
}
void unmap_kernel_page(void *virtual_base) {
uint64_t virtual_base_u64 = (uint64_t)virtual_base;

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/paging.h
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -33,6 +33,9 @@ void mark_physical_memory_free(uint64_t base, uint64_t length);
void map_in_kernel_page_table(
uint64_t physical_base, void *virtual_base, int writable, int executable);
//virtual base should be page-aligned and within kernel range.
void create_kernel_guard_page(void *virtual_base);
//unmaps one page. base should be page-aligned and within kernel range.
void unmap_kernel_page(void *virtual_base);

View file

@ -69,11 +69,8 @@ void create_process(struct process *process_out) {
}
void map_page_for_process(
struct process *process, uint64_t physical_base,
void *virtual_base, int writable, int executable, int owned) {
assert(physical_base % 4096 == 0)
static void get_page_table_entry(
struct process *process, void *virtual_base, int *p1i_out, int *p2i_out, int *p3i_out) {
uint64_t vma = (uint64_t)virtual_base;
assert(vma % 4096 == 0)
@ -112,6 +109,21 @@ void map_page_for_process(
process->owned_pages_bitmaps[p3i][p2i][i] = 0;
}
*p1i_out = p1i;
*p2i_out = p2i;
*p3i_out = p3i;
}
void map_page_for_process(
struct process *process, uint64_t physical_base,
void *virtual_base, int writable, int executable, int owned) {
assert(physical_base % 4096 == 0)
int p1i, p2i, p3i;
get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i);
assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0)
process->p1_virtual_bases[p3i][p2i][p1i] =
@ -123,6 +135,17 @@ void map_page_for_process(
}
void create_guard_page_for_process(struct process *process, void *virtual_base) {
int p1i, p2i, p3i;
get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i);
assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0)
process->p1_virtual_bases[p3i][p2i][p1i] = 0x6;
}
void unmap_page_for_process(
struct process *process, void *virtual_base) {
@ -532,7 +555,8 @@ void destroy_thread(struct thread *thread) {
--thread->process->n_threads;
for (void *p = thread->syscall_stack_bottom; p < thread->syscall_stack_top; p += 4096)
unmap_kernel_page(thread->syscall_stack_bottom);
for (void *p = thread->syscall_stack_bottom + 4096; p < thread->syscall_stack_top; p += 4096)
unmap_and_free_kernel_page(p);
if (thread->process->n_threads == 0)
@ -545,22 +569,25 @@ void destroy_thread(struct thread *thread) {
}
#define INITIAL_STACK_SIZE (16 << 20)
#define INITIAL_SYSCALL_STACK_SIZE 16384
#define MAX_STACK_SIZE ((16 << 20) - 4096)
#define INITIAL_STACK_SIZE 16384
#define SYSCALL_STACK_SIZE 12288
void create_thread(struct process *process, struct thread *thread_out) {
//TODO: allocate stack as needed on page faults, have guard pages, etc.
void *stack_bottom_vma =
find_free_process_region(process, INITIAL_STACK_SIZE / 4096);
find_free_process_region(process, MAX_STACK_SIZE + 4096);
for (int i = 0; i < INITIAL_STACK_SIZE / 4096; ++i) {
for (int i = 0; i < MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i += 4096)
create_guard_page_for_process(process, stack_bottom_vma + i);
for (int i = MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i < MAX_STACK_SIZE + 4096; i += 4096) {
uint64_t pma = take_free_physical_page();
map_page_for_process(
process, pma,
stack_bottom_vma + i * 4096,
stack_bottom_vma + i,
1, 0, 1);
void *kvma = find_free_kernel_region(4096);
@ -570,19 +597,21 @@ void create_thread(struct process *process, struct thread *thread_out) {
}
void *syscall_stack_bottom = find_free_kernel_region(INITIAL_SYSCALL_STACK_SIZE);
void *syscall_stack_bottom = find_free_kernel_region(SYSCALL_STACK_SIZE + 4096);
for (int i = 0; i < INITIAL_SYSCALL_STACK_SIZE / 4096; ++i) {
create_kernel_guard_page(syscall_stack_bottom);
for (int i = 4096; i < 4096 + SYSCALL_STACK_SIZE; i += 4096) {
uint64_t pma = take_free_physical_page();
map_in_kernel_page_table(
pma, syscall_stack_bottom + i * 4096, 1, 0);
pma, syscall_stack_bottom + i, 1, 0);
}
thread_out->process = process;
thread_out->stack_bottom = stack_bottom_vma;
thread_out->stack_top = stack_bottom_vma + INITIAL_STACK_SIZE;
thread_out->stack_top = stack_bottom_vma + 4096 + MAX_STACK_SIZE;
thread_out->syscall_stack_bottom = syscall_stack_bottom;
thread_out->syscall_stack_top = syscall_stack_bottom + INITIAL_SYSCALL_STACK_SIZE;
thread_out->syscall_stack_top = syscall_stack_bottom + 4096 + SYSCALL_STACK_SIZE;
++process->n_threads;
@ -640,8 +669,6 @@ void syscall_map_framebuffer(struct framebuffer_info *info_out) {
}
#define INITIAL_FILE_HANDLE_COUNT 128
enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out) {
assert(running_thread != 0)

View file

@ -115,6 +115,9 @@ void map_page_for_process(
struct process *process, uint64_t physical_base,
void *virtual_base, int writable, int executable, int owned);
//physical and virtual bases must be page-aligned.
void create_guard_page_for_process(struct process *process, void *virtual_base);
//virtual base must be page-aligned, in bottom p3, and not zero.
//also frees physical page if owned by process.
void unmap_page_for_process(

View file

@ -1,5 +1,5 @@
; Calcite, src/kernel/syscalls.asm
; Copyright 2025 Benji Dial
; Copyright 2025-2026 Benji Dial
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
@ -44,6 +44,13 @@ syscall_entry:
mov rcx, rax
call syscall_entry_c
xor rdi, rdi
xor rsi, rsi
xor rdx, rdx
xor r8, r8
xor r9, r9
xor r10, r10
pop r11
pop rcx
pop rsp