kern

annotate src/vm.c @ 22:7ece008f09c5

writing the vm
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 03 Apr 2011 18:42:19 +0300
parents 8be069e6bb05
children 5454cee245a3
rev   line source
nuclear@17 1 #include <stdio.h>
nuclear@17 2 #include <string.h>
nuclear@17 3 #include <inttypes.h>
nuclear@17 4 #include "vm.h"
nuclear@17 5 #include <stdio.h>
nuclear@17 6 #include "intr.h"
nuclear@17 7 #include "mem.h"
nuclear@17 8 #include "panic.h"
nuclear@17 9
nuclear@17 10
nuclear@17 11 #define KMEM_START 0xc0000000
nuclear@17 12 #define IDMAP_START 0xa0000
nuclear@17 13
nuclear@17 14 #define ATTR_PGDIR_MASK 0x3f
nuclear@17 15 #define ATTR_PGTBL_MASK 0x1ff
nuclear@17 16 #define ADDR_PGENT_MASK 0xfffff000
nuclear@17 17
nuclear@17 18 #define PAGEFAULT 14
nuclear@17 19
nuclear@22 20
nuclear@22 21 struct page_range {
nuclear@22 22 int start, end;
nuclear@22 23 struct page_range *next;
nuclear@22 24 };
nuclear@22 25
nuclear@22 26 /* defined in vm-asm.S */
nuclear@22 27 void enable_paging(void);
nuclear@22 28 void set_pgdir_addr(uint32_t addr);
nuclear@22 29 uint32_t get_fault_addr(void);
nuclear@22 30
nuclear@22 31 static void pgfault(int inum, uint32_t err);
nuclear@22 32 static struct page_range *alloc_node(void);
nuclear@22 33 static void free_node(struct page_range *node);
nuclear@22 34
nuclear@22 35 /* page directory */
nuclear@22 36 static uint32_t *pgdir;
nuclear@22 37
nuclear@22 38 /* 2 lists of free ranges, for kernel memory and user memory */
nuclear@22 39 static struct page_range *pglist[2];
nuclear@22 40 /* list of free page_range structures to be used in the lists */
nuclear@22 41 static struct page_range *node_pool;
nuclear@22 42
nuclear@22 43
nuclear@17 44 void init_vm(struct mboot_info *mb)
nuclear@17 45 {
nuclear@19 46 uint32_t idmap_end;
nuclear@19 47
nuclear@17 48 init_mem(mb);
nuclear@17 49
nuclear@18 50 pgdir = (uint32_t*)alloc_phys_page();
nuclear@17 51 memset(pgdir, 0, sizeof pgdir);
nuclear@17 52
nuclear@17 53 /* map the video memory and kernel code 1-1 */
nuclear@19 54 get_kernel_mem_range(0, &idmap_end);
nuclear@19 55 map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0);
nuclear@17 56
nuclear@17 57 interrupt(PAGEFAULT, pgfault);
nuclear@17 58
nuclear@17 59 set_pgdir_addr((int32_t)pgdir);
nuclear@17 60 enable_paging();
nuclear@17 61 }
nuclear@17 62
nuclear@17 63 void map_page(int vpage, int ppage, unsigned int attr)
nuclear@17 64 {
nuclear@17 65 uint32_t *pgtbl;
nuclear@17 66 int diridx = PAGE_TO_PGTBL(vpage);
nuclear@17 67 int pgidx = PAGE_TO_PGTBL_PG(vpage);
nuclear@17 68
nuclear@17 69 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@17 70 uint32_t addr = alloc_phys_page();
nuclear@17 71 pgtbl = (uint32_t*)addr;
nuclear@18 72 memset(pgtbl, 0, PGSIZE);
nuclear@18 73
nuclear@17 74 pgdir[diridx] = addr | (attr & ATTR_PGDIR_MASK) | PG_PRESENT;
nuclear@17 75 } else {
nuclear@17 76 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
nuclear@17 77 }
nuclear@17 78
nuclear@17 79 pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT;
nuclear@17 80 }
nuclear@17 81
nuclear@17 82 void unmap_page(int vpage)
nuclear@17 83 {
nuclear@17 84 uint32_t *pgtbl;
nuclear@17 85 int diridx = PAGE_TO_PGTBL(vpage);
nuclear@17 86 int pgidx = PAGE_TO_PGTBL_PG(vpage);
nuclear@17 87
nuclear@17 88 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@17 89 goto err;
nuclear@17 90 }
nuclear@17 91 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
nuclear@17 92
nuclear@17 93 if(!(pgtbl[pgidx] & PG_PRESENT)) {
nuclear@17 94 goto err;
nuclear@17 95 }
nuclear@17 96 pgtbl[pgidx] = 0;
nuclear@17 97
nuclear@17 98 return;
nuclear@17 99 err:
nuclear@17 100 printf("unmap_page(%d): page already not mapped\n", vpage);
nuclear@17 101 }
nuclear@17 102
nuclear@22 103 /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */
nuclear@17 104 void map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr)
nuclear@17 105 {
nuclear@17 106 int i;
nuclear@17 107
nuclear@17 108 for(i=0; i<pgcount; i++) {
nuclear@22 109 uint32_t paddr = ppg_start == -1 ? alloc_phys_page() : ppg_start + i;
nuclear@22 110
nuclear@22 111 map_page(vpg_start + i, paddr, attr);
nuclear@17 112 }
nuclear@17 113 }
nuclear@17 114
nuclear@17 115 void map_mem_range(uint32_t vaddr, size_t sz, uint32_t paddr, unsigned int attr)
nuclear@17 116 {
nuclear@17 117 int vpg_start, ppg_start, num_pages;
nuclear@17 118
nuclear@17 119 if(!sz) return;
nuclear@17 120
nuclear@17 121 if(ADDR_TO_PGOFFS(paddr)) {
nuclear@17 122 panic("map_mem_range called with unaligned physical address: %x\n", paddr);
nuclear@17 123 }
nuclear@17 124
nuclear@17 125 vpg_start = ADDR_TO_PAGE(vaddr);
nuclear@17 126 ppg_start = ADDR_TO_PAGE(paddr);
nuclear@17 127 num_pages = ADDR_TO_PAGE(sz) + 1;
nuclear@17 128
nuclear@17 129 map_page_range(vpg_start, num_pages, ppg_start, attr);
nuclear@17 130 }
nuclear@17 131
nuclear@18 132 uint32_t virt_to_phys(uint32_t vaddr)
nuclear@18 133 {
nuclear@18 134 uint32_t pgaddr, *pgtbl;
nuclear@18 135 int diridx = ADDR_TO_PGTBL(vaddr);
nuclear@18 136 int pgidx = ADDR_TO_PGTBL_PG(vaddr);
nuclear@18 137
nuclear@18 138 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@18 139 panic("virt_to_phys(%x): page table %d not present\n", vaddr, diridx);
nuclear@18 140 }
nuclear@18 141 pgtbl = (uint32_t*)(pgdir[diridx] & PGENT_ADDR_MASK);
nuclear@18 142
nuclear@18 143 if(!(pgtbl[pgidx] & PG_PRESENT)) {
nuclear@18 144 panic("virt_to_phys(%x): page %d not present\n", vaddr, ADDR_TO_PAGE(vaddr));
nuclear@18 145 }
nuclear@18 146 pgaddr = pgtbl[pgidx] & PGENT_ADDR_MASK;
nuclear@18 147
nuclear@18 148 return pgaddr | ADDR_TO_PGOFFS(vaddr);
nuclear@18 149 }
nuclear@18 150
nuclear@22 151 /* allocate a contiguous block of virtual memory pages along with
nuclear@22 152 * backing physical memory for them, and update the page table.
nuclear@22 153 */
nuclear@22 154 int pgalloc(int num, int area)
nuclear@22 155 {
nuclear@22 156 int ret = -1;
nuclear@22 157 struct page_range *node, *prev, dummy;
nuclear@22 158
nuclear@22 159 dummy.next = pglist[area];
nuclear@22 160 node = pglist[area];
nuclear@22 161 prev = &dummy;
nuclear@22 162
nuclear@22 163 while(node) {
nuclear@22 164 if(node->end - node->start >= num) {
nuclear@22 165 ret = node->start;
nuclear@22 166 node->start += num;
nuclear@22 167
nuclear@22 168 if(node->start == node->end) {
nuclear@22 169 prev->next = node->next;
nuclear@22 170 node->next = 0;
nuclear@22 171
nuclear@22 172 if(node == pglist[area]) {
nuclear@22 173 pglist[area] = 0;
nuclear@22 174 }
nuclear@22 175 free_node(node);
nuclear@22 176 }
nuclear@22 177 break;
nuclear@22 178 }
nuclear@22 179
nuclear@22 180 prev = node;
nuclear@22 181 node = node->next;
nuclear@22 182 }
nuclear@22 183
nuclear@22 184 if(ret >= 0) {
nuclear@22 185 /* allocate physical storage and map them */
nuclear@22 186 map_page_range(ret, num, -1, 0);
nuclear@22 187 }
nuclear@22 188
nuclear@22 189 return ret;
nuclear@22 190 }
nuclear@22 191
nuclear@22 192 void pgfree(int start, int num)
nuclear@22 193 {
nuclear@22 194 /* TODO */
nuclear@22 195 }
nuclear@22 196
nuclear@17 197 static void pgfault(int inum, uint32_t err)
nuclear@17 198 {
nuclear@17 199 printf("~~~~ PAGE FAULT ~~~~\n");
nuclear@17 200
nuclear@17 201 printf("fault address: %x\n", get_fault_addr());
nuclear@17 202
nuclear@17 203 if(err & PG_PRESENT) {
nuclear@17 204 if(err & 8) {
nuclear@17 205 printf("reserved bit set in some paging structure\n");
nuclear@17 206 } else {
nuclear@17 207 printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read");
nuclear@17 208 printf("in %s mode\n", err & PG_USER ? "user" : "kernel");
nuclear@17 209 }
nuclear@17 210 } else {
nuclear@17 211 printf("page not present\n");
nuclear@17 212 }
nuclear@19 213
nuclear@19 214 panic("unhandled page fault\n");
nuclear@17 215 }
nuclear@22 216
nuclear@22 217 /* --- page range list node management --- */
nuclear@22 218 static struct page_range *alloc_node(void)
nuclear@22 219 {
nuclear@22 220 struct page_range *node;
nuclear@22 221 uint32_t paddr;
nuclear@22 222
nuclear@22 223 if(node_pool) {
nuclear@22 224 node = node_pool;
nuclear@22 225 node_pool = node_pool->next;
nuclear@22 226 return node;
nuclear@22 227 }
nuclear@22 228
nuclear@22 229 /* no node structures in the pool, we need to allocate and map
nuclear@22 230 * a page, split it up into node structures, add them in the pool
nuclear@22 231 * and allocate one of them.
nuclear@22 232 */
nuclear@22 233 if(!(paddr = alloc_phys_page())) {
nuclear@22 234 panic("ran out of physical memory while allocating VM range structures\n");
nuclear@22 235 }
nuclear@22 236
nuclear@22 237 /* TODO cont. */
nuclear@22 238 return 0;
nuclear@22 239 }
nuclear@22 240
nuclear@22 241 static void free_node(struct page_range *node)
nuclear@22 242 {
nuclear@22 243 node->next = node_pool;
nuclear@22 244 node_pool = node;
nuclear@22 245 }