kern

annotate src/vm.c @ 23:5454cee245a3

- fixed tragic mistake in the initial kernel image mapping - page table modifications by disabling paging first - page allocation completed
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 04 Apr 2011 23:34:06 +0300
parents 7ece008f09c5
children 53588744382c
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@23 28 void disable_paging(void);
nuclear@23 29 int get_paging_status(void);
nuclear@22 30 void set_pgdir_addr(uint32_t addr);
nuclear@23 31 void flush_tlb(void);
nuclear@23 32 void flush_tlb_addr(uint32_t addr);
nuclear@23 33 #define flush_tlb_page(p) flush_tlb_addr(PAGE_TO_ADDR(p))
nuclear@22 34 uint32_t get_fault_addr(void);
nuclear@22 35
nuclear@23 36 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high);
nuclear@22 37 static void pgfault(int inum, uint32_t err);
nuclear@22 38 static struct page_range *alloc_node(void);
nuclear@22 39 static void free_node(struct page_range *node);
nuclear@22 40
nuclear@22 41 /* page directory */
nuclear@22 42 static uint32_t *pgdir;
nuclear@22 43
nuclear@22 44 /* 2 lists of free ranges, for kernel memory and user memory */
nuclear@22 45 static struct page_range *pglist[2];
nuclear@22 46 /* list of free page_range structures to be used in the lists */
nuclear@22 47 static struct page_range *node_pool;
nuclear@23 48 /* the first page range for the whole kernel address space, to get things started */
nuclear@23 49 static struct page_range first_node;
nuclear@22 50
nuclear@22 51
nuclear@17 52 void init_vm(struct mboot_info *mb)
nuclear@17 53 {
nuclear@19 54 uint32_t idmap_end;
nuclear@19 55
nuclear@23 56 /* initialize the physical memory map and allocator */
nuclear@17 57 init_mem(mb);
nuclear@17 58
nuclear@23 59 /* setup the page tables */
nuclear@18 60 pgdir = (uint32_t*)alloc_phys_page();
nuclear@23 61 memset(pgdir, 0, PGSIZE);
nuclear@23 62 set_pgdir_addr((int32_t)pgdir);
nuclear@17 63
nuclear@17 64 /* map the video memory and kernel code 1-1 */
nuclear@19 65 get_kernel_mem_range(0, &idmap_end);
nuclear@19 66 map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0);
nuclear@17 67
nuclear@23 68 /* set the page fault handler */
nuclear@17 69 interrupt(PAGEFAULT, pgfault);
nuclear@17 70
nuclear@23 71 /* we can enable paging now */
nuclear@17 72 enable_paging();
nuclear@23 73
nuclear@23 74 /* initialize the virtual page allocator */
nuclear@23 75 node_pool = 0;
nuclear@23 76
nuclear@23 77 first_node.start = ADDR_TO_PAGE(KMEM_START);
nuclear@23 78 first_node.end = PAGE_COUNT;
nuclear@23 79 first_node.next = 0;
nuclear@23 80 pglist[MEM_KERNEL] = &first_node;
nuclear@23 81
nuclear@23 82 pglist[MEM_USER] = alloc_node();
nuclear@23 83 pglist[MEM_USER]->start = 0;
nuclear@23 84 pglist[MEM_USER]->end = ADDR_TO_PAGE(KMEM_START);
nuclear@23 85 pglist[MEM_USER]->next = 0;
nuclear@17 86 }
nuclear@17 87
nuclear@23 88 /* if ppage == -1 we allocate a physical page by calling alloc_phys_page */
nuclear@23 89 int map_page(int vpage, int ppage, unsigned int attr)
nuclear@17 90 {
nuclear@17 91 uint32_t *pgtbl;
nuclear@23 92 int diridx, pgidx, pgon;
nuclear@23 93
nuclear@23 94 pgon = get_paging_status();
nuclear@23 95 disable_paging();
nuclear@23 96
nuclear@23 97 if(ppage < 0) {
nuclear@23 98 uint32_t addr = alloc_phys_page();
nuclear@23 99 if(!addr) {
nuclear@23 100 return -1;
nuclear@23 101 }
nuclear@23 102 ppage = ADDR_TO_PAGE(addr);
nuclear@23 103 }
nuclear@23 104
nuclear@23 105 diridx = PAGE_TO_PGTBL(vpage);
nuclear@23 106 pgidx = PAGE_TO_PGTBL_PG(vpage);
nuclear@17 107
nuclear@17 108 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@17 109 uint32_t addr = alloc_phys_page();
nuclear@17 110 pgtbl = (uint32_t*)addr;
nuclear@18 111 memset(pgtbl, 0, PGSIZE);
nuclear@18 112
nuclear@17 113 pgdir[diridx] = addr | (attr & ATTR_PGDIR_MASK) | PG_PRESENT;
nuclear@17 114 } else {
nuclear@17 115 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
nuclear@17 116 }
nuclear@17 117
nuclear@17 118 pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT;
nuclear@23 119 flush_tlb_page(vpage);
nuclear@23 120
nuclear@23 121 if(pgon) {
nuclear@23 122 enable_paging();
nuclear@23 123 }
nuclear@23 124 return 0;
nuclear@17 125 }
nuclear@17 126
nuclear@17 127 void unmap_page(int vpage)
nuclear@17 128 {
nuclear@17 129 uint32_t *pgtbl;
nuclear@17 130 int diridx = PAGE_TO_PGTBL(vpage);
nuclear@17 131 int pgidx = PAGE_TO_PGTBL_PG(vpage);
nuclear@17 132
nuclear@17 133 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@17 134 goto err;
nuclear@17 135 }
nuclear@17 136 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
nuclear@17 137
nuclear@17 138 if(!(pgtbl[pgidx] & PG_PRESENT)) {
nuclear@17 139 goto err;
nuclear@17 140 }
nuclear@17 141 pgtbl[pgidx] = 0;
nuclear@23 142 flush_tlb_page(vpage);
nuclear@17 143
nuclear@17 144 return;
nuclear@17 145 err:
nuclear@17 146 printf("unmap_page(%d): page already not mapped\n", vpage);
nuclear@17 147 }
nuclear@17 148
nuclear@22 149 /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */
nuclear@23 150 int map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr)
nuclear@17 151 {
nuclear@23 152 int i, phys_pg;
nuclear@23 153 uint32_t paddr;
nuclear@17 154
nuclear@17 155 for(i=0; i<pgcount; i++) {
nuclear@23 156 if(ppg_start < 0) {
nuclear@23 157 if(!(paddr = alloc_phys_page())) {
nuclear@23 158 return -1;
nuclear@23 159 }
nuclear@23 160 phys_pg = ADDR_TO_PAGE(paddr);
nuclear@23 161 } else {
nuclear@23 162 phys_pg = ppg_start + i;
nuclear@23 163 }
nuclear@22 164
nuclear@23 165 map_page(vpg_start + i, phys_pg, attr);
nuclear@17 166 }
nuclear@23 167 return 0;
nuclear@17 168 }
nuclear@17 169
nuclear@23 170 /* if paddr is 0, we allocate physical pages with alloc_phys_page() */
nuclear@23 171 int map_mem_range(uint32_t vaddr, size_t sz, uint32_t paddr, unsigned int attr)
nuclear@17 172 {
nuclear@17 173 int vpg_start, ppg_start, num_pages;
nuclear@17 174
nuclear@23 175 if(!sz) return -1;
nuclear@17 176
nuclear@17 177 if(ADDR_TO_PGOFFS(paddr)) {
nuclear@17 178 panic("map_mem_range called with unaligned physical address: %x\n", paddr);
nuclear@17 179 }
nuclear@17 180
nuclear@17 181 vpg_start = ADDR_TO_PAGE(vaddr);
nuclear@23 182 ppg_start = paddr > 0 ? ADDR_TO_PAGE(paddr) : -1;
nuclear@17 183 num_pages = ADDR_TO_PAGE(sz) + 1;
nuclear@17 184
nuclear@23 185 return map_page_range(vpg_start, num_pages, ppg_start, attr);
nuclear@17 186 }
nuclear@17 187
nuclear@18 188 uint32_t virt_to_phys(uint32_t vaddr)
nuclear@18 189 {
nuclear@18 190 uint32_t pgaddr, *pgtbl;
nuclear@18 191 int diridx = ADDR_TO_PGTBL(vaddr);
nuclear@18 192 int pgidx = ADDR_TO_PGTBL_PG(vaddr);
nuclear@18 193
nuclear@18 194 if(!(pgdir[diridx] & PG_PRESENT)) {
nuclear@18 195 panic("virt_to_phys(%x): page table %d not present\n", vaddr, diridx);
nuclear@18 196 }
nuclear@18 197 pgtbl = (uint32_t*)(pgdir[diridx] & PGENT_ADDR_MASK);
nuclear@18 198
nuclear@18 199 if(!(pgtbl[pgidx] & PG_PRESENT)) {
nuclear@18 200 panic("virt_to_phys(%x): page %d not present\n", vaddr, ADDR_TO_PAGE(vaddr));
nuclear@18 201 }
nuclear@18 202 pgaddr = pgtbl[pgidx] & PGENT_ADDR_MASK;
nuclear@18 203
nuclear@18 204 return pgaddr | ADDR_TO_PGOFFS(vaddr);
nuclear@18 205 }
nuclear@18 206
nuclear@22 207 /* allocate a contiguous block of virtual memory pages along with
nuclear@22 208 * backing physical memory for them, and update the page table.
nuclear@22 209 */
nuclear@22 210 int pgalloc(int num, int area)
nuclear@22 211 {
nuclear@22 212 int ret = -1;
nuclear@22 213 struct page_range *node, *prev, dummy;
nuclear@22 214
nuclear@22 215 dummy.next = pglist[area];
nuclear@22 216 node = pglist[area];
nuclear@22 217 prev = &dummy;
nuclear@22 218
nuclear@22 219 while(node) {
nuclear@22 220 if(node->end - node->start >= num) {
nuclear@22 221 ret = node->start;
nuclear@22 222 node->start += num;
nuclear@22 223
nuclear@22 224 if(node->start == node->end) {
nuclear@22 225 prev->next = node->next;
nuclear@22 226 node->next = 0;
nuclear@22 227
nuclear@22 228 if(node == pglist[area]) {
nuclear@22 229 pglist[area] = 0;
nuclear@22 230 }
nuclear@22 231 free_node(node);
nuclear@22 232 }
nuclear@22 233 break;
nuclear@22 234 }
nuclear@22 235
nuclear@22 236 prev = node;
nuclear@22 237 node = node->next;
nuclear@22 238 }
nuclear@22 239
nuclear@22 240 if(ret >= 0) {
nuclear@23 241 /* allocate physical storage and map */
nuclear@23 242 if(map_page_range(ret, num, -1, 0) == -1) {
nuclear@23 243 ret = -1;
nuclear@23 244 }
nuclear@22 245 }
nuclear@22 246
nuclear@22 247 return ret;
nuclear@22 248 }
nuclear@22 249
nuclear@22 250 void pgfree(int start, int num)
nuclear@22 251 {
nuclear@23 252 int area, end;
nuclear@23 253 struct page_range *node, *new, *prev, *next;
nuclear@23 254
nuclear@23 255 if(!(new = alloc_node())) {
nuclear@23 256 panic("pgfree: can't allocate new page_range node to add the freed pages\n");
nuclear@23 257 }
nuclear@23 258 new->start = start;
nuclear@23 259 end = new->end = start + num;
nuclear@23 260
nuclear@23 261 area = PAGE_TO_ADDR(start) >= KMEM_START ? MEM_KERNEL : MEM_USER;
nuclear@23 262
nuclear@23 263 if(!pglist[area] || pglist[area]->start > start) {
nuclear@23 264 next = new->next = pglist[area];
nuclear@23 265 pglist[area] = new;
nuclear@23 266 prev = 0;
nuclear@23 267
nuclear@23 268 } else {
nuclear@23 269
nuclear@23 270 prev = 0;
nuclear@23 271 node = pglist[area];
nuclear@23 272 next = node ? node->next : 0;
nuclear@23 273
nuclear@23 274 while(node) {
nuclear@23 275 if(!next || next->start > start) {
nuclear@23 276 /* place here, after node */
nuclear@23 277 new->next = next;
nuclear@23 278 node->next = new;
nuclear@23 279 prev = node; /* needed by coalesce after the loop */
nuclear@23 280 break;
nuclear@23 281 }
nuclear@23 282
nuclear@23 283 prev = node;
nuclear@23 284 node = next;
nuclear@23 285 next = node ? node->next : 0;
nuclear@23 286 }
nuclear@23 287 }
nuclear@23 288
nuclear@23 289 coalesce(prev, new, next);
nuclear@23 290 }
nuclear@23 291
nuclear@23 292 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high)
nuclear@23 293 {
nuclear@23 294 if(high) {
nuclear@23 295 if(mid->end == high->start) {
nuclear@23 296 mid->end = high->end;
nuclear@23 297 mid->next = high->next;
nuclear@23 298 free_node(high);
nuclear@23 299 }
nuclear@23 300 }
nuclear@23 301
nuclear@23 302 if(low) {
nuclear@23 303 if(low->end == mid->start) {
nuclear@23 304 low->end += mid->end;
nuclear@23 305 low->next = mid->next;
nuclear@23 306 free_node(mid);
nuclear@23 307 }
nuclear@23 308 }
nuclear@22 309 }
nuclear@22 310
nuclear@17 311 static void pgfault(int inum, uint32_t err)
nuclear@17 312 {
nuclear@17 313 printf("~~~~ PAGE FAULT ~~~~\n");
nuclear@17 314
nuclear@17 315 printf("fault address: %x\n", get_fault_addr());
nuclear@17 316
nuclear@17 317 if(err & PG_PRESENT) {
nuclear@17 318 if(err & 8) {
nuclear@17 319 printf("reserved bit set in some paging structure\n");
nuclear@17 320 } else {
nuclear@17 321 printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read");
nuclear@17 322 printf("in %s mode\n", err & PG_USER ? "user" : "kernel");
nuclear@17 323 }
nuclear@17 324 } else {
nuclear@17 325 printf("page not present\n");
nuclear@17 326 }
nuclear@19 327
nuclear@19 328 panic("unhandled page fault\n");
nuclear@17 329 }
nuclear@22 330
nuclear@22 331 /* --- page range list node management --- */
nuclear@23 332 #define NODES_IN_PAGE (PGSIZE / sizeof(struct page_range))
nuclear@23 333
nuclear@22 334 static struct page_range *alloc_node(void)
nuclear@22 335 {
nuclear@22 336 struct page_range *node;
nuclear@23 337 int pg, i;
nuclear@22 338
nuclear@22 339 if(node_pool) {
nuclear@22 340 node = node_pool;
nuclear@22 341 node_pool = node_pool->next;
nuclear@23 342 printf("alloc_node -> %x\n", (unsigned int)node);
nuclear@22 343 return node;
nuclear@22 344 }
nuclear@22 345
nuclear@23 346 /* no node structures in the pool, we need to allocate a new page,
nuclear@23 347 * split it up into node structures, add them in the pool, and
nuclear@23 348 * allocate one of them.
nuclear@22 349 */
nuclear@23 350 if(!(pg = pgalloc(1, MEM_KERNEL))) {
nuclear@22 351 panic("ran out of physical memory while allocating VM range structures\n");
nuclear@22 352 }
nuclear@23 353 node_pool = (struct page_range*)PAGE_TO_ADDR(pg);
nuclear@22 354
nuclear@23 355 /* link them up, skip the first as we'll just allocate it anyway */
nuclear@23 356 for(i=2; i<NODES_IN_PAGE; i++) {
nuclear@23 357 node_pool[i - 1].next = node_pool + i;
nuclear@23 358 }
nuclear@23 359 node_pool[NODES_IN_PAGE - 1].next = 0;
nuclear@23 360
nuclear@23 361 /* grab the first and return it */
nuclear@23 362 node = node_pool++;
nuclear@23 363 printf("alloc_node -> %x\n", (unsigned int)node);
nuclear@23 364 return node;
nuclear@22 365 }
nuclear@22 366
nuclear@22 367 static void free_node(struct page_range *node)
nuclear@22 368 {
nuclear@22 369 node->next = node_pool;
nuclear@22 370 node_pool = node;
nuclear@23 371 printf("free_node\n");
nuclear@22 372 }
nuclear@23 373
nuclear@23 374
nuclear@23 375 void dbg_print_vm(int area)
nuclear@23 376 {
nuclear@23 377 struct page_range *node = pglist[area];
nuclear@23 378 int last = area == MEM_USER ? 0 : ADDR_TO_PAGE(KMEM_START);
nuclear@23 379
nuclear@23 380 printf("%s vm space\n", area == MEM_USER ? "user" : "kernel");
nuclear@23 381
nuclear@23 382 while(node) {
nuclear@23 383 if(node->start > last) {
nuclear@23 384 printf(" vm-used: %x -> %x\n", PAGE_TO_ADDR(last), PAGE_TO_ADDR(node->start));
nuclear@23 385 }
nuclear@23 386
nuclear@23 387 printf(" vm-free: %x -> ", PAGE_TO_ADDR(node->start));
nuclear@23 388 if(node->end >= PAGE_COUNT) {
nuclear@23 389 printf("END\n");
nuclear@23 390 } else {
nuclear@23 391 printf("%x\n", PAGE_TO_ADDR(node->end));
nuclear@23 392 }
nuclear@23 393
nuclear@23 394 last = node->end;
nuclear@23 395 node = node->next;
nuclear@23 396 }
nuclear@23 397 }