Basic MMU usage
This commit is contained in:
parent
92f48d1851
commit
18be89ebfe
@ -7,12 +7,14 @@
|
|||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "multiboot.h"
|
#include "multiboot.h"
|
||||||
|
#include "paging.h"
|
||||||
#include "pit.h"
|
#include "pit.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "stdarg.h"
|
#include "stdarg.h"
|
||||||
#ifdef RUN_TEST
|
#ifdef RUN_TEST
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "types.h"
|
||||||
#include "vga.h"
|
#include "vga.h"
|
||||||
|
|
||||||
#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit)))
|
#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");
|
printf("Setting up Pagination\n");
|
||||||
memSetup(upper_mem);
|
paddr_t lastUserByMem;
|
||||||
|
memSetup(upper_mem, &lastUserByMem);
|
||||||
|
pagingSetup(lastUserByMem);
|
||||||
|
|
||||||
printf("Setting up IRQ handlers\n");
|
printf("Setting up IRQ handlers\n");
|
||||||
irqSetRoutine(IRQ_KEYBOARD, keyboard_handler);
|
irqSetRoutine(IRQ_KEYBOARD, keyboard_handler);
|
||||||
|
13
core/mem.c
13
core/mem.c
@ -9,22 +9,23 @@ static struct mem_desc *used_page;
|
|||||||
static unsigned long bottom_mem;
|
static unsigned long bottom_mem;
|
||||||
static unsigned long top_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
|
// 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
|
// Memory description is stored after the kernel. We need some place to store it
|
||||||
unsigned long memdesc_end =
|
unsigned long memdesc_end =
|
||||||
(unsigned long)page_desc +
|
(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;
|
uint lastUsed = (memdesc_end >> PAGE_SHIFT) + 1;
|
||||||
list_init(free_page);
|
list_init(free_page);
|
||||||
list_init(used_page);
|
list_init(used_page);
|
||||||
bottom_mem = lastUsed;
|
bottom_mem = lastUsed;
|
||||||
top_mem = upper_mem * 1024;
|
*lastUsedOut = memdesc_end;
|
||||||
for (uint i = 0; i < (upper_mem / (PAGE_SIZE / 1024)); i++) {
|
top_mem = upperMem * 1024;
|
||||||
|
for (uint i = 0; i < (upperMem / (PAGE_SIZE / 1024)); i++) {
|
||||||
struct mem_desc *mem = &page_desc[i];
|
struct mem_desc *mem = &page_desc[i];
|
||||||
if (i < lastUsed) {
|
if (i < lastUsed) {
|
||||||
mem->ref = 1;
|
mem->ref = 1;
|
||||||
|
@ -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);
|
paddr_t allocPhyPage(void);
|
||||||
int unrefPhyPage(paddr_t addr);
|
int unrefPhyPage(paddr_t addr);
|
||||||
int refPhyPage(paddr_t addr);
|
int refPhyPage(paddr_t addr);
|
||||||
|
118
core/paging.c
Normal file
118
core/paging.c
Normal file
@ -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;
|
||||||
|
}
|
4
core/paging.h
Normal file
4
core/paging.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
int pagingSetup(paddr_t upperKernelAddr);
|
Loading…
Reference in New Issue
Block a user