From 117f6423c4c00fdd3081b70b5fe8e9cb47599459 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 02:05:34 +0400 Subject: [PATCH] Fix run_elf (still leaking memory now). --- Makefile | 2 +- cpu/gdt.c | 6 +++- cpu/gdt.h | 3 +- cpu/memlayout.h | 8 +++++ kernel/mem.h | 5 ++- kernel/vm.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++--- proc.c | 7 ++++- 7 files changed, 106 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index dbaa2ec..195eff9 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 user/shout LDFLAGS=-m elf_i386 user/%: user/%.o user/crt.o - $(LD) $(LDFLAGS) -o $@ -Ttext 0x10000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x401000 $^ image.bin: mbr.bin fs.img cat $^ >$@ diff --git a/cpu/gdt.c b/cpu/gdt.c index f005541..5a031d2 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -1,4 +1,6 @@ #include "gdt.h" +#include "x86.h" +#include "memlayout.h" #include "../lib/string.h" #include "kernel/mem.h" @@ -52,7 +54,7 @@ void load_gdt() { asm("lgdt %0": : "m"(gdt_desc)); } -void switchuvm(struct taskstate *tss, void* esp) { +void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir) { memset(tss, 0, sizeof(*tss)); seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0); seg_desc[SEG_TSS].s = 0; @@ -63,4 +65,6 @@ void switchuvm(struct taskstate *tss, void* esp) { tss->iomb = (ushort) 0xFFFF; asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); + + lcr3(V2P(pgdir)); } diff --git a/cpu/gdt.h b/cpu/gdt.h index 00dd40b..d67c11a 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -29,6 +29,7 @@ #define KERN_STACK_BASE 0x90000 #ifndef __ASSEMBLER__ +#include "kernel/mem.h" typedef unsigned uint; typedef unsigned short ushort; @@ -73,5 +74,5 @@ struct taskstate { }; void load_gdt(); -void switchuvm(struct taskstate *tss, void* esp); +void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir); #endif diff --git a/cpu/memlayout.h b/cpu/memlayout.h index 33a5818..414a0be 100644 --- a/cpu/memlayout.h +++ b/cpu/memlayout.h @@ -5,7 +5,9 @@ #define PHYSTOP 0x8000000 #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE-1))) +#define PTXSHIFT 12 // offset of PTX in a linear address #define PDXSHIFT 22 // offset of PDX in a linear address #define PXMASK 0x3FF @@ -17,6 +19,12 @@ // page directory index #define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK) + +// page table index +#define PTX(va) (((uintptr_t)(va) >> PTXSHIFT) & PXMASK) + +// Address in page table or page directory entry +#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF) #endif // Page table/directory entry flags. diff --git a/kernel/mem.h b/kernel/mem.h index 56ee2d5..5d66fcd 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -11,4 +11,7 @@ void freerange(void *vstart, void *vend); void* kalloc(void); void kfree(char*); -void kvmalloc(); \ No newline at end of file +pde_t *setupkvm(); +void kvmalloc(); +void switchkvm(); +int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top); diff --git a/kernel/vm.c b/kernel/vm.c index e6836e6..99ba35b 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -1,11 +1,12 @@ #include "mem.h" #include "cpu/memlayout.h" #include "cpu/x86.h" +#include "console.h" pde_t *kvm; -void kvmalloc() { - kvm = kalloc(); +pde_t *setupkvm(void) { + pde_t *kvm = kalloc(); memset(kvm, 0, PGSIZE); // Map physical memory to KERNBASE..KERNBASE+PHYSTOP @@ -13,6 +14,81 @@ void kvmalloc() { uintptr_t va = KERNBASE + pa; kvm[PDX(va)] = pa | PTE_P | PTE_W | PTE_PS; } - - lcr3(V2P(kvm)); + return kvm; +} + +void kvmalloc() { + kvm = setupkvm(); + switchkvm(); +} + +void switchkvm(void) +{ + lcr3(V2P(kvm)); // switch to the kernel page table +} + +// Return the address of the PTE in page table pgdir +// that corresponds to virtual address va. If alloc!=0, +// create any required page table pages. +static pte_t * +walkpgdir(pde_t *pgdir, const void *va, int alloc) +{ + pde_t *pde; + pte_t *pgtab; + + pde = &pgdir[PDX(va)]; + if (*pde & PTE_P) { + pgtab = (pte_t *)P2V(PTE_ADDR(*pde)); + } else { + if (!alloc || (pgtab = (pte_t *)kalloc()) == 0) + return 0; + // Make sure all those PTE_P bits are zero. + memset(pgtab, 0, PGSIZE); + // The permissions here are overly generous, but they can + // be further restricted by the permissions in the page table + // entries, if necessary. + *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; + } + return &pgtab[PTX(va)]; +} + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. +static int +mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm) +{ + char *a, *last; + pte_t *pte; + + a = (char *)PGROUNDDOWN((uintptr_t)va); + last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1); + for (;;) + { + if ((pte = walkpgdir(pgdir, a, 1)) == 0) + return -1; + if (*pte & PTE_P) + panic("remap"); + *pte = pa | perm | PTE_P; + if (a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } + return 0; +} + +int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) { + for (uintptr_t a = PGROUNDUP(base); a < top; a += PGSIZE) { + char *pa = kalloc(); + if (pa == 0) { + return -1; + } + memset(pa, 0, PGSIZE); + if (mappages(pgdir, (void*)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) { + kfree(pa); + return -1; + } + } + return 0; } diff --git a/proc.c b/proc.c index e6d81af..a89f124 100644 --- a/proc.c +++ b/proc.c @@ -3,6 +3,7 @@ #include "fs/fs.h" #include "cpu/gdt.h" #include "cpu/isr.h" +#include "cpu/memlayout.h" #include "kernel/mem.h" #include "lib/string.h" #include "console.h" @@ -43,7 +44,10 @@ void run_elf(const char* name) { } if (!vm.user_task) { vm.user_task = kalloc(); - switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom); + vm.user_task->pgdir = setupkvm(); + allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size); + allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE); + switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir); } if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) { printk(name); @@ -75,6 +79,7 @@ void run_elf(const char* name) { _Noreturn void killproc() { void* task_stack; + switchkvm(); swtch(&task_stack, vm.kernel_thread); __builtin_unreachable(); }