diff --git a/core/main.c b/core/main.c index 182341c..c3817e1 100644 --- a/core/main.c +++ b/core/main.c @@ -7,12 +7,14 @@ #include "klibc.h" #include "mem.h" #include "multiboot.h" +#include "paging.h" #include "pit.h" #include "serial.h" #include "stdarg.h" #ifdef RUN_TEST #include "test.h" #endif +#include "types.h" #include "vga.h" #define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit))) @@ -59,7 +61,9 @@ void kmain(unsigned long magic, unsigned long addr) } printf("Setting up Pagination\n"); - memSetup(upper_mem); + paddr_t lastUserByMem; + memSetup(upper_mem, &lastUserByMem); + pagingSetup(lastUserByMem); printf("Setting up IRQ handlers\n"); irqSetRoutine(IRQ_KEYBOARD, keyboard_handler); diff --git a/core/mem.c b/core/mem.c index 4338fb5..13a402c 100644 --- a/core/mem.c +++ b/core/mem.c @@ -9,22 +9,23 @@ static struct mem_desc *used_page; static unsigned long bottom_mem; static unsigned long top_mem; -int memSetup(unsigned long upper_mem) +int memSetup(paddr_t upperMem, paddr_t * lastUsedOut) { // Align upper mem (in kB) on page size even if it does loose a page - upper_mem = ALIGN_DOWN(upper_mem, PAGE_SIZE / 1024); + upperMem = ALIGN_DOWN(upperMem, PAGE_SIZE / 1024); - printf("Available Mem from %d to %d \n", &__ld_kernel_end, upper_mem * 1024); + printf("Available Mem from %d to %d \n", &__ld_kernel_end, upperMem * 1024); // Memory description is stored after the kernel. We need some place to store it unsigned long memdesc_end = (unsigned long)page_desc + - ((upper_mem) / (PAGE_SIZE / 1024)) * sizeof(struct mem_desc); + ((upperMem) / (PAGE_SIZE / 1024)) * sizeof(struct mem_desc); uint lastUsed = (memdesc_end >> PAGE_SHIFT) + 1; list_init(free_page); list_init(used_page); bottom_mem = lastUsed; - top_mem = upper_mem * 1024; - for (uint i = 0; i < (upper_mem / (PAGE_SIZE / 1024)); i++) { + *lastUsedOut = memdesc_end; + top_mem = upperMem * 1024; + for (uint i = 0; i < (upperMem / (PAGE_SIZE / 1024)); i++) { struct mem_desc *mem = &page_desc[i]; if (i < lastUsed) { mem->ref = 1; diff --git a/core/mem.h b/core/mem.h index bdba9f8..bb58650 100644 --- a/core/mem.h +++ b/core/mem.h @@ -17,7 +17,7 @@ struct mem_desc{ }; -int memSetup(unsigned long upper_mem); +int memSetup(paddr_t upperMem, paddr_t *lastUsed); paddr_t allocPhyPage(void); int unrefPhyPage(paddr_t addr); int refPhyPage(paddr_t addr); diff --git a/core/paging.c b/core/paging.c new file mode 100644 index 0000000..b83df2e --- /dev/null +++ b/core/paging.c @@ -0,0 +1,118 @@ +#include "klibc.h" +#include "mem.h" +#include "stdarg.h" + +// In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry point to a Page Table. +// The 10 next bits are then an index in this Page Table. A Page Table Entry then point to a physical address at which is added the remaining 12 bits. +// So they are 1024 entry in the PD, each of them pointing to a PT of 1024 entry. Each PTE pointing to 4K page. +// First address (up to page_desc from mem.c) are mapped such as Paddr == Vaddr. +// To make PD always accessible a (x86?) trick is used : The mirroring. A given entry N in the PD point to the PD (this is possible because PDE very looks like PTE in x86). +// So N << (10 + 12 = 4Mo) point to the Paddr of PD. Then, accessing N * 4Mo + I * 4Ko is accessing the PT of the Ieme entry in the PD (as MMU take the PD pointed by the PDE number N like a PT). +// More particularly, accessing N * 4Mo + N * 4ko is accessing the PD. +// +// PD is at Vaddr N * 4Mo and take 4ko. Each PT are allocated dynamically. +// Just make sure that N have not been used by identity mapping + +#define PT_SHIFT 12 +#define PTE_MASK 0x3ff //10bits +#define PD_SHIFT 22 +#define PD_MIRROR_PAGE_IDX 1023 + +struct pde { + uint32_t present : 1; + uint32_t write : 1; // 0 read - 1 RW + uint32_t user : 1; // 0 supervisor - 1 user + uint32_t write_through : 1; // 0 write-back - 1 write_through + uint32_t cache_disable : 1; + uint32_t access : 1; // have been accessed + uint32_t zero : 1; // Not used + uint32_t size : 1; // 0 for 4Kb 1 for 4Mb + uint32_t ignored : 1; + uint32_t available : 3; + uint32_t pt_addr : 20; +} __attribute__((packed)); + +struct pte { + uint32_t present : 1; + uint32_t write : 1; // 0 read - 1 RW + uint32_t user : 1; // 0 supervisor - 1 user + uint32_t write_through : 1; // 0 write-back - 1 write_through + uint32_t cache_disable : 1; + uint32_t access : 1; // have been accessed + uint32_t dirty : 1; // if set, indicates that page has been written to. This flag is + // not updated by the CPU, and once set will not unset itself. + uint32_t zero : 1; // if PAT is supported, shall indicate the memory type. Otherwise, + // it must be 0. + uint32_t global : 1; // if set, prevents the TLB from updating the address in its cache + // if CR3 is reset. Note, that the page global enable bit in CR4 + // must be set to enable this feature. + uint32_t available : 3; + uint32_t paddr : 20; +} __attribute__((packed)); + +struct pdbr { + uint32_t zero1 : 3; // reserved + uint32_t write_through : 1; // 0 write-back - 1 write-through + uint32_t cache_disabled : 1; // 1=cache disabled + uint32_t zero2 : 7; // reserved + uint32_t pd_paddr : 20; +} __attribute__((packed)); + +static inline void __native_flush_tlb_single(unsigned long addr) +{ + asm volatile("invlpg (%0)" ::"r"(addr) : "memory"); +} + +int pagingSetup(paddr_t upperKernelAddr) +{ + struct pdbr cr3; + + // x86 got 1024 of pde for 4Byte each: 4ko ! + struct pde *pd = (struct pde *)allocPhyPage(); + + memset(pd, 0, PAGE_SIZE); + memset(&cr3, 0x0, sizeof(struct pdbr)); + + cr3.pd_paddr = ((paddr_t)pd) >> 12; + + // MMU not enabled for the moment. No need to use mirroring + // Identity mapping up to upperKernelAddr + for (paddr_t i = 0; i < upperKernelAddr; i += PAGE_SIZE) { + uint pdEntry = i >> (PD_SHIFT); + uint ptEntry = (i >> PT_SHIFT ) & PTE_MASK; + struct pte *pt; + if (pd[pdEntry].present){ + pt = (struct pte *)(pd[pdEntry].pt_addr << PT_SHIFT); + refPhyPage((paddr_t)pt); + } else { + pt = (struct pte *)allocPhyPage(); + + memset(pt, 0, PAGE_SIZE); + pd[pdEntry].present = 1; + pd[pdEntry].write = 1; + pd[pdEntry].pt_addr = ((paddr_t)pt >> PT_SHIFT); + } + + pt[ptEntry].present = 1; + pt[ptEntry].write = 1; //TODO set Kernel code as RO + pt[ptEntry].paddr = i >> PAGE_SHIFT; + } + + // Setup mirroring + pd[PD_MIRROR_PAGE_IDX].present = 1; + pd[PD_MIRROR_PAGE_IDX].write = 1; + pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT); + + + // Loading of the PDBR in the MMU: + asm volatile ("movl %0,%%cr3\n\t" + "movl %%cr0,%%eax\n\t" + "orl $0x80010000, %%eax\n\t" /* bit 31 | bit 16 */ + "movl %%eax,%%cr0\n\t" + "jmp 1f\n\t" + "1:\n\t" + "movl $2f, %%eax\n\t" + "jmp *%%eax\n\t" + "2:\n\t" ::"r"(cr3):"memory","eax"); + return 0; +} diff --git a/core/paging.h b/core/paging.h new file mode 100644 index 0000000..5daf35f --- /dev/null +++ b/core/paging.h @@ -0,0 +1,4 @@ +#pragma once +#include "types.h" + +int pagingSetup(paddr_t upperKernelAddr);