nuclear@17: #include nuclear@17: #include nuclear@17: #include nuclear@17: #include "vm.h" nuclear@17: #include nuclear@17: #include "intr.h" nuclear@17: #include "mem.h" nuclear@17: #include "panic.h" nuclear@17: nuclear@17: nuclear@17: #define KMEM_START 0xc0000000 nuclear@17: #define IDMAP_START 0xa0000 nuclear@17: nuclear@17: #define ATTR_PGDIR_MASK 0x3f nuclear@17: #define ATTR_PGTBL_MASK 0x1ff nuclear@17: #define ADDR_PGENT_MASK 0xfffff000 nuclear@17: nuclear@17: #define PAGEFAULT 14 nuclear@17: nuclear@22: nuclear@22: struct page_range { nuclear@22: int start, end; nuclear@22: struct page_range *next; nuclear@22: }; nuclear@22: nuclear@22: /* defined in vm-asm.S */ nuclear@22: void enable_paging(void); nuclear@22: void set_pgdir_addr(uint32_t addr); nuclear@22: uint32_t get_fault_addr(void); nuclear@22: nuclear@22: static void pgfault(int inum, uint32_t err); nuclear@22: static struct page_range *alloc_node(void); nuclear@22: static void free_node(struct page_range *node); nuclear@22: nuclear@22: /* page directory */ nuclear@22: static uint32_t *pgdir; nuclear@22: nuclear@22: /* 2 lists of free ranges, for kernel memory and user memory */ nuclear@22: static struct page_range *pglist[2]; nuclear@22: /* list of free page_range structures to be used in the lists */ nuclear@22: static struct page_range *node_pool; nuclear@22: nuclear@22: nuclear@17: void init_vm(struct mboot_info *mb) nuclear@17: { nuclear@19: uint32_t idmap_end; nuclear@19: nuclear@17: init_mem(mb); nuclear@17: nuclear@18: pgdir = (uint32_t*)alloc_phys_page(); nuclear@17: memset(pgdir, 0, sizeof pgdir); nuclear@17: nuclear@17: /* map the video memory and kernel code 1-1 */ nuclear@19: get_kernel_mem_range(0, &idmap_end); nuclear@19: map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0); nuclear@17: nuclear@17: interrupt(PAGEFAULT, pgfault); nuclear@17: nuclear@17: set_pgdir_addr((int32_t)pgdir); nuclear@17: enable_paging(); nuclear@17: } nuclear@17: nuclear@17: void map_page(int vpage, int ppage, unsigned int attr) nuclear@17: { nuclear@17: uint32_t *pgtbl; nuclear@17: int diridx = PAGE_TO_PGTBL(vpage); nuclear@17: int pgidx = PAGE_TO_PGTBL_PG(vpage); nuclear@17: nuclear@17: if(!(pgdir[diridx] & PG_PRESENT)) { nuclear@17: uint32_t addr = alloc_phys_page(); nuclear@17: pgtbl = (uint32_t*)addr; nuclear@18: memset(pgtbl, 0, PGSIZE); nuclear@18: nuclear@17: pgdir[diridx] = addr | (attr & ATTR_PGDIR_MASK) | PG_PRESENT; nuclear@17: } else { nuclear@17: pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK); nuclear@17: } nuclear@17: nuclear@17: pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT; nuclear@17: } nuclear@17: nuclear@17: void unmap_page(int vpage) nuclear@17: { nuclear@17: uint32_t *pgtbl; nuclear@17: int diridx = PAGE_TO_PGTBL(vpage); nuclear@17: int pgidx = PAGE_TO_PGTBL_PG(vpage); nuclear@17: nuclear@17: if(!(pgdir[diridx] & PG_PRESENT)) { nuclear@17: goto err; nuclear@17: } nuclear@17: pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK); nuclear@17: nuclear@17: if(!(pgtbl[pgidx] & PG_PRESENT)) { nuclear@17: goto err; nuclear@17: } nuclear@17: pgtbl[pgidx] = 0; nuclear@17: nuclear@17: return; nuclear@17: err: nuclear@17: printf("unmap_page(%d): page already not mapped\n", vpage); nuclear@17: } nuclear@17: nuclear@22: /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */ nuclear@17: void map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr) nuclear@17: { nuclear@17: int i; nuclear@17: nuclear@17: for(i=0; iend - node->start >= num) { nuclear@22: ret = node->start; nuclear@22: node->start += num; nuclear@22: nuclear@22: if(node->start == node->end) { nuclear@22: prev->next = node->next; nuclear@22: node->next = 0; nuclear@22: nuclear@22: if(node == pglist[area]) { nuclear@22: pglist[area] = 0; nuclear@22: } nuclear@22: free_node(node); nuclear@22: } nuclear@22: break; nuclear@22: } nuclear@22: nuclear@22: prev = node; nuclear@22: node = node->next; nuclear@22: } nuclear@22: nuclear@22: if(ret >= 0) { nuclear@22: /* allocate physical storage and map them */ nuclear@22: map_page_range(ret, num, -1, 0); nuclear@22: } nuclear@22: nuclear@22: return ret; nuclear@22: } nuclear@22: nuclear@22: void pgfree(int start, int num) nuclear@22: { nuclear@22: /* TODO */ nuclear@22: } nuclear@22: nuclear@17: static void pgfault(int inum, uint32_t err) nuclear@17: { nuclear@17: printf("~~~~ PAGE FAULT ~~~~\n"); nuclear@17: nuclear@17: printf("fault address: %x\n", get_fault_addr()); nuclear@17: nuclear@17: if(err & PG_PRESENT) { nuclear@17: if(err & 8) { nuclear@17: printf("reserved bit set in some paging structure\n"); nuclear@17: } else { nuclear@17: printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read"); nuclear@17: printf("in %s mode\n", err & PG_USER ? "user" : "kernel"); nuclear@17: } nuclear@17: } else { nuclear@17: printf("page not present\n"); nuclear@17: } nuclear@19: nuclear@19: panic("unhandled page fault\n"); nuclear@17: } nuclear@22: nuclear@22: /* --- page range list node management --- */ nuclear@22: static struct page_range *alloc_node(void) nuclear@22: { nuclear@22: struct page_range *node; nuclear@22: uint32_t paddr; nuclear@22: nuclear@22: if(node_pool) { nuclear@22: node = node_pool; nuclear@22: node_pool = node_pool->next; nuclear@22: return node; nuclear@22: } nuclear@22: nuclear@22: /* no node structures in the pool, we need to allocate and map nuclear@22: * a page, split it up into node structures, add them in the pool nuclear@22: * and allocate one of them. nuclear@22: */ nuclear@22: if(!(paddr = alloc_phys_page())) { nuclear@22: panic("ran out of physical memory while allocating VM range structures\n"); nuclear@22: } nuclear@22: nuclear@22: /* TODO cont. */ nuclear@22: return 0; nuclear@22: } nuclear@22: nuclear@22: static void free_node(struct page_range *node) nuclear@22: { nuclear@22: node->next = node_pool; nuclear@22: node_pool = node; nuclear@22: }