diff --git a/kernel/interrupts.S b/kernel/interrupts.S index 460a216..c04a73f 100644 --- a/kernel/interrupts.S +++ b/kernel/interrupts.S @@ -57,8 +57,6 @@ ISR_ERRCODE 14 # Page Fault (Has Error Code!) isr32: cli pushal - call send_eoi_master - push %ds mov $0x10, %ax @@ -68,8 +66,9 @@ isr32: call switch_context movl %eax, %esp - pop %ds + call send_eoi_master + pop %ds popal sti iret diff --git a/kernel/interrupts.c b/kernel/interrupts.c index 526b16f..7209d46 100644 --- a/kernel/interrupts.c +++ b/kernel/interrupts.c @@ -2,6 +2,8 @@ #include +#include + #include "kbd.h" #include "syscall.h" #include "common.h" @@ -11,6 +13,7 @@ #include #include +#include #define IDT_MAX_DESCRIPTORS 256 #define ATTR_KERN_32 0x8E // active(1), ring 0 (00), interrupt gate(0), 32 bit (1110) @@ -135,10 +138,34 @@ void common_interrupt_handler(intr_regs_t *args) { break; } case 14:{ - disable_interrupts(); uint32_t faulting_address; - process_t* task = current_task(); asm volatile("mov %%cr2, %0" : "=r"(faulting_address)); + + if (is_cow(faulting_address)) { + uint32_t pde_entry = faulting_address >> 22; + uint32_t pte_entry = (faulting_address >> 12) & 0x3FF; + uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE)); + + uint32_t p_dst = (uint32_t)p_alloc_frame(); + uint32_t p_src = (uint32_t)tablev[pte_entry] & 0xFFFFF000; + + map_page(TEMP_PT_SRC, p_src, PAGE_WRITABLE | PAGE_PRESENT); + map_page(TEMP_PT_DST, p_dst, PAGE_WRITABLE | PAGE_PRESENT); + + memcpy((void*)TEMP_PT_SRC, (void*)TEMP_PT_DST, PAGE_SIZE); + + unmap_page(TEMP_PT_SRC); + unmap_page(TEMP_PT_DST); + + uint32_t flags = tablev[pte_entry] & 0xFFF; + flags &= ~PAGE_COW; + tablev[pte_entry] = p_dst | flags | PAGE_WRITABLE; + + flush_tlb(); + break; + } + + process_t* task = current_task(); kprintf("\n--- PAGE FAULT ---\n"); kprintf("Current Task PID: %d\n", task->pid); kprintf("Faulted Virtual Address: 0x%x\n", faulting_address); diff --git a/kernel/kmain.c b/kernel/kmain.c index 6020952..b644477 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -59,7 +59,7 @@ vfs_node_t* load_initrd() { void load_root_program(const char* path) { lock_scheduler(); uint32_t kernel_cr3 = fetch_cr3(); - void* cr3 = create_task_pd(); + void* cr3 = create_new_pd(); load_pd(cr3); flush_tlb(); diff --git a/kernel/memory/mm.c b/kernel/memory/mm.c index 5735e04..48c1ad8 100644 --- a/kernel/memory/mm.c +++ b/kernel/memory/mm.c @@ -9,20 +9,6 @@ extern uint8_t __kernel_end; extern uint32_t page_directory[1024]; -#define PAGE_SIZE 4096 -#define MAX_RAM 0xFFFFFFFF // 4GB -#define BITMAP_SIZE ((MAX_RAM / PAGE_SIZE) / 8) - -#define PAGE_TABLES 0xFFC00000 -#define PAGE_DIRECTORY 0xFFFFF000 - -#define TEMP_MAPPING_VADDR 0xDFFF0000 -#define TEMP_COPY_SRC 0xDFFE0000 // Safely 1 page down -#define TEMP_COPY_DST 0xDFFD0000 - -#define USR_PAGE 0x07 -#define SUP_PAGE 0x03 - static uint32_t max_address = 0; static uint32_t total_available_memory = 0; static uint8_t memory_bitmap[BITMAP_SIZE]; @@ -120,8 +106,8 @@ void p_free_frame(void* addr) { void map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags) { uint32_t pde_entry = vaddr >> 22; uint32_t pte_entry = (vaddr >> 12) & 0x3FF; - uint32_t* pd = (uint32_t*)PAGE_DIRECTORY; - uint32_t* tablev = (uint32_t*)(PAGE_TABLES + (pde_entry * PAGE_SIZE)); + uint32_t* pd = (uint32_t*)CUR_PAGE_DIRECTORY; + uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE)); if ((pd[pde_entry] & PAGE_PRESENT) == 0) { uint32_t new_table = (uint32_t)p_alloc_frame(); @@ -136,6 +122,35 @@ void map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags) { flush_tlb(); } +void unmap_page(uint32_t vaddr) { + uint32_t pde_entry = vaddr >> 22; + uint32_t pte_entry = (vaddr >> 12) & 0x3FF; + uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE)); + tablev[pte_entry] = 0; + flush_tlb(); +} + +int is_cow(uint32_t vaddr) { + uint32_t pde_entry = vaddr >> 22; + uint32_t pte_entry = (vaddr >> 12) & 0x3FF; + uint32_t* pd = (uint32_t*)CUR_PAGE_DIRECTORY; + uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE)); + + if (!(pd[pde_entry] & PAGE_PRESENT)) { + return 0; + } + + if (tablev[pte_entry] & PAGE_WRITABLE) { + return 0; + } + + if (!(tablev[pte_entry] & PAGE_COW)) { + return 0; + } + + return 1; +} + void init_pmm(void* mmap_base, uint32_t mmap_limit) { for (int i = 0; i < BITMAP_SIZE; i++) { memory_bitmap[i] = 0xFF; @@ -166,103 +181,90 @@ uint32_t total_free_memory() { return total_available_memory; } -void* create_task_pd() { - uint32_t pd_address = (uint32_t)p_alloc_frame(); - if (pd_address == 0) { - return 0; +void* create_new_pd() { + uint32_t task_pd_addr = (uint32_t)p_alloc_frame(); + if (task_pd_addr == 0) { + return NULL; // outta mem } - map_page(TEMP_MAPPING_VADDR, pd_address, PAGE_PRESENT | PAGE_WRITABLE); + map_page(TEMP_TASKPD_VADDR, task_pd_addr, PAGE_PRESENT | PAGE_WRITABLE); - uint32_t* task_pd = (uint32_t*)TEMP_MAPPING_VADDR; - uint32_t* kernel_pd = (uint32_t*)PAGE_DIRECTORY; + uint32_t* task_pd = (uint32_t*)TEMP_TASKPD_VADDR; + uint32_t* parent_pd = (uint32_t*)CUR_PAGE_DIRECTORY; + + for(int i = 768; i < 1023; i++) { + task_pd[i] = parent_pd[i]; + } + + task_pd[1023] = task_pd_addr | PAGE_PRESENT | PAGE_WRITABLE; for(int i = 0; i < 768; i++) { task_pd[i] = 0; } - for(int i = 768; i < 1023; i++) { - task_pd[i] = kernel_pd[i]; - } - - task_pd[1023] = (uint32_t)pd_address | PAGE_PRESENT | PAGE_WRITABLE; + unmap_page(TEMP_TASKPD_VADDR); - uint32_t pde = TEMP_MAPPING_VADDR >> 22; - uint32_t pte = (TEMP_MAPPING_VADDR >> 12) & 0x3FF; - uint32_t* kernel_pt = (uint32_t*)(PAGE_TABLES + (pde * PAGE_SIZE)); - kernel_pt[pte] = 0; - return (void*)pd_address; + return (void*)task_pd_addr; } -void* clone_task_pd() { - uint32_t child_pd_phys = (uint32_t)p_alloc_frame(); - if (child_pd_phys == 0) return NULL; - - map_page(TEMP_MAPPING_VADDR, child_pd_phys, PAGE_PRESENT | PAGE_WRITABLE); - uint32_t* child_pd = (uint32_t*)TEMP_MAPPING_VADDR; - uint32_t* parent_pd = (uint32_t*)PAGE_DIRECTORY; - - for (int i = 768; i < 1023; i++) { - child_pd[i] = parent_pd[i]; +void* clone_current_pd() { + uint32_t task_pd_addr = (uint32_t)p_alloc_frame(); + if (task_pd_addr == 0) { + return NULL; // outta mem } - child_pd[1023] = child_pd_phys | PAGE_PRESENT | PAGE_WRITABLE; + map_page(TEMP_TASKPD_VADDR, task_pd_addr, PAGE_PRESENT | PAGE_WRITABLE); - for (int i = 0; i < 768; i++) { - child_pd[i] = 0; + uint32_t* task_pd = (uint32_t*)TEMP_TASKPD_VADDR; + uint32_t* parent_pd = (uint32_t*)CUR_PAGE_DIRECTORY; + + for(int i = 768; i < 1023; i++) { + task_pd[i] = parent_pd[i]; + } + + task_pd[1023] = task_pd_addr | PAGE_PRESENT | PAGE_WRITABLE; + + for(int i = 0; i < 768; i++) { + task_pd[i] = 0; } for (int i = 0; i < 768; i++) { uint32_t pde = parent_pd[i]; if (!(pde & PAGE_PRESENT)) continue; - uint32_t child_pt_phys = (uint32_t)p_alloc_frame(); + + uint32_t task_pt = (uint32_t)p_alloc_frame(); + task_pd[i] = task_pt | (pde & 0xFFF); + + uint32_t parent_pt = pde & 0xFFFFF000; - child_pd[i] = child_pt_phys | (pde & 0xFFF); + map_page(TEMP_PT_DST, task_pt, PAGE_PRESENT | PAGE_WRITABLE); + map_page(TEMP_PT_SRC, parent_pt, PAGE_PRESENT | PAGE_WRITABLE); - map_page(TEMP_COPY_SRC, pde & ~(PAGE_SIZE - 1), PAGE_PRESENT | PAGE_WRITABLE); - map_page(TEMP_COPY_DST, child_pt_phys, PAGE_PRESENT | PAGE_WRITABLE); - flush_tlb(); + uint32_t* pt_dst = (uint32_t*)TEMP_PT_DST; + uint32_t* pt_src = (uint32_t*)TEMP_PT_SRC; - uint32_t* src_pt = (uint32_t*)TEMP_COPY_SRC; - uint32_t* dst_pt = (uint32_t*)TEMP_COPY_DST; + for(int j = 0; j < 1024; j++) { + uint32_t pte_src = pt_src[j]; + if (!(pte_src & PAGE_PRESENT)) continue; - for (int j = 0; j < 1024; j++) { - uint32_t pte = src_pt[j]; - if (!(pte & PAGE_PRESENT)) { - dst_pt[j] = 0; + if (!(pte_src & PAGE_WRITABLE)) { + pt_dst[j] = pt_src[j]; continue; } - uint32_t child_frame_phys = (uint32_t)p_alloc_frame(); - dst_pt[j] = child_frame_phys | (pte & 0xFFF); + // the src page is writable, mark the pte src as read only and COW, then copy to dst pte + pt_src[j] &= ~(PAGE_WRITABLE); + pt_src[j] |= PAGE_COW; + flush_tlb(); // huge performance hit here, and probably everywhere else too where we aren't switching cr3s - uint32_t parent_data_phys = pte & ~(PAGE_SIZE - 1); - - map_page(TEMP_COPY_SRC, parent_data_phys, PAGE_PRESENT | PAGE_WRITABLE); - map_page(TEMP_COPY_DST, child_frame_phys, PAGE_PRESENT | PAGE_WRITABLE); - flush_tlb(); - - memcpy((void*)TEMP_COPY_DST, (void*)TEMP_COPY_SRC, PAGE_SIZE); - - map_page(TEMP_COPY_SRC, pde & ~(PAGE_SIZE - 1), PAGE_PRESENT | PAGE_WRITABLE); - map_page(TEMP_COPY_DST, child_pt_phys, PAGE_PRESENT | PAGE_WRITABLE); - flush_tlb(); + pt_dst[j] = pt_src[j]; } + + unmap_page(TEMP_PT_DST); + unmap_page(TEMP_PT_SRC); } - uint32_t pde_main = TEMP_MAPPING_VADDR >> 22; - uint32_t* pt_main = (uint32_t*)(PAGE_TABLES + (pde_main * PAGE_SIZE)); - pt_main[(TEMP_MAPPING_VADDR >> 12) & 0x3FF] = 0; + unmap_page(TEMP_TASKPD_VADDR); - uint32_t pde_src = TEMP_COPY_SRC >> 22; - uint32_t* pt_src = (uint32_t*)(PAGE_TABLES + (pde_src * PAGE_SIZE)); - pt_src[(TEMP_COPY_SRC >> 12) & 0x3FF] = 0; - - uint32_t pde_dst = TEMP_COPY_DST >> 22; - uint32_t* pt_dst = (uint32_t*)(PAGE_TABLES + (pde_dst * PAGE_SIZE)); - pt_dst[(TEMP_COPY_DST >> 12) & 0x3FF] = 0; - - flush_tlb(); - - return (void*)child_pd_phys; + return (void*)task_pd_addr; } \ No newline at end of file diff --git a/kernel/memory/mm.h b/kernel/memory/mm.h index 0248aa7..23aef4d 100644 --- a/kernel/memory/mm.h +++ b/kernel/memory/mm.h @@ -3,12 +3,24 @@ #include -#define PAGE_PRESENT 0x1 -#define PAGE_WRITABLE 0x2 -#define PAGE_USER 0x4 +#define PAGE_PRESENT (1 << 0) +#define PAGE_WRITABLE (1 << 1) +#define PAGE_USER (1 << 2) +#define PAGE_COW (1 << 9) + +#define TEMP_KERNPD_VADDR 0xDFF90000 +#define TEMP_TASKPD_VADDR 0xDFFA0000 +#define TEMP_PT_SRC 0xDFFB0000 +#define TEMP_PT_DST 0xDFFC0000 #define PAGE_SIZE 4096 +#define MAX_RAM 0xFFFFFFFF // 4GB +#define BITMAP_SIZE ((MAX_RAM / PAGE_SIZE) / 8) + +#define CUR_PAGE_DIRECTORY 0xFFFFF000 +#define CUR_PAGE_TABLES 0xFFC00000 + typedef struct __attribute__((packed)) { uint32_t addr; } mem_page_t; @@ -20,10 +32,13 @@ void* p_alloc_frame(); uint32_t total_free_memory(); -void* create_task_pd(); -void* clone_task_pd(); +void* create_new_pd(); +void* clone_current_pd(); + +int is_cow(uint32_t vaddr); void map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags); +void unmap_page(uint32_t vaddr); void load_pd(void* paddr); diff --git a/kernel/scheduler.S b/kernel/scheduler.S index fd0e4e4..18f1e9a 100644 --- a/kernel/scheduler.S +++ b/kernel/scheduler.S @@ -10,7 +10,7 @@ yield: # void yield() pushfl // EFLAGS pushl %cs // CS - pushl %ecx // EIP + pushl %ecx // EIP pushal call send_eoi_master diff --git a/kernel/scheduler.c b/kernel/scheduler.c index 0e9f248..8d0c312 100644 --- a/kernel/scheduler.c +++ b/kernel/scheduler.c @@ -62,6 +62,8 @@ uint32_t switch_context(uint32_t current_esp) { if (current_process->flags & PROCESS_FLAG_USER) { tss.esp0 = current_process->kstack_top; } + + return current_process->esp; } diff --git a/kernel/syscall.c b/kernel/syscall.c index 74af192..80202f2 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -124,7 +124,6 @@ static int32_t sys_pty(int* slave_fd) { static int32_t sys_fork(registers_t* parent_regs) { process_t* parent = current_task(); - process_t* child = kalloc(sizeof(process_t)); if (!child) { return -1; @@ -149,11 +148,8 @@ static int32_t sys_fork(registers_t* parent_regs) { child_regs->eax = 0; child->esp = (uint32_t)child_regs; - kprintf("child registers for PID %d\n", child->pid); - kprintf("\tEIP: 0x%x\n", child_regs->eip); - - // uint32_t cr3 = (uint32_t)clone_task_pd(); - child->cr3 = parent->cr3; + uint32_t cr3 = (uint32_t)clone_current_pd(); + child->cr3 = cr3; add_task(child); return child->pid; diff --git a/programs/lib/rlibc.a b/programs/lib/rlibc.a index eb5773c..eea302d 100644 Binary files a/programs/lib/rlibc.a and b/programs/lib/rlibc.a differ diff --git a/programs/test/main.c b/programs/test/main.c index cadd0ee..c9d12f6 100644 --- a/programs/test/main.c +++ b/programs/test/main.c @@ -1,12 +1,12 @@ +#include "syscall.h" #include int main(int argc, char *argv[]) { (void)argc; (void)argv; - int fk = 0; - if ((fk = fork()) > 0) { - return fk; + if (fork() == 0) { + return 1; } return 0;