From 277e4af4fa9e80816c809542d792ee6bebb7f202 Mon Sep 17 00:00:00 2001 From: Alex AUVOLAT Date: Fri, 4 May 2012 20:06:37 +0200 Subject: Migration to C++! --- src/kernel/mem/_dlmalloc.h | 3 + src/kernel/mem/gdt.c | 64 -------------- src/kernel/mem/gdt.cpp | 64 ++++++++++++++ src/kernel/mem/mem.c | 137 ------------------------------ src/kernel/mem/mem.cpp | 137 ++++++++++++++++++++++++++++++ src/kernel/mem/mem.h | 12 ++- src/kernel/mem/paging.c | 203 --------------------------------------------- src/kernel/mem/paging.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++ src/kernel/mem/paging.h | 26 +++--- src/kernel/mem/seg.c | 113 ------------------------- src/kernel/mem/seg.cpp | 113 +++++++++++++++++++++++++ src/kernel/mem/seg.h | 24 +++--- 12 files changed, 550 insertions(+), 549 deletions(-) delete mode 100644 src/kernel/mem/gdt.c create mode 100644 src/kernel/mem/gdt.cpp delete mode 100644 src/kernel/mem/mem.c create mode 100644 src/kernel/mem/mem.cpp delete mode 100644 src/kernel/mem/paging.c create mode 100644 src/kernel/mem/paging.cpp delete mode 100644 src/kernel/mem/seg.c create mode 100644 src/kernel/mem/seg.cpp (limited to 'src/kernel/mem') diff --git a/src/kernel/mem/_dlmalloc.h b/src/kernel/mem/_dlmalloc.h index 7b2e2cc..a80273c 100644 --- a/src/kernel/mem/_dlmalloc.h +++ b/src/kernel/mem/_dlmalloc.h @@ -1,6 +1,7 @@ #ifndef DEF_DLMALLOC_H #define DEF_DLMALLOC_H +#include #include "lib/std.h" #include #include @@ -16,6 +17,8 @@ #define USE_LOCKS 2 + + /* Version identifier to allow people to support multiple versions */ #ifndef DLMALLOC_VERSION #define DLMALLOC_VERSION 20805 diff --git a/src/kernel/mem/gdt.c b/src/kernel/mem/gdt.c deleted file mode 100644 index 494aaf2..0000000 --- a/src/kernel/mem/gdt.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "gdt.h" -#include -#include - -extern void gdt_flush(uint32_t); //ASM (imported from idt_.asm) -extern void tss_flush(); - -#define GDT_ENTRIES 6 // The contents of each entry is defined in gdt_init. - -static struct tss_entry tss_entry; -static struct gdt_entry gdt_entries[GDT_ENTRIES]; -static struct gdt_ptr gdt_ptr; - -/* This function is called by the task_switch function on each context switch. - It updates the TSS so that an interrupt will be handled on the correct kernel stack - (one kernel stack is allocated to each thread). */ -void gdt_setKernelStack(uint32_t esp0) { - tss_entry.esp0 = esp0; -} - -/* For internal use only. Writes one entry of the GDT with given parameters. */ -static void gdt_setGate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { - gdt_entries[num].base_low = (base & 0xFFFF); - gdt_entries[num].base_middle = (base >> 16) & 0xFF; - gdt_entries[num].base_high = (base >> 24) & 0xFF; - - gdt_entries[num].limit_low = (limit & 0xFFFF); - gdt_entries[num].granularity = (limit >> 16) & 0x0F; - gdt_entries[num].granularity |= gran & 0xF0; - gdt_entries[num].access = access; -} - -/* For internal use only. Writes one entry of the GDT, that entry being a pointer to the TSS. */ -static void gdt_writeTss(int num, uint32_t ss0, uint32_t esp0) { - uint32_t base = (uint32_t)&tss_entry; - uint32_t limit = base + sizeof(struct tss_entry); - - gdt_setGate(num, base, limit, 0xE9, 0); - - memset(&tss_entry, 0, sizeof(struct tss_entry)); - - tss_entry.ss0 = ss0; tss_entry.esp0 = esp0; - - tss_entry.cs = 0x0b; - tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13; -} - -/* Write data to the GDT and enable it. */ -void gdt_init() { - gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1; - gdt_ptr.base = (uint32_t)&gdt_entries; - - gdt_setGate(0, 0, 0, 0, 0); //Null segment - gdt_setGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Kernel code segment 0x08 - gdt_setGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Kernel data segment 0x10 - gdt_setGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User code segment 0x18 - gdt_setGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User data segment 0x20 - gdt_writeTss(5, 0x10, 0); - - gdt_flush((uint32_t)&gdt_ptr); - tss_flush(); - - monitor_write("[GDT] "); -} diff --git a/src/kernel/mem/gdt.cpp b/src/kernel/mem/gdt.cpp new file mode 100644 index 0000000..49fbd82 --- /dev/null +++ b/src/kernel/mem/gdt.cpp @@ -0,0 +1,64 @@ +#include "gdt.h" +#include +#include + +extern "C" void gdt_flush(uint32_t); //ASM (imported from idt_.asm) +extern "C" void tss_flush(); + +#define GDT_ENTRIES 6 // The contents of each entry is defined in gdt_init. + +static tss_entry tss_entry; +static gdt_entry gdt_entries[GDT_ENTRIES]; +static gdt_ptr gdt_ptr; + +/* This function is called by the task_switch function on each context switch. + It updates the TSS so that an interrupt will be handled on the correct kernel stack + (one kernel stack is allocated to each thread). */ +void gdt_setKernelStack(uint32_t esp0) { + tss_entry.esp0 = esp0; +} + +/* For internal use only. Writes one entry of the GDT with given parameters. */ +static void gdt_setGate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { + gdt_entries[num].base_low = (base & 0xFFFF); + gdt_entries[num].base_middle = (base >> 16) & 0xFF; + gdt_entries[num].base_high = (base >> 24) & 0xFF; + + gdt_entries[num].limit_low = (limit & 0xFFFF); + gdt_entries[num].granularity = (limit >> 16) & 0x0F; + gdt_entries[num].granularity |= gran & 0xF0; + gdt_entries[num].access = access; +} + +/* For internal use only. Writes one entry of the GDT, that entry being a pointer to the TSS. */ +static void gdt_writeTss(int num, uint32_t ss0, uint32_t esp0) { + uint32_t base = (uint32_t)&tss_entry; + uint32_t limit = base + sizeof(tss_entry); + + gdt_setGate(num, base, limit, 0xE9, 0); + + memset(&tss_entry, 0, sizeof(tss_entry)); + + tss_entry.ss0 = ss0; tss_entry.esp0 = esp0; + + tss_entry.cs = 0x0b; + tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13; +} + +/* Write data to the GDT and enable it. */ +void gdt_init() { + gdt_ptr.limit = (sizeof(gdt_entry) * GDT_ENTRIES) - 1; + gdt_ptr.base = (uint32_t)&gdt_entries; + + gdt_setGate(0, 0, 0, 0, 0); //Null segment + gdt_setGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Kernel code segment 0x08 + gdt_setGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Kernel data segment 0x10 + gdt_setGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User code segment 0x18 + gdt_setGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User data segment 0x20 + gdt_writeTss(5, 0x10, 0); + + gdt_flush((uint32_t)&gdt_ptr); + tss_flush(); + + monitor_write("[GDT] "); +} diff --git a/src/kernel/mem/mem.c b/src/kernel/mem/mem.c deleted file mode 100644 index 3310b70..0000000 --- a/src/kernel/mem/mem.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "mem.h" -#include -#include -#include "paging.h" - -#include - -#include "_dlmalloc.h" - -#include "mem.h" - -#define FREEPAGESTOKEEP 5 - -#define KHEAP_IDXSIZE 0x4000 // only used with heap.std.h -#define KHEAP_INITSIZE 0x00080000 -#define KHEAP_MAXSIZE 0x08000000 - -size_t mem_placementAddr; -int _no_more_ksbrk = 0; - - -// ****************************** -// PAGE ALLOCATION -// **************************** -static struct freepage { - size_t virt, phys; -} freepages[FREEPAGESTOKEEP]; -uint32_t freepagecount = 0; - -/* For internal use only. Populates the cache of pages that can be given to requesters. */ -static void get_free_pages() { - static uint32_t locked = 0; - uint32_t i; - if (locked) return; - locked = 1; - while (freepagecount < FREEPAGESTOKEEP) { - if (_no_more_ksbrk) { - for (i = 0xFFFFF000; i >= 0xF0000000; i -= 0x1000) { - if (pagedir_getPage(kernel_pagedir, i, 1)->frame == 0) break; - } - freepages[freepagecount].virt = i; - uint32_t frame = frame_alloc(); - freepages[freepagecount].phys = frame * 0x1000; - page_map(pagedir_getPage(kernel_pagedir, i, 0), freepages[freepagecount].phys / 0x1000, 0, 0); - freepagecount++; - } else { - if (mem_placementAddr & 0xFFFFF000) { - mem_placementAddr &= 0xFFFFF000; - mem_placementAddr += 0x1000; - } - freepages[freepagecount].virt = (size_t)ksbrk(0x1000); - freepages[freepagecount].phys = freepages[freepagecount].virt - K_HIGHHALF_ADDR; - freepagecount++; - } - } - locked = 0; -} - -/* Gives one page from the cache to someone requesting it. */ -void* kmalloc_page(size_t *phys) { - cli(); - get_free_pages(); - freepagecount--; - *phys = freepages[freepagecount].phys; - size_t tmp = freepages[freepagecount].virt; - sti(); - return (void*)tmp; -} - -void kfree_page(void* ptr) { - size_t addr = (size_t)ptr; - if (_no_more_ksbrk) { - page_unmapFree(pagedir_getPage(kernel_pagedir, addr, 0)); - } -} - -//*********************************** -// MEMORY ALLOCATION FOR DLMALLOC -// ************************* - -void* ksbrk(size_t size) { - if (_no_more_ksbrk == 0) { // ksbrk is NOT being called by dlmalloc - if (size & 0x0FFF) { - size = (size & 0xFFFFF000) + 0x1000; - } - } - - size_t tmp = mem_placementAddr; - size_t er_begin, er_end, i; - - /* (DBG) monitor_write(" "); */ - - mem_placementAddr += size; - - if (_no_more_ksbrk) { // paging enabled, we must allocate these pages - if (tmp < mem_placementAddr) { - er_begin = tmp; - if (er_begin & 0x0FFF) er_begin = (er_begin & 0xFFFFF000) + 0x1000; - er_end = mem_placementAddr; - if (er_end & 0x0FFF) er_end = (er_end & 0xFFFFF000) + 0x1000; - for (i = er_begin; i < er_end; i += 0x1000) { - struct page *p = pagedir_getPage(kernel_pagedir, i, 1); - size_t f = frame_alloc(); - page_map(p, f, 0, 0); - /* (DBG) monitor_write(" "); */ - } - } else if (tmp > mem_placementAddr) { - er_begin = (size_t)mem_placementAddr; - if (er_begin & 0x0FFF) er_begin = (er_begin & 0xFFFFF000) + 0x1000; - er_end = tmp; - if (er_end & 0x0FFF) er_end = (er_end & 0xFFFFF000) + 0x1000; - for (i = er_end - 0x1000; i >= er_begin; i -= 0x1000) { - // (DBG) monitor_write(" "); - page_unmapFree(pagedir_getPage(kernel_pagedir, i, 0)); - } - } - } - - return (void*)tmp; -} - -void kbrk(void* ptr) { - monitor_write("\n"); - - if ((size_t)ptr > (size_t)&end) { - ksbrk(ptr - (size_t)mem_placementAddr); - } else { - PANIC("INVALID KBRK."); - } -} diff --git a/src/kernel/mem/mem.cpp b/src/kernel/mem/mem.cpp new file mode 100644 index 0000000..5e04de3 --- /dev/null +++ b/src/kernel/mem/mem.cpp @@ -0,0 +1,137 @@ +#include "mem.h" +#include +#include +#include "paging.h" + +#include + +#include "_dlmalloc.h" + +#include "mem.h" + +#define FREEPAGESTOKEEP 5 + +#define KHEAP_IDXSIZE 0x4000 // only used with heap.std.h +#define KHEAP_INITSIZE 0x00080000 +#define KHEAP_MAXSIZE 0x08000000 + +size_t mem_placementAddr; +bool _no_more_ksbrk = false; + + +// ****************************** +// PAGE ALLOCATION +// **************************** +static struct freepage { + size_t virt, phys; +} freepages[FREEPAGESTOKEEP]; +uint32_t freepagecount = 0; + +/* For internal use only. Populates the cache of pages that can be given to requesters. */ +static void get_free_pages() { + static uint32_t locked = 0; + uint32_t i; + if (locked) return; + locked = 1; + while (freepagecount < FREEPAGESTOKEEP) { + if (_no_more_ksbrk) { + for (i = 0xFFFFF000; i >= 0xF0000000; i -= 0x1000) { + if (pagedir_getPage(kernel_pagedir, i, 1)->frame == 0) break; + } + freepages[freepagecount].virt = i; + uint32_t frame = frame_alloc(); + freepages[freepagecount].phys = frame * 0x1000; + page_map(pagedir_getPage(kernel_pagedir, i, 0), freepages[freepagecount].phys / 0x1000, 0, 0); + freepagecount++; + } else { + if (mem_placementAddr & 0xFFFFF000) { + mem_placementAddr &= 0xFFFFF000; + mem_placementAddr += 0x1000; + } + freepages[freepagecount].virt = (size_t)ksbrk(0x1000); + freepages[freepagecount].phys = freepages[freepagecount].virt - K_HIGHHALF_ADDR; + freepagecount++; + } + } + locked = 0; +} + +/* Gives one page from the cache to someone requesting it. */ +void* kmalloc_page(size_t *phys) { + cli(); + get_free_pages(); + freepagecount--; + *phys = freepages[freepagecount].phys; + size_t tmp = freepages[freepagecount].virt; + sti(); + return (void*)tmp; +} + +void kfree_page(void* ptr) { + size_t addr = (size_t)ptr; + if (_no_more_ksbrk) { + page_unmapFree(pagedir_getPage(kernel_pagedir, addr, 0)); + } +} + +//*********************************** +// MEMORY ALLOCATION FOR DLMALLOC +// ************************* + +void* ksbrk(size_t size) { + if (!_no_more_ksbrk) { // ksbrk is NOT being called by dlmalloc + if (size & 0x0FFF) { + size = (size & 0xFFFFF000) + 0x1000; + } + } + + size_t tmp = mem_placementAddr; + size_t er_begin, er_end, i; + + /* (DBG) monitor_write(" "); */ + + mem_placementAddr += size; + + if (_no_more_ksbrk) { // paging enabled, we must allocate these pages + if (tmp < mem_placementAddr) { + er_begin = tmp; + if (er_begin & 0x0FFF) er_begin = (er_begin & 0xFFFFF000) + 0x1000; + er_end = mem_placementAddr; + if (er_end & 0x0FFF) er_end = (er_end & 0xFFFFF000) + 0x1000; + for (i = er_begin; i < er_end; i += 0x1000) { + page *p = pagedir_getPage(kernel_pagedir, i, 1); + size_t f = frame_alloc(); + page_map(p, f, 0, 0); + /* (DBG) monitor_write(" "); */ + } + } else if (tmp > mem_placementAddr) { + er_begin = (size_t)mem_placementAddr; + if (er_begin & 0x0FFF) er_begin = (er_begin & 0xFFFFF000) + 0x1000; + er_end = tmp; + if (er_end & 0x0FFF) er_end = (er_end & 0xFFFFF000) + 0x1000; + for (i = er_end - 0x1000; i >= er_begin; i -= 0x1000) { + // (DBG) monitor_write(" "); + page_unmapFree(pagedir_getPage(kernel_pagedir, i, 0)); + } + } + } + + return (void*)tmp; +} + +void kbrk(void* ptr) { + monitor_write("\n"); + + if ((size_t)ptr > (size_t)&end) { + ksbrk((size_t)ptr - (size_t)mem_placementAddr); + } else { + PANIC("INVALID KBRK."); + } +} diff --git a/src/kernel/mem/mem.h b/src/kernel/mem/mem.h index cb9c396..085d9ce 100644 --- a/src/kernel/mem/mem.h +++ b/src/kernel/mem/mem.h @@ -11,19 +11,17 @@ void* kmalloc_page(size_t *phys); void kfree_page(void* page); // Internal, used by dlmalloc -void* ksbrk(size_t size); -void kbrk(void* ptr); +extern "C" void* ksbrk(size_t size); +extern "C" void kbrk(void* ptr); #define kmalloc dlmalloc #define kfree dlfree -void kheap_init(); - -extern int _no_more_ksbrk; +extern bool _no_more_ksbrk; extern size_t mem_placementAddr; -extern void end; //Symbol defined by linker : end of kernel code +extern "C" size_t end; //Symbol defined by linker : end of kernel code -extern void k_highhalf_addr; // Symbol defined by linker : high half position of the kerne +extern "C" size_t k_highhalf_addr; // Symbol defined by linker : high half position of the kerne // Should be at 0xC0000000 #define K_HIGHHALF_ADDR ((size_t)(&k_highhalf_addr)) diff --git a/src/kernel/mem/paging.c b/src/kernel/mem/paging.c deleted file mode 100644 index 0527f06..0000000 --- a/src/kernel/mem/paging.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "paging.h" -#include -#include -#include -#include "mem.h" -#include "seg.h" -#include -#include - -static struct bitset frames; - -struct page_directory *kernel_pagedir, *current_pagedir; - -/************************** PHYSICAL MEMORY ALLOCATION ************************/ - -/* Allocates a page of physical memory. */ -uint32_t frame_alloc() { - uint32_t free = bitset_firstFree(&frames); - if (free == (uint32_t) -1) { - PANIC("No more frames to allocate, system is out of memory!"); - } - bitset_set(&frames, free); - return free; -} - -void frame_free(uint32_t id) { - bitset_clear(&frames, id); -} - -/************************* PAGING INITIALIZATION *****************************/ - -/* This function creates the kernel page directory. It must be called before the GDT is loaded. - It maps 0xC0000000+ (k_highhalf_addr) to the corresponding physical kernel code, but it also maps - 0x00000000+ to that code because with the false GDT we set up in loader_.asm, - the code will be looked for at the beginning of the memory. Only when the real GDT is loaded - we can de-allocate pages at 0x00000000 ; this is done by paging_cleanup. */ -void paging_init(size_t totalRam) { - uint32_t i; - - frames.size = totalRam / 0x1000; - frames.bits = ksbrk(INDEX_FROM_BIT(frames.size)); - - kernel_pagedir = ksbrk(sizeof(struct page_directory)); - kernel_pagedir->mappedSegs = 0; - kernel_pagedir->tablesPhysical = kmalloc_page(&kernel_pagedir->physicalAddr); - for (i = 0; i < 1024; i++) { - kernel_pagedir->tables[i] = 0; - kernel_pagedir->tablesPhysical[i] = 0; - } - - for (i = K_HIGHHALF_ADDR; i < mem_placementAddr; i += 0x1000) { - page_map(pagedir_getPage(kernel_pagedir, i, 1), frame_alloc(), 0, 0); - } - for (i = 0; i < (mem_placementAddr - K_HIGHHALF_ADDR) / 0x100000; i++) { - kernel_pagedir->tablesPhysical[i] = kernel_pagedir->tablesPhysical[i + FIRST_KERNEL_PAGETABLE]; - kernel_pagedir->tables[i] = kernel_pagedir->tables[i + FIRST_KERNEL_PAGETABLE]; - } - - monitor_write("{PD: "); - monitor_writeHex(kernel_pagedir->physicalAddr); - pagedir_switch(kernel_pagedir); - monitor_write("} [Paging] "); -} - -/* De-allocates pages at 0x00000000 where kernel code was read from with the GDT from loader_.asm. */ -void paging_cleanup() { - size_t i; - for (i = 0; i < (mem_placementAddr - K_HIGHHALF_ADDR) / 0x100000; i++) { - kernel_pagedir->tablesPhysical[i] = 0; - kernel_pagedir->tables[i] = 0; - } - monitor_write("[PD Cleanup] "); -} - -/************************* PAGING EVERYDAY USE *****************************/ - -/* Switch to a page directory. Can be done if we are sure not to be interrupted by a task switch. - Example use for cross-memory space writing in linker/elf.c */ -void pagedir_switch(struct page_directory *pd) { - current_pagedir = pd; - asm volatile("mov %0, %%cr3" : : "r"(pd->physicalAddr)); - uint32_t cr0; - asm volatile("mov %%cr0, %0" : "=r"(cr0)); - cr0 |= 0x80000000; - asm volatile("mov %0, %%cr0" : : "r"(cr0)); -} - -/* Creates a new page directory for a process, and maps the kernel page tables on it. */ -struct page_directory *pagedir_new() { - uint32_t i; - - struct page_directory *pd = kmalloc(sizeof(struct page_directory)); - pd->tablesPhysical = kmalloc_page(&pd->physicalAddr); - pd->mappedSegs = 0; - - for (i = 0; i < 1024; i++) { - pd->tables[i] = 0; pd->tablesPhysical[i] = 0; - } - - for (i = FIRST_KERNEL_PAGETABLE; i < 1024; i++) { - pd->tables[i] = kernel_pagedir->tables[i]; - pd->tablesPhysical[i] = kernel_pagedir->tablesPhysical[i]; - } - - return pd; -} - -/* Deletes a page directory, cleaning it up. */ -void pagedir_delete(struct page_directory *pd) { - uint32_t i; - //Unmap segments - while (pd->mappedSegs != 0) seg_unmap(pd->mappedSegs); - //Cleanup page tables - for (i = 0; i < FIRST_KERNEL_PAGETABLE; i++) { - kfree_page(pd->tables[i]); - } - kfree_page(pd->tablesPhysical); - kfree(pd); -} - -/* Handle a paging fault. First, looks for the corresponding segment. - If the segment was found and it handles the fault, return normally. - Else, display informatinos and return an error. */ -uint32_t paging_fault(struct registers *regs) { - size_t addr; - struct segment_map *seg = 0; - asm volatile("mov %%cr2, %0" : "=r"(addr)); - - seg = current_pagedir->mappedSegs; - while (seg) { - if (seg->start <= addr && seg->start + seg->len > addr) break; - seg = seg->next; - } - - if (seg != 0) { - if (seg->seg->handle_fault(seg, addr, (regs->err_code & 0x2) && (regs->eip < K_HIGHHALF_ADDR)) != 0) seg = 0; - } - - if (seg == 0) { - NL; WHERE; monitor_write("Unhandled Page Fault\t"); - monitor_write("cr2:"); monitor_writeHex(addr); - NL; TAB; - if (regs->err_code & 0x1) monitor_write("present"); TAB; - if (regs->err_code & 0x2) monitor_write("write"); TAB; - if (regs->err_code & 0x4) monitor_write("user"); TAB; - if (regs->err_code & 0x8) monitor_write("rsvd"); TAB; - if (regs->err_code & 0x10) monitor_write("opfetch"); - return 1; - } - return 0; -} - -/* Gets the corresponding page in a page directory for a given address. - If make is set, the necessary page table can be created. - Can return 0 if make is not set. */ -struct page *pagedir_getPage(struct page_directory *pd, uint32_t address, int make) { - address /= 0x1000; - uint32_t table_idx = address / 1024; - - if (pd->tables[table_idx]) { - return &pd->tables[table_idx]->pages[address % 1024]; - } else if (make) { - pd->tables[table_idx] = kmalloc_page(pd->tablesPhysical + table_idx); - - memset((uint8_t*)pd->tables[table_idx], 0, 0x1000); - pd->tablesPhysical[table_idx] |= 0x07; - - if (table_idx >= FIRST_KERNEL_PAGETABLE) { - tasking_updateKernelPagetable(table_idx, pd->tables[table_idx], pd->tablesPhysical[table_idx]); - } - - return &pd->tables[table_idx]->pages[address % 1024]; - } else { - return 0; - } -} - -/* Modifies a page structure so that it is mapped to a frame. */ -void page_map(struct page *page, uint32_t frame, uint32_t user, uint32_t rw) { - if (page != 0 && page->frame == 0 && page->present == 0) { - page->present = 1; - page->rw = (rw ? 1 : 0); - page->user = (user ? 1 : 0); - page->frame = frame; - } -} - -/* Modifies a page structure so that it is no longer mapped to a frame. */ -void page_unmap(struct page *page) { - if (page != 0) { - page->frame = 0; - page->present = 0; - } -} - -/* Same as above but also frees the frame. */ -void page_unmapFree(struct page *page) { - if (page != 0) { - if (page->frame != 0) frame_free(page->frame); - page->frame = 0; - page->present = 0; - } -} diff --git a/src/kernel/mem/paging.cpp b/src/kernel/mem/paging.cpp new file mode 100644 index 0000000..eb7e615 --- /dev/null +++ b/src/kernel/mem/paging.cpp @@ -0,0 +1,203 @@ +#include "paging.h" +#include +#include +#include +#include "mem.h" +#include "seg.h" +#include +#include + +static bitset frames; + +page_directory *kernel_pagedir, *current_pagedir; + +/************************** PHYSICAL MEMORY ALLOCATION ************************/ + +/* Allocates a page of physical memory. */ +uint32_t frame_alloc() { + uint32_t free = frames.firstFree(); + if (free == (uint32_t) -1) { + PANIC("No more frames to allocate, system is out of memory!"); + } + frames.set(free); + return free; +} + +void frame_free(uint32_t id) { + frames.clear(id); +} + +/************************* PAGING INITIALIZATION *****************************/ + +/* This function creates the kernel page directory. It must be called before the GDT is loaded. + It maps 0xC0000000+ (k_highhalf_addr) to the corresponding physical kernel code, but it also maps + 0x00000000+ to that code because with the false GDT we set up in loader_.asm, + the code will be looked for at the beginning of the memory. Only when the real GDT is loaded + we can de-allocate pages at 0x00000000 ; this is done by paging_cleanup. */ +void paging_init(size_t totalRam) { + uint32_t i; + + frames.size = totalRam / 0x1000; + frames.bits = (uint32_t*)ksbrk(INDEX_FROM_BIT(frames.size)); + + kernel_pagedir = (page_directory*)ksbrk(sizeof(page_directory)); + kernel_pagedir->mappedSegs = 0; + kernel_pagedir->tablesPhysical = (uint32_t*)kmalloc_page(&kernel_pagedir->physicalAddr); + for (i = 0; i < 1024; i++) { + kernel_pagedir->tables[i] = 0; + kernel_pagedir->tablesPhysical[i] = 0; + } + + for (i = K_HIGHHALF_ADDR; i < mem_placementAddr; i += 0x1000) { + page_map(pagedir_getPage(kernel_pagedir, i, 1), frame_alloc(), 0, 0); + } + for (i = 0; i < (mem_placementAddr - K_HIGHHALF_ADDR) / 0x100000; i++) { + kernel_pagedir->tablesPhysical[i] = kernel_pagedir->tablesPhysical[i + FIRST_KERNEL_PAGETABLE]; + kernel_pagedir->tables[i] = kernel_pagedir->tables[i + FIRST_KERNEL_PAGETABLE]; + } + + monitor_write("{PD: "); + monitor_writeHex(kernel_pagedir->physicalAddr); + pagedir_switch(kernel_pagedir); + monitor_write("} [Paging] "); +} + +/* De-allocates pages at 0x00000000 where kernel code was read from with the GDT from loader_.asm. */ +void paging_cleanup() { + size_t i; + for (i = 0; i < (mem_placementAddr - K_HIGHHALF_ADDR) / 0x100000; i++) { + kernel_pagedir->tablesPhysical[i] = 0; + kernel_pagedir->tables[i] = 0; + } + monitor_write("[PD Cleanup] "); +} + +/************************* PAGING EVERYDAY USE *****************************/ + +/* Switch to a page directory. Can be done if we are sure not to be interrupted by a task switch. + Example use for cross-memory space writing in linker/elf.c */ +void pagedir_switch(page_directory *pd) { + current_pagedir = pd; + asm volatile("mov %0, %%cr3" : : "r"(pd->physicalAddr)); + uint32_t cr0; + asm volatile("mov %%cr0, %0" : "=r"(cr0)); + cr0 |= 0x80000000; + asm volatile("mov %0, %%cr0" : : "r"(cr0)); +} + +/* Creates a new page directory for a process, and maps the kernel page tables on it. */ +page_directory *pagedir_new() { + uint32_t i; + + page_directory *pd = new page_directory(); + pd->tablesPhysical = (uint32_t*)kmalloc_page(&pd->physicalAddr); + pd->mappedSegs = 0; + + for (i = 0; i < 1024; i++) { + pd->tables[i] = 0; pd->tablesPhysical[i] = 0; + } + + for (i = FIRST_KERNEL_PAGETABLE; i < 1024; i++) { + pd->tables[i] = kernel_pagedir->tables[i]; + pd->tablesPhysical[i] = kernel_pagedir->tablesPhysical[i]; + } + + return pd; +} + +/* Deletes a page directory, cleaning it up. */ +void pagedir_delete(page_directory *pd) { + uint32_t i; + //Unmap segments + while (pd->mappedSegs != 0) seg_unmap(pd->mappedSegs); + //Cleanup page tables + for (i = 0; i < FIRST_KERNEL_PAGETABLE; i++) { + kfree_page(pd->tables[i]); + } + kfree_page(pd->tablesPhysical); + kfree(pd); +} + +/* Handle a paging fault. First, looks for the corresponding segment. + If the segment was found and it handles the fault, return normally. + Else, display informatinos and return an error. */ +uint32_t paging_fault(registers *regs) { + size_t addr; + segment_map *seg = 0; + asm volatile("mov %%cr2, %0" : "=r"(addr)); + + seg = current_pagedir->mappedSegs; + while (seg) { + if (seg->start <= addr && seg->start + seg->len > addr) break; + seg = seg->next; + } + + if (seg != 0) { + if (seg->seg->handle_fault(seg, addr, (regs->err_code & 0x2) && (regs->eip < K_HIGHHALF_ADDR)) != 0) seg = 0; + } + + if (seg == 0) { + NL; WHERE; monitor_write("Unhandled Page Fault\t"); + monitor_write("cr2:"); monitor_writeHex(addr); + NL; TAB; + if (regs->err_code & 0x1) monitor_write("present"); TAB; + if (regs->err_code & 0x2) monitor_write("write"); TAB; + if (regs->err_code & 0x4) monitor_write("user"); TAB; + if (regs->err_code & 0x8) monitor_write("rsvd"); TAB; + if (regs->err_code & 0x10) monitor_write("opfetch"); + return 1; + } + return 0; +} + +/* Gets the corresponding page in a page directory for a given address. + If make is set, the necessary page table can be created. + Can return 0 if make is not set. */ +page *pagedir_getPage(page_directory *pd, uint32_t address, int make) { + address /= 0x1000; + uint32_t table_idx = address / 1024; + + if (pd->tables[table_idx]) { + return &pd->tables[table_idx]->pages[address % 1024]; + } else if (make) { + pd->tables[table_idx] = (page_table*)kmalloc_page(pd->tablesPhysical + table_idx); + + memset((uint8_t*)pd->tables[table_idx], 0, 0x1000); + pd->tablesPhysical[table_idx] |= 0x07; + + if (table_idx >= FIRST_KERNEL_PAGETABLE) { + tasking_updateKernelPagetable(table_idx, pd->tables[table_idx], pd->tablesPhysical[table_idx]); + } + + return &pd->tables[table_idx]->pages[address % 1024]; + } else { + return 0; + } +} + +/* Modifies a page structure so that it is mapped to a frame. */ +void page_map(page *page, uint32_t frame, uint32_t user, uint32_t rw) { + if (page != 0 && page->frame == 0 && page->present == 0) { + page->present = 1; + page->rw = (rw ? 1 : 0); + page->user = (user ? 1 : 0); + page->frame = frame; + } +} + +/* Modifies a page structure so that it is no longer mapped to a frame. */ +void page_unmap(page *page) { + if (page != 0) { + page->frame = 0; + page->present = 0; + } +} + +/* Same as above but also frees the frame. */ +void page_unmapFree(page *page) { + if (page != 0) { + if (page->frame != 0) frame_free(page->frame); + page->frame = 0; + page->present = 0; + } +} diff --git a/src/kernel/mem/paging.h b/src/kernel/mem/paging.h index 2d5713f..f91fe37 100644 --- a/src/kernel/mem/paging.h +++ b/src/kernel/mem/paging.h @@ -18,32 +18,32 @@ struct page { }; struct page_table { - struct page pages[1024]; + page pages[1024]; }; struct segment_map; struct page_directory { - struct page_table *tables[1024]; //Virtual addresses of page tables + page_table *tables[1024]; //Virtual addresses of page tables uint32_t *tablesPhysical; //Pointer to the virtual address of the page directory (contain phys addr of pt) uint32_t physicalAddr; //Physical address of info above - struct segment_map *mappedSegs; + segment_map *mappedSegs; }; -extern struct page_directory *kernel_pagedir, *current_pagedir; +extern page_directory *kernel_pagedir, *current_pagedir; uint32_t frame_alloc(); void frame_free(uint32_t id); void paging_init(size_t totalRam); void paging_cleanup(); -void pagedir_switch(struct page_directory *pd); -struct page_directory *pagedir_new(); //Creates a brand new empty page directory for a process, with kernel pages -void pagedir_delete(struct page_directory *pagedir); -struct page *pagedir_getPage(struct page_directory *pd, uint32_t address, int make); -void page_map(struct page *page, uint32_t frame, uint32_t user, uint32_t rw); -void page_unmap(struct page *page); -void page_unmapFree(struct page *page); - -uint32_t paging_fault(struct registers *regs); //returns a boolean : 1 if unhandled, 0 if ok +void pagedir_switch(page_directory *pd); +page_directory *pagedir_new(); //Creates a brand new empty page directory for a process, with kernel pages +void pagedir_delete(page_directory *pagedir); +page *pagedir_getPage(page_directory *pd, uint32_t address, int make); +void page_map(page *page, uint32_t frame, uint32_t user, uint32_t rw); +void page_unmap(page *page); +void page_unmapFree(page *page); + +uint32_t paging_fault(registers *regs); //returns a boolean : 1 if unhandled, 0 if ok #endif diff --git a/src/kernel/mem/seg.c b/src/kernel/mem/seg.c deleted file mode 100644 index 4a33db3..0000000 --- a/src/kernel/mem/seg.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "seg.h" -#include "mem.h" -#include - -/* Call this function when mapping a segment to a page directory. - Calls the appropriate map method and updates the segment's and pagedir's information. */ -struct segment_map *seg_map(struct segment* seg, struct page_directory *pagedir, size_t offset) { - struct segment_map *sm = seg->map(seg, pagedir, offset); - if (sm == 0) return 0; - seg->mappings++; - sm->seg = seg; - sm->pagedir = pagedir; - sm->next = pagedir->mappedSegs; - pagedir->mappedSegs = sm; - return sm; -} - -/* Call this function when unmapping a segment from a page directory. - The segment will automatically be deleted if it is not mapped. - Calls the appropriate unmap method and updates the segment's and pagedir's information. */ -void seg_unmap(struct segment_map *map) { - map->seg->unmap(map); - if (map->pagedir->mappedSegs == map) { - map->pagedir->mappedSegs = map->pagedir->mappedSegs->next; - } else { - struct segment_map *m = map->pagedir->mappedSegs; - while (m->next != 0 && m->next != map) m = m->next; - if (m->next == map) m->next = map->next; - } - map->seg->mappings--; - if (map->seg->mappings == 0) { - map->seg->delete(map->seg); - kfree(map->seg->seg_data); - kfree(map->seg); - } - kfree (map); -} - -// ************************************ SIMPLESEG stuff ************* - -static struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pagedir, size_t offset); -static void simpleseg_unmap(struct segment_map*); -static void simpleseg_delete(struct segment *seg); -static int simpleseg_handleFault(struct segment_map* map, size_t addr, int write); - -/* Call this when creating a simpleseg. - Creates the simpleseg structure and the segment structure and fills them up. */ -struct segment* simpleseg_make(size_t start, size_t len, int writable) { - struct simpleseg *ss = kmalloc(sizeof(struct simpleseg)); - struct segment *se = kmalloc(sizeof(struct segment)); - se->seg_data = ss; - se->mappings = 0; - se->map = simpleseg_map; - se->unmap = simpleseg_unmap; - se->delete = simpleseg_delete; - se->handle_fault = simpleseg_handleFault; - ss->writable = writable; ss->start = start; ss->len = len; - return se; -} - -/* For internal use only. Called when a simpleseg is mapped to a pagedirectory. */ -struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pagedir, size_t offset) { - struct segment_map *sm = kmalloc(sizeof(struct segment_map)); - sm->start = ((struct simpleseg*)(seg->seg_data))->start; - sm->len = ((struct simpleseg*)(seg->seg_data))->len; - return sm; -} - -/* For internal use only. Called when a simpleseg is unmapped. - Frees all the allocated pages. */ -void simpleseg_unmap(struct segment_map* sm) { - size_t i; - for (i = sm->start; i < sm->start + sm->len; i += 0x1000) { - page_unmapFree(pagedir_getPage(sm->pagedir, i, 0)); - } -} - -/* For internal use only. Handles a page fault. Can allocate and map a frame if necessary. */ -int simpleseg_handleFault(struct segment_map* sm, size_t addr, int write) { - struct simpleseg *ss = sm->seg->seg_data; - if (write && !ss->writable) return 1; - addr &= 0xFFFFF000; - struct page *p = pagedir_getPage(sm->pagedir, addr, 1); - if (p->frame != 0) return 1; - page_map(p, frame_alloc(), 1, ss->writable); - return 0; -} - -/* For internal use only. Called when the simpleseg is deleted. Does nothing. */ -void simpleseg_delete(struct segment* seg) { -} - -/* Call this to resize a simpleseg. Ajusts the size and frees pages if the new size is smaller.*/ -int simpleseg_resize(struct segment_map *map, size_t len) { - size_t i; - - if (map == 0) return -1; - if (map->seg->delete != simpleseg_delete) return -2; //check segment is a simpleseg - - struct simpleseg *s = (struct simpleseg*)map->seg->seg_data; - if (len & 0xFFF) len = (len & 0xFFFFF000) + 0x1000; - if (len < map->len) { - for (i = map->start + len; i < map->start + map->len; i += 0x1000) { - page_unmapFree(pagedir_getPage(map->pagedir, i, 0)); - } - map->len = len; - s->len = len; - } else if (len > map->len) { - map->len = len; - s->len = len; - } - return 0; -} diff --git a/src/kernel/mem/seg.cpp b/src/kernel/mem/seg.cpp new file mode 100644 index 0000000..c29e94e --- /dev/null +++ b/src/kernel/mem/seg.cpp @@ -0,0 +1,113 @@ +#include "seg.h" +#include "mem.h" +#include + +/* Call this function when mapping a segment to a page directory. + Calls the appropriate map method and updates the segment's and pagedir's information. */ +segment_map *seg_map(segment* seg, page_directory *pagedir, size_t offset) { + segment_map *sm = seg->map(seg, pagedir, offset); + if (sm == 0) return 0; + seg->mappings++; + sm->seg = seg; + sm->pagedir = pagedir; + sm->next = pagedir->mappedSegs; + pagedir->mappedSegs = sm; + return sm; +} + +/* Call this function when unmapping a segment from a page directory. + The segment will automatically be deleted if it is not mapped. + Calls the appropriate unmap method and updates the segment's and pagedir's information. */ +void seg_unmap(segment_map *map) { + map->seg->unmap(map); + if (map->pagedir->mappedSegs == map) { + map->pagedir->mappedSegs = map->pagedir->mappedSegs->next; + } else { + segment_map *m = map->pagedir->mappedSegs; + while (m->next != 0 && m->next != map) m = m->next; + if (m->next == map) m->next = map->next; + } + map->seg->mappings--; + if (map->seg->mappings == 0) { + map->seg->del(map->seg); + kfree(map->seg->seg_data); + kfree(map->seg); + } + kfree (map); +} + +// ************************************ SIMPLESEG stuff ************* + +static segment_map* simpleseg_map(segment* seg, page_directory* pagedir, size_t offset); +static void simpleseg_unmap(segment_map*); +static void simpleseg_delete(segment *seg); +static int simpleseg_handleFault(segment_map* map, size_t addr, int write); + +/* Call this when creating a simpleseg. + Creates the simpleseg structure and the segment structure and fills them up. */ +segment* simpleseg_make(size_t start, size_t len, int writable) { + simpleseg *ss = new simpleseg(); + segment *se = new segment(); + se->seg_data = ss; + se->mappings = 0; + se->map = simpleseg_map; + se->unmap = simpleseg_unmap; + se->del = simpleseg_delete; + se->handle_fault = simpleseg_handleFault; + ss->writable = writable; ss->start = start; ss->len = len; + return se; +} + +/* For internal use only. Called when a simpleseg is mapped to a pagedirectory. */ +segment_map* simpleseg_map(segment* seg, page_directory* pagedir, size_t offset) { + segment_map *sm = new segment_map(); + sm->start = ((simpleseg*)(seg->seg_data))->start; + sm->len = ((simpleseg*)(seg->seg_data))->len; + return sm; +} + +/* For internal use only. Called when a simpleseg is unmapped. + Frees all the allocated pages. */ +void simpleseg_unmap(segment_map* sm) { + size_t i; + for (i = sm->start; i < sm->start + sm->len; i += 0x1000) { + page_unmapFree(pagedir_getPage(sm->pagedir, i, 0)); + } +} + +/* For internal use only. Handles a page fault. Can allocate and map a frame if necessary. */ +int simpleseg_handleFault(segment_map* sm, size_t addr, int write) { + simpleseg *ss = (simpleseg*)sm->seg->seg_data; + if (write && !ss->writable) return 1; + addr &= 0xFFFFF000; + page *p = pagedir_getPage(sm->pagedir, addr, 1); + if (p->frame != 0) return 1; + page_map(p, frame_alloc(), 1, ss->writable); + return 0; +} + +/* For internal use only. Called when the simpleseg is deleted. Does nothing. */ +void simpleseg_delete(segment* seg) { +} + +/* Call this to resize a simpleseg. Ajusts the size and frees pages if the new size is smaller.*/ +int simpleseg_resize(segment_map *map, size_t len) { + size_t i; + + if (map == 0) return -1; + if (map->seg->del != simpleseg_delete) return -2; //check segment is a simpleseg + + simpleseg *s = (simpleseg*)map->seg->seg_data; + if (len & 0xFFF) len = (len & 0xFFFFF000) + 0x1000; + if (len < map->len) { + for (i = map->start + len; i < map->start + map->len; i += 0x1000) { + page_unmapFree(pagedir_getPage(map->pagedir, i, 0)); + } + map->len = len; + s->len = len; + } else if (len > map->len) { + map->len = len; + s->len = len; + } + return 0; +} diff --git a/src/kernel/mem/seg.h b/src/kernel/mem/seg.h index d37ba59..ea95dfa 100644 --- a/src/kernel/mem/seg.h +++ b/src/kernel/mem/seg.h @@ -12,23 +12,23 @@ struct segment { int mappings; // these 4 functions must not be used directly by anyone - struct segment_map* (*map)(struct segment* seg, struct page_directory* pagedir, size_t offset); - void (*unmap)(struct segment_map*); - void (*delete)(struct segment* seg); - int (*handle_fault)(struct segment_map* map, size_t addr, int write); //0 if ok, 1 if segfault + segment_map* (*map)(segment* seg, page_directory* pagedir, size_t offset); + void (*unmap)(segment_map*); + void (*del)(segment* seg); + int (*handle_fault)(segment_map* map, size_t addr, int write); //0 if ok, 1 if segfault }; struct segment_map { - struct segment* seg; - struct page_directory* pagedir; + segment* seg; + page_directory* pagedir; size_t start, len; - struct segment_map *next; + segment_map *next; }; //parameter offset in seg_map doesn't need to be used -struct segment_map *seg_map(struct segment* seg, struct page_directory* pagedir, size_t offset); -/* When unmapping a segment, the segment is deleted if it is not mapped anywhere anymore. */ -void seg_unmap(struct segment_map* map); +segment_map *seg_map(segment* seg, page_directory* pagedir, size_t offset); +// When unmapping a segment, the segment is deleted if it is not mapped anywhere anymore. +void seg_unmap(segment_map* map); /// ************************************* SIMPLESEG stuff ***************** @@ -37,7 +37,7 @@ struct simpleseg { size_t start, len; }; -struct segment* simpleseg_make(size_t start, size_t len, int writable); -int simpleseg_resize(struct segment_map *map, size_t len); +segment* simpleseg_make(size_t start, size_t len, int writable); +int simpleseg_resize(segment_map *map, size_t len); #endif -- cgit v1.2.3