diff --git a/src/arch/x86_64/cls.c b/src/arch/x86_64/cls.c index 973dd496..a18e3b46 100644 --- a/src/arch/x86_64/cls.c +++ b/src/arch/x86_64/cls.c @@ -3,10 +3,11 @@ #include cls_t bspcls; - +vmm_context initialvmmcontext; void arch_setcls(cls_t* addr){ wrmsr(MSR_GSBASE, (uint64_t)addr); + wrmsr(MSR_KERNELGSBASE, (uint64_t)addr); } cls_t* arch_getcls(){ @@ -15,4 +16,5 @@ cls_t* arch_getcls(){ void bsp_setcls(){ arch_setcls(&bspcls); + bspcls.context = &initialvmmcontext; } diff --git a/src/arch/x86_64/idt.c b/src/arch/x86_64/idt.c index 80e3ce8e..3290ec99 100644 --- a/src/arch/x86_64/idt.c +++ b/src/arch/x86_64/idt.c @@ -34,6 +34,8 @@ void idt_bspinit(){ idt_setentry(&idt[i], asmisr_except, 0x28, FLAGS_PRESENT | FLAGS_TYPE_TRAP, 0); } + idt_setentry(&idt[VECTOR_PF], asmisr_pagefault, 0x28, FLAGS_PRESENT | FLAGS_TYPE_TRAP, 0); + idt_reload(); } diff --git a/src/arch/x86_64/include/arch/idt.h b/src/arch/x86_64/include/arch/idt.h index fbc16a37..d968e9f6 100644 --- a/src/arch/x86_64/include/arch/idt.h +++ b/src/arch/x86_64/include/arch/idt.h @@ -8,6 +8,21 @@ #define FLAGS_TYPE_INTERRUPT (0xE << 8) #define FLAGS_TYPE_TRAP (0xF << 8) +#define VECTOR_DIV 0 +#define VECTOR_DEBUG 1 +#define VECTOR_NMI 2 +#define VECTOR_BREAKPOINT 3 +#define VECTOR_OVERFLOW 4 +#define VECTOR_BOUNDRANGE 5 +#define VECTOR_INVALIDOPCODE 6 +#define VECTOR_DEVUNAVAILABLE 7 +#define VECTOR_DOUBLEFAULT 8 +#define VECTOR_COPROCESSOR 9 +#define VECTOR_INVALIDTSS 10 +#define VECTOR_SEGMENTNOTPRESENT 11 +#define VECTOR_STACK 12 +#define VECTOR_GPF 13 +#define VECTOR_PF 14 typedef struct{ diff --git a/src/arch/x86_64/include/arch/isr.h b/src/arch/x86_64/include/arch/isr.h index b1fc9cb0..7879c2a0 100644 --- a/src/arch/x86_64/include/arch/isr.h +++ b/src/arch/x86_64/include/arch/isr.h @@ -5,7 +5,7 @@ extern void asmisr_general(arch_regsnoerror*); extern void asmisr_except(arch_regserror*); - +extern void asmisr_pagefault(arch_regserror*); #endif diff --git a/src/arch/x86_64/include/arch/mmu.h b/src/arch/x86_64/include/arch/mmu.h index 67a0cc49..80c14521 100644 --- a/src/arch/x86_64/include/arch/mmu.h +++ b/src/arch/x86_64/include/arch/mmu.h @@ -6,11 +6,11 @@ #define PAGE_SIZE 4096 -#define ARCH_MMU_MAP_READ (1 << 0) -#define ARCH_MMU_MAP_WRITE (1 << 1) -#define ARCH_MMU_MAP_USER (1 << 2) -#define ARCH_MMU_MAP_NOEXEC (1 << 63) -#define ARCH_MMU_MAP_PAGESIZE (1 << 7) +#define ARCH_MMU_MAP_READ (uint64_t)(1 << 0) +#define ARCH_MMU_MAP_WRITE (uint64_t)(1 << 1) +#define ARCH_MMU_MAP_USER (uint64_t)(1 << 2) +#define ARCH_MMU_MAP_NOEXEC (uint64_t)(1 << 63) +#define ARCH_MMU_MAP_PAGESIZE (uint64_t)(1 << 7) typedef uint64_t* arch_mmu_tableptr; diff --git a/src/arch/x86_64/isr.asm b/src/arch/x86_64/isr.asm index 390a04b2..7b12992f 100644 --- a/src/arch/x86_64/isr.asm +++ b/src/arch/x86_64/isr.asm @@ -2,6 +2,8 @@ section .text %macro pushregs 0 + + push rbp push rsi push rdi @@ -27,6 +29,11 @@ section .text mov rax, gs push rax + cmp rax,0x30 + jmp .noswapgs + swapgs + .noswapgs: + mov rdi,cr2 push rdi @@ -36,8 +43,10 @@ section .text %macro popregs 0 add rsp, 8 ; cr2 - + pop rax + cmp rax,0x30 + je .dontpopsegs mov gs,rax pop rax mov fs,rax @@ -45,7 +54,12 @@ section .text mov es,rax pop rax mov ds,rax - + swapgs + jmp .poprest + .dontpopsegs: + add rsp,24 + + .poprest: pop rax pop rbx pop rcx @@ -89,7 +103,7 @@ section .text %macro except 2 - + global %1 %1: diff --git a/src/arch/x86_64/isr.c b/src/arch/x86_64/isr.c index 5ec12e6c..0d549bc5 100644 --- a/src/arch/x86_64/isr.c +++ b/src/arch/x86_64/isr.c @@ -1,5 +1,6 @@ #include #include +#include void isr_general(arch_regsnoerror *reg){ _panic("Unhandled interrupt", 0); @@ -11,6 +12,9 @@ void isr_except(arch_regserror *reg){ asm("hlt;"); } -void isr_pagefault(void*){ +void isr_pagefault(arch_regserror *reg){ + + if(!vmm_dealwithrequest(reg->cr2)) + _panic("Page fault!\n", 0); } diff --git a/src/arch/x86_64/main.c b/src/arch/x86_64/main.c index de9df780..c052de09 100644 --- a/src/arch/x86_64/main.c +++ b/src/arch/x86_64/main.c @@ -4,6 +4,7 @@ #include #include #include +#include static volatile struct limine_terminal_request liminettyr = { .id = LIMINE_TERMINAL_REQUEST, @@ -40,8 +41,8 @@ void kmain(){ arch_mmu_init(); - - + vmm_init(); + } diff --git a/src/arch/x86_64/mmu.c b/src/arch/x86_64/mmu.c index b43d1dfd..53f4668d 100644 --- a/src/arch/x86_64/mmu.c +++ b/src/arch/x86_64/mmu.c @@ -44,6 +44,7 @@ static bool setpage(arch_mmu_tableptr context, void* vaddr, uint64_t entry){ // TODO unallocate in case of failure + if(!pdptaddr){ pdptaddr = pmm_alloc(1); changeentry(&context[pdpt], pdptaddr, PTR_FLAGS); @@ -53,7 +54,7 @@ static bool setpage(arch_mmu_tableptr context, void* vaddr, uint64_t entry){ pdptaddr = (void*)pdptaddr + (size_t)limine_hhdm_offset; uint64_t* pdaddr = pdptaddr[pd] & ~0xFFF; - + if(!pdaddr){ pdaddr = pmm_alloc(1); changeentry(&pdptaddr[pd], pdaddr, PTR_FLAGS); @@ -64,8 +65,6 @@ static bool setpage(arch_mmu_tableptr context, void* vaddr, uint64_t entry){ uint64_t* ptaddr = pdaddr[pt]& ~0xFFF; - - if(!ptaddr){ ptaddr = pmm_alloc(1); changeentry(&pdaddr[pt], ptaddr, PTR_FLAGS); @@ -87,7 +86,8 @@ int arch_mmu_map(arch_mmu_tableptr context, void* paddr, void* vaddr, size_t fla changeentry(&entry, paddr, flags); int ret = setpage(context, vaddr, entry); - + + if(ret) invalidate(vaddr); return ret; @@ -128,13 +128,13 @@ void arch_mmu_init(){ for(size_t entry = 0; entry < count; ++entry){ void* addr = entries[entry]->base; - for(size_t offset = 0; offset < entries[entry]->length; offset += PAGE_SIZE){ + for(size_t pageoffset = 0; pageoffset < entries[entry]->length / PAGE_SIZE; ++pageoffset){ uint64_t entry; - changeentry(&entry, addr + offset, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE); + changeentry(&entry, addr + pageoffset*PAGE_SIZE, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE); - setpage(context, addr + offset, entry); - setpage(context, addr + offset + (size_t)limine_hhdm_offset, entry); + setpage(context, addr + pageoffset*PAGE_SIZE, entry); + setpage(context, addr + pageoffset*PAGE_SIZE + (size_t)limine_hhdm_offset, entry); } @@ -190,6 +190,8 @@ void arch_mmu_init(){ switchcontext(context); + arch_getcls()->context->context = context; + printf("In bootstrap context\n"); } diff --git a/src/include/kernel/vmm.h b/src/include/kernel/vmm.h new file mode 100644 index 00000000..f45bf743 --- /dev/null +++ b/src/include/kernel/vmm.h @@ -0,0 +1,59 @@ +#ifndef _VMM_H_INCLUDE +#define _VMM_H_INCLUDE + +#include +#include +#include + +#define KERNEL_SPACE_START 0xFFFF800000000000 +#define KERNEL_SPACE_END 0xFFFFFFFFFFFFFFFF + +#define VMM_TYPE_FREE 0 +#define VMM_TYPE_ANON 1 +#define VMM_TYPE_FILE 2 + +struct vmm_cacheheader; + +typedef struct{ + int lock; + struct vmm_cacheheader* next; + size_t freecount; +} vmm_cacheheader; + +struct vmm_mapping; + +typedef struct _vmm_mapping { + struct _vmm_mapping *next; + struct _vmm_mapping *prev; + vmm_cacheheader* cache; + void* start; + void* end; + size_t mmuflags; + size_t type; + void* data; + size_t offset; +} vmm_mapping; + + +#define VMM_CACHE_ENTRY_COUNT ((PAGE_SIZE - sizeof(vmm_cacheheader)) / sizeof(vmm_mapping)) + +typedef struct{ + vmm_cacheheader header; + vmm_mapping mappings[VMM_CACHE_ENTRY_COUNT]; +} vmm_cache; + +typedef struct { + vmm_mapping* userstart; + arch_mmu_tableptr context; + int lock; +} vmm_context; + +void vmm_init(); + +bool vmm_dealwithrequest(void* addr); +bool vmm_setused(void* addr, size_t pagec, size_t mmuflags); +bool vmm_unmap(void* addr, size_t pagec); +bool vmm_map(void* paddr, void* vaddr, size_t pagec, size_t mmuflags); +void* vmm_alloc(size_t pagec, size_t mmuflags); + +#endif diff --git a/src/sys/mm/vmm.c b/src/sys/mm/vmm.c new file mode 100644 index 00000000..941f1f43 --- /dev/null +++ b/src/sys/mm/vmm.c @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +vmm_cache* caches; +int klock; +vmm_mapping *kmapstart; + +static vmm_cache* newcache(){ + vmm_cache* cache = pmm_hhdmalloc(1); + if(!cache) return NULL; + memset(cache, 0, sizeof(vmm_cache)); + cache->header.freecount = VMM_CACHE_ENTRY_COUNT; + return cache; +} + +static void freecache(vmm_cache* cache){ + pmm_hhdmfree(cache, 1); +} + +static void freeentry(vmm_mapping* entry){ + spinlock_acquire(&entry->cache->lock); + + vmm_cacheheader* cache = entry->cache; + + ++entry->cache->freecount; + memset(entry, 0, sizeof(vmm_mapping)); + + // TODO set first free + + spinlock_release(&cache->lock); +} + +static vmm_mapping* allocentry(vmm_cache* cache){ + + vmm_mapping* found = NULL; + + spinlock_acquire(&cache->header.lock); + + if(cache->header.freecount == 0){ + spinlock_release(&cache->header.lock); + return NULL; + } + + // TODO keep first free in cache header + + for(size_t i = 0; i < VMM_CACHE_ENTRY_COUNT; ++i){ + if(!cache->mappings[i].cache){ + found = &cache->mappings[i]; + goto _found; + } + } + + _found: + + --cache->header.freecount; + if(found) + found->cache = &cache->header; + + spinlock_release(&cache->header.lock); + + return found; +} + +static vmm_mapping* allocatefirst(){ + + // FIXME there's a possible race condition here I think + + vmm_mapping *map = NULL; + vmm_cache *cache = caches; + + map = allocentry(cache); + + while(cache->header.next && map == NULL){ + cache = cache->header.next; + map = allocentry(cache); + } + + if(!map){ + // try allocating new cache + vmm_cache* new = newcache(); + if(!new) return NULL; + map = allocentry(new); + cache->header.next = new; + } + + + return map; + +} + +static char* debugnames[] = { + "FREE", + "ANON", + "FILE" +}; + +static void debug_dumpkernelmappings(){ + vmm_mapping* map = kmapstart; + + printf("Kernel mappings:\n"); + + while(map != NULL){ + printf("START: %016p END: %016p MMUFLAGS: %016p DATA: %016p OFFSET: %lu TYPE: %s\n", map->start, map->end, map->mmuflags, map->data, map->offset, debugnames[map->type]); + map = map->next; + } + +} + +static void fragcheck(vmm_mapping* map){ + if(map->type == VMM_TYPE_FILE) + return; + + if(map->prev && map->prev->type == map->type && map->prev->mmuflags == map->mmuflags){ + vmm_mapping *prev = map->prev; + prev->next = map->next; + prev->end = map->end; + if(map->next) + map->next->prev = prev; + freeentry(map); + map = prev; + } + + if(map->next && map->next->type == map->type && map->next->mmuflags == map->mmuflags){ + vmm_mapping* next = map->next; + map->next = next->next; + map->end = next->end; + if(next->next) + next->next->prev = map; + freeentry(next); + } + +} + +// FIXME I snatched this from the old codebase. This is ugly and it should definetively be +// rewritten + +static bool setmap(vmm_mapping** mapstart, void* addr, size_t pagec, size_t mmuflags, size_t type, void* data, size_t offset){ + + vmm_mapping* map = *mapstart; + while(map && addr > map->end) + map = map->next; + + if(!map) return false; + + vmm_mapping* newmap = allocatefirst(); + + if(!newmap) return false; + + + newmap->start = addr; + newmap->end = addr + pagec*PAGE_SIZE-1; + newmap->mmuflags = mmuflags; + newmap->type = type; + newmap->data = data; + newmap->offset = offset; + + // spans multiple entries? + if(newmap->end > map->end){ + vmm_mapping* nextmap = map->next; + while(nextmap && newmap->end < nextmap->start) + nextmap = nextmap->next; + + if(!nextmap){ + freeentry(newmap); + return false; + } + + // unallocate entries inbetween + + vmm_mapping* loopmap = map->next; + + while(loopmap != nextmap){ + loopmap = loopmap->next; + freeentry(loopmap->prev); + } + + map->end = newmap->start-1; + + if(nextmap->type == VMM_TYPE_FILE) + nextmap->offset += newmap->end - nextmap->start + 1; + nextmap->start = newmap->end+1; + + map->next = newmap; + newmap->prev = map; + newmap->next = nextmap; + nextmap->prev = newmap; + + if(map->start >= map->end){ + newmap->prev = map->prev; + freeentry(map); + if(map->prev == NULL) + *mapstart = newmap; + } + + if(nextmap->start >= nextmap->end){ + newmap->next = nextmap->next; + freeentry(nextmap); + } + + fragcheck(newmap); + + return true; + + } + + // do we need to split? + + if(map->start != newmap->start && map->end != newmap->end){ + vmm_mapping* splitmap = allocatefirst(); + if(!splitmap){ + freeentry(newmap); + return false; + } + + splitmap->next = map->next; + splitmap->prev = newmap; + splitmap->end = map->end; + splitmap->start = newmap->end+1; + splitmap->type = map->type; + if(splitmap->type == VMM_TYPE_FILE) + splitmap->offset = map->offset + splitmap->start - map->start; + splitmap->data = map->data; + + map->next = newmap; + map->end = newmap->start-1; + + newmap->prev = map; + newmap->next = splitmap; + + return true; + } + + if(map->start == newmap->start){ + newmap->next = map; + newmap->prev = map->prev; + if(newmap->prev){ + vmm_mapping* tmp = newmap->prev; + tmp->next = newmap; + } + else + *mapstart = newmap; + + + map->start = newmap->end+1; + if(map->type == VMM_TYPE_FILE) + map->offset += newmap->end - newmap->start + 1; + map->prev = newmap; + + if(map->start >= map->end){ + newmap->next = map->next; + freeentry(map); + } + } + else{ + // end + newmap->next = map->next; + newmap->prev = map; + + if(newmap->next){ + vmm_mapping* tmp = newmap->next; + tmp->prev = newmap; + } + + map->end = newmap->start-1; + map->next = newmap; + + if(map->start >= map->end){ + newmap->prev = map->prev; + freeentry(map); + } + + } + + + fragcheck(newmap); + return true; + +} + +static vmm_mapping* findmappingfromaddr(vmm_mapping* map, void* addr){ + + while(map && !(addr > map->start && addr <= map->end)) + map = map->next; + + return map; + +} + +static void* findfirstfreearea(vmm_mapping* map, size_t pagec){ + while(map && map->end < map->start + pagec*PAGE_SIZE-1 || map->type != VMM_TYPE_FREE) + map = map->next; + + if(map) return map->start; + + return NULL; +} + + +static bool unmap(vmm_mapping* start, void* addr, size_t pagec){ + +} + + +// TODO do it in the lower half of memory as well + +void* vmm_alloc(size_t pagec, size_t mmuflags){ + spinlock_acquire(&klock); + + void* addr = findfirstfreearea(kmapstart, pagec); + + if(!addr){ + spinlock_release(&klock); + return NULL; + }; + + + + if(!setmap(&kmapstart, addr, pagec, mmuflags, VMM_TYPE_ANON, 0, 0)) + addr = NULL; + + spinlock_release(&klock); + + return addr; +} + +bool vmm_unmap(void* addr, size_t pagec){ + + spinlock_acquire(&klock); + + unmap(kmapstart, addr, pagec); + + spinlock_release(&klock); + +} + +// called by the page fault handler + +bool vmm_dealwithrequest(void* addr){ + + spinlock_acquire(&klock); + + bool status; + + vmm_mapping* map = findmappingfromaddr(kmapstart, addr); + void* paddr; + + if((!map) || map->type == VMM_TYPE_FREE){ + status = false; + goto done; + } + + if(map->type == VMM_TYPE_FILE) + _panic("File mappings are not supported (yet)", 0); + + // allocate a page + + addr = (size_t)addr & ~(0xFFF); + + printf("Demand paging %p\n", addr); + + + + cls_t* cls = arch_getcls(); + + + paddr = pmm_alloc(1); + + + if(addr == NULL || arch_mmu_map(cls->context->context, paddr, addr, map->mmuflags) == false){ + status = false; + goto done; + } + + status = true; + + done: + + spinlock_release(&klock); + + return status; + +} + +void vmm_init(){ + printf("%lu mappings per cache\n", VMM_CACHE_ENTRY_COUNT); + caches = newcache(); + if(!caches) _panic("Out of memory", 0); + + kmapstart = allocentry(caches); + kmapstart->start = (void*)KERNEL_SPACE_START; + kmapstart->end = (void*)KERNEL_SPACE_END; + + + // add the hhdm to the maps + + extern volatile struct limine_memmap_request memmap_req; + size_t count = memmap_req.response->entry_count; + struct limine_memmap_entry** entries = memmap_req.response->entries; + + for(size_t entry = 0; entry < count; ++entry){ + setmap(&kmapstart, (size_t)entries[entry]->base + (size_t)limine_hhdm_offset & ~(0xFFF), entries[entry]->length / PAGE_SIZE, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, VMM_TYPE_ANON, 0, 0); + } + + // now the kernel + + extern int _text_start, _text_end, _rodata_start, _rodata_end, _data_start, _data_end; + + void* textstart = &_text_start; + void* textend = &_text_end; + void* rodatastart = &_rodata_start; + void* rodataend = &_rodata_end; + void* datastart = &_data_start; + void* dataend = &_data_end; + + setmap(&kmapstart, textstart, (textend - textstart) / PAGE_SIZE, ARCH_MMU_MAP_READ, VMM_TYPE_ANON, 0, 0); + + setmap(&kmapstart, rodatastart, (rodataend - rodatastart) / PAGE_SIZE, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_NOEXEC, VMM_TYPE_ANON, 0, 0); + + setmap(&kmapstart, datastart, (dataend - datastart) / PAGE_SIZE, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE | ARCH_MMU_MAP_NOEXEC, VMM_TYPE_ANON, 0, 0); + + + + debug_dumpkernelmappings(); + + printf("Writing to 10 demand paged pages\n"); + + volatile char* all = vmm_alloc(10, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE); + printf("Alloc at %p\n", all); + + + for(size_t i = 0; i < PAGE_SIZE*10; ++i) + *(all + i) = 0; + + + printf("WOOOOOO!!!\n"); + +}