fix a bug in the elf loader/mmu stuff in the bootloader that would cause
[newos.git] / boot / ppc / stage2_mmu.c
blobbb19a698b32d9ebf26bc825343b6c4eaa70431e7
1 /*
2 ** Copyright 2001, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <boot/stage2.h>
6 #include <boot/shared/openfirmware.h>
7 #include <kernel/kernel.h>
8 #include <arch/cpu.h>
9 #include <libc/string.h>
10 #include "stage2_priv.h"
12 static unsigned int primary_hash(unsigned int vsid, unsigned int vaddr);
13 static unsigned int secondary_hash(unsigned int primary_hash);
15 static struct ppc_pteg *ptable = 0;
16 static int ptable_size = 0;
17 static unsigned int ptable_hash_mask = 0;
20 static unsigned long total_ram_size = 0;
22 static void print_pte(struct ppc_pte *e);
24 static bool does_intersect(unsigned long base1, unsigned long len1, unsigned long base2, unsigned long len2)
26 unsigned long end1 = base1 + len1;
27 unsigned long end2 = base2 + len2;
29 if(base2 >= base1 && base2 <= end1)
30 return true; // base2 is inside first range
31 if(end2 >= base1 && end2 <= end1)
32 return true; // end of second range inside first range
33 if(base1 >= base2 && base1 <= end2)
34 return true; // base1 is inside second range
35 if(end1 >= base2 && end1 <= end2)
36 return true; // end of first range inside second range
38 return false;
41 static void find_phys_memory_map(kernel_args *ka)
43 int handle;
44 unsigned int i;
45 struct mem_region {
46 unsigned long pa;
47 int len;
48 } mem_regions[32];
49 unsigned int mem_regions_len = 0;
51 // get the physical memory map of the system
52 handle = of_finddevice("/chosen");
53 of_getprop(handle, "memory", &handle, sizeof(handle));
54 handle = of_instance_to_package(handle);
55 memset(mem_regions, 0, sizeof(mem_regions));
56 mem_regions_len = of_getprop(handle, "reg", mem_regions, sizeof(mem_regions));
57 mem_regions_len /= sizeof(struct mem_region);
59 printf("num mem regions %d\n", mem_regions_len);
61 // copy these regions over to the kernel args structure
62 ka->num_phys_mem_ranges = 0;
63 for(i=0; i<mem_regions_len; i++) {
64 if(mem_regions[i].len > 0) {
65 total_ram_size += mem_regions[i].len;
66 if(ka->num_phys_mem_ranges > 0) {
67 if(mem_regions[i].pa == ka->phys_mem_range[ka->num_phys_mem_ranges-1].start + ka->phys_mem_range[ka->num_phys_mem_ranges-1].size) {
68 // this range just extends the old one
69 ka->phys_mem_range[ka->num_phys_mem_ranges-1].size += mem_regions[i].len;
70 continue;
73 ka->phys_mem_range[ka->num_phys_mem_ranges].start = mem_regions[i].pa;
74 ka->phys_mem_range[ka->num_phys_mem_ranges].size = mem_regions[i].len;
75 ka->num_phys_mem_ranges++;
76 if(ka->num_phys_mem_ranges == MAX_PHYS_MEM_ADDR_RANGE) {
77 printf("too many physical memory maps, increase MAX_PHYS_MEM_ADDR_RANGE\n");
78 for(;;);
82 printf("num phys mem regions %d\n", ka->num_phys_mem_ranges);
83 for(i=0; i<ka->num_phys_mem_ranges; i++) {
84 printf(" phys map %d: pa 0x%lx, len 0x%lx\n", i, ka->phys_mem_range[i].start, ka->phys_mem_range[i].size);
88 static bool is_in_phys_mem(kernel_args *ka, unsigned long addr)
90 unsigned int i;
92 for(i = 0; i < ka->num_phys_mem_ranges; i++) {
93 if(does_intersect(ka->phys_mem_range[i].start, ka->phys_mem_range[i].size, addr, 0))
94 return true;
96 return false;
99 static void mark_used_phys_mem_range(kernel_args *ka, unsigned long base, unsigned long len)
101 unsigned int i;
102 unsigned long start;
104 base = ROUNDOWN(base, PAGE_SIZE);
105 len = ROUNDUP(len, PAGE_SIZE);
106 start = base;
108 while(start < base + len){
109 // cycle through the list of physical runs of used pages,
110 // seeing if start will intersect one of them
111 for(i = 0; i < ka->num_phys_alloc_ranges; i++) {
112 if(start == ka->phys_alloc_range[i].start + ka->phys_alloc_range[i].size) {
113 // it will extend it
114 ka->phys_alloc_range[i].size += PAGE_SIZE;
115 goto next_page;
117 if(start + PAGE_SIZE == ka->phys_alloc_range[i].start) {
118 // it will prepend it
119 ka->phys_alloc_range[i].start = start;
120 ka->phys_alloc_range[i].size += PAGE_SIZE;
121 goto next_page;
123 if(does_intersect(ka->phys_alloc_range[i].start, ka->phys_alloc_range[i].size, start, PAGE_SIZE)) {
124 // it's off in the middle of this range, skip it
125 goto next_page;
129 // didn't find it in one of the existing ranges, must need to start a new one
130 if(ka->num_phys_alloc_ranges >= MAX_PHYS_ALLOC_ADDR_RANGE) {
131 printf("mark_used_phys_mem_range: MAX_PHYS_ALLOC_ADDR_RANGE (%d) too small\n", MAX_PHYS_ALLOC_ADDR_RANGE);
132 for(;;);
135 // create a new allocated range
136 ka->phys_alloc_range[ka->num_phys_alloc_ranges].start = start;
137 ka->phys_alloc_range[ka->num_phys_alloc_ranges].size = PAGE_SIZE;
138 ka->num_phys_alloc_ranges++;
140 next_page:
141 start += PAGE_SIZE;
145 static void find_used_phys_memory_map(kernel_args *ka)
147 int handle;
148 unsigned int i;
149 struct translation_map {
150 unsigned long va;
151 int len;
152 unsigned long pa;
153 int mode;
154 } memmap[64];
155 unsigned int translation_map_len = 0;
157 printf("looking for current translations...\n");
159 ka->num_phys_alloc_ranges = 0;
161 // get the current translation map of the system,
162 // to find how much memory was mapped to load the stage1 and bootdir
163 handle = of_finddevice("/chosen");
164 of_getprop(handle, "mmu", &handle, sizeof(handle));
165 handle = of_instance_to_package(handle);
166 memset(memmap, 0, sizeof(memmap));
167 translation_map_len = of_getprop(handle, "translations", memmap, sizeof(memmap));
168 translation_map_len /= sizeof(struct translation_map);
169 printf("found %d translations\n", translation_map_len);
170 for(i=0; i<translation_map_len; i++) {
171 printf("package loaded at pa 0x%lx va 0x%lx, len 0x%x\n", memmap[i].pa, memmap[i].va, memmap[i].len);
172 if(is_in_phys_mem(ka, memmap[i].va)) {
173 // we found the translation that covers the loaded package. Save this.
174 mark_used_phys_mem_range(ka, memmap[i].pa, memmap[i].len);
177 for(i=0; i<ka->num_phys_alloc_ranges; i++) {
178 printf("phys alloc map %d: pa 0x%lx, len 0x%lx\n", i, ka->phys_alloc_range[i].start, ka->phys_alloc_range[i].size);
182 static void mark_used_virt_mem_range(kernel_args *ka, unsigned long base, unsigned long len)
184 unsigned int i;
185 unsigned long start;
187 base = ROUNDOWN(base, PAGE_SIZE);
188 len = ROUNDUP(len, PAGE_SIZE);
189 start = base;
191 while(start < base + len) {
192 // cycle through the list of virtual runs of used pages,
193 // seeing if start will intersect one of them
194 for(i = 0; i < ka->num_virt_alloc_ranges; i++) {
195 if(start == ka->virt_alloc_range[i].start + ka->virt_alloc_range[i].size) {
196 // it will extend it
197 ka->virt_alloc_range[i].size += PAGE_SIZE;
198 goto next_page;
200 if(start + PAGE_SIZE == ka->virt_alloc_range[i].start) {
201 // it will prepend it
202 ka->virt_alloc_range[i].start = start;
203 ka->virt_alloc_range[i].size += PAGE_SIZE;
204 goto next_page;
206 if(does_intersect(ka->virt_alloc_range[i].start, ka->virt_alloc_range[i].size, start, PAGE_SIZE)) {
207 // it's off in the middle of this range, skip it
208 goto next_page;
212 // didn't find it in one of the existing ranges, must need to start a new one
213 if(ka->num_virt_alloc_ranges >= MAX_VIRT_ALLOC_ADDR_RANGE) {
214 printf("mark_used_virt_mem_range: MAX_VIRT_ALLOC_ADDR_RANGE (%d) too small\n", MAX_VIRT_ALLOC_ADDR_RANGE);
215 for(;;);
218 // create a new allocated range
219 ka->virt_alloc_range[ka->num_virt_alloc_ranges].start = start;
220 ka->virt_alloc_range[ka->num_virt_alloc_ranges].size = PAGE_SIZE;
221 ka->num_virt_alloc_ranges++;
223 next_page:
224 start += PAGE_SIZE;
228 unsigned long mmu_allocate_page(kernel_args *ka)
230 unsigned long page;
232 if(ka->num_phys_alloc_ranges == 0) {
233 // no physical allocated ranges, start one
234 page = ka->phys_mem_range[0].start;
235 mark_used_phys_mem_range(ka, page, PAGE_SIZE);
236 return page;
239 // allocate from the first allocated physical range
240 page = ka->phys_alloc_range[0].start + ka->phys_alloc_range[0].size;
241 ka->phys_alloc_range[0].size += PAGE_SIZE;
243 // XXX check for validity better
245 return page;
249 static void tlbia()
251 unsigned long i;
253 asm volatile("sync");
254 for(i=0; i< 0x40000; i += 0x1000) {
255 asm volatile("tlbie %0" :: "r" (i));
256 asm volatile("eieio");
257 asm volatile("sync");
259 asm volatile("tlbsync");
260 asm volatile("sync");
263 #define CACHELINE 32
265 void syncicache(void *address, int len)
267 int l, off;
268 char *p;
270 off = (unsigned int)address & (CACHELINE - 1);
271 len += off;
273 l = len;
274 p = (char *)address - off;
275 do {
276 asm volatile ("dcbst 0,%0" :: "r"(p));
277 p += CACHELINE;
278 } while((l -= CACHELINE) > 0);
279 asm volatile ("sync");
280 p = (char *)address - off;
281 do {
282 asm volatile ("icbi 0,%0" :: "r"(p));
283 p += CACHELINE;
284 } while((len -= CACHELINE) > 0);
285 asm volatile ("sync");
286 asm volatile ("isync");
289 int s2_mmu_init(kernel_args *ka)
291 unsigned int ibats[8];
292 unsigned int dbats[8];
293 unsigned long top_ram = 0;
294 int i;
296 ka->num_virt_alloc_ranges = 0;
298 // figure out where physical memory is and what is being used
299 find_phys_memory_map(ka);
300 find_used_phys_memory_map(ka);
302 #if 0
303 // find the largest address of physical memory, but with a max of 256 MB,
304 // so it'll be within our 256 MB BAT window
305 for(i=0; i<ka->num_phys_mem_ranges; i++) {
306 if(ka->phys_mem_range[i].start + ka->phys_mem_range[i].size > top_ram) {
307 if(ka->phys_mem_range[i].start + ka->phys_mem_range[i].size > 256*1024*1024) {
308 if(ka->phys_mem_range[i].start < 256*1024*1024) {
309 top_ram = 256*1024*1024;
310 break;
313 top_ram = ka->phys_mem_range[i].start + ka->phys_mem_range[i].size;
316 printf("top of ram (but under 256MB) is 0x%x\n", top_ram);
317 #endif
319 // figure the size of the new pagetable, as recommended by Motorola
320 if(total_ram_size <= 8*1024*1024) {
321 ptable_size = 64*1024;
322 } else if(total_ram_size <= 16*1024*1024) {
323 ptable_size = 128*1024;
324 } else if(total_ram_size <= 32*1024*1024) {
325 ptable_size = 256*1024;
326 } else if(total_ram_size <= 64*1024*1024) {
327 ptable_size = 512*1024;
328 } else if(total_ram_size <= 128*1024*1024) {
329 ptable_size = 1024*1024;
330 } else if(total_ram_size <= 256*1024*1024) {
331 ptable_size = 2*1024*1024;
332 } else if(total_ram_size <= 512*1024*1024) {
333 ptable_size = 4*1024*1024;
334 } else if(total_ram_size <= 1024*1024*1024) {
335 ptable_size = 8*1024*1024;
336 } else if(total_ram_size <= 2*1024*1024*1024UL) {
337 ptable_size = 16*1024*1024;
338 } else {
339 ptable_size = 32*1024*1024;
342 // look at the old page table
343 printf("old page table at 0x%x, size 0x%x\n", (addr_t)getsdr1() & 0xffff0000,
344 (((addr_t)getsdr1() & 0x1ff) + 1) << 16);
346 // figure out where to put the page table
347 printf("allocating a page table using claim\n");
348 ptable_hash_mask = (ptable_size >> 6) - 1;
349 ptable = (struct ppc_pteg *)of_claim(0, ptable_size, ptable_size);
350 printf("ptable at pa 0x%x, size 0x%x\n", ptable, ptable_size);
351 printf("mask = 0x%x\n", ptable_hash_mask);
353 // mark it used
354 mark_used_phys_mem_range(ka, (unsigned long)ptable, ptable_size);
356 // save it's new location in the kernel args
357 ka->arch_args.page_table.start = (unsigned long)ptable;
358 ka->arch_args.page_table.size = ptable_size;
359 ka->arch_args.page_table_mask = ptable_hash_mask;
361 #if 0
363 struct ppc_pteg *old_ptable;
364 int j;
366 printf("sdr1 = 0x%x\n", getsdr1());
368 old_ptable = (struct ppc_pteg *)((unsigned int)getsdr1() & 0xffff0000);
369 printf("old_ptable %p\n", old_ptable);
370 for(i=0; i< (64*1024) >> 6 ; i++) {
371 for(j=0; j< 8; j++)
372 if(old_ptable[i].pte[j].v && old_ptable[i].pte[j].vsid == 0)
373 print_pte(&old_ptable[i].pte[j]);
376 #endif
378 unsigned int sp;
379 asm volatile("mr %0,1" : "=r"(sp));
380 printf("sp = 0x%x\n", sp);
382 /* set up the new BATs */
383 getibats(ibats);
384 getdbats(dbats);
386 for(i=0; i<8; i++) {
387 ibats[i] = 0;
388 dbats[i] = 0;
390 // identity map the first 256MB of RAM
391 dbats[0] = ibats[0] = BATU_LEN_256M | BATU_VS;
392 dbats[1] = ibats[1] = BATL_CI | BATL_PP_RW;
394 // XXX fix
395 dbats[2] = ibats[2] = 0x80000000 | BATU_LEN_256M | BATU_VS;
396 dbats[3] = ibats[3] = 0x80000000 | BATL_CI | BATL_PP_RW;
397 dbats[4] = ibats[4] = 0x90000000 | BATU_LEN_256M | BATU_VS;
398 dbats[5] = ibats[5] = 0x90000000 | BATL_CI | BATL_PP_RW;
399 dbats[6] = ibats[6] = 0xf0000000 | BATU_LEN_256M | BATU_VS;
400 dbats[7] = ibats[7] = 0xf0000000 | BATL_CI | BATL_PP_RW;
403 #if 0
404 // map the framebuffer using a BAT to 256MB
406 unsigned int framebuffer_phys = ka->fb.mapping.start & ~((16*1024*1024) - 1);
408 dbats[2] = ibats[2] = 0x10000000 | BATU_LEN_16M | BATU_VS;
409 dbats[3] = ibats[3] = framebuffer_phys | BATL_CI | BATL_PP_RW;
410 printf("remapping framebuffer at pa 0x%x to va 0x%x using BAT\n",
411 ka->fb.mapping.start, 0x10000000 + ka->fb.mapping.start - framebuffer_phys);
412 s2_change_framebuffer_addr(ka, 0x10000000 + ka->fb.mapping.start - framebuffer_phys);
414 #endif
415 setibats(ibats);
416 setdbats(dbats);
418 tlbia();
420 printf("unsetting the old page table\n");
421 setsdr1(0);
422 tlbia();
424 printf("memsetting new pagetable\n");
425 memset(ptable, 0, ptable_size);
427 printf("done\n");
429 printf("setting up the 16 segment registers\n");
430 // set up the segment registers
431 for(i=0; i<16; i++) {
432 setsr(i * 0x10000000, i);
435 printf("done, setting sdr1\n");
436 setsdr1(((unsigned int)ptable & 0xffff0000) | (ptable_hash_mask >> 10));
437 tlbia();
438 printf("sdr1 = 0x%x\n", getsdr1());
440 #if 0
441 mmu_map_page(0x96008000, 0x96008000);
442 mmu_map_page(0x96009000, 0x96009000);
443 mmu_map_page(0x9600a000, 0x9600a000);
444 mmu_map_page(0x96008000, 0x30000000);
446 printf("testing...\n");
447 printf("hello\n");
448 printf("%d\n", *(int *)0x30000000);
449 printf("%d\n", *(int *)0x96008000);
451 *(int *)0x30000000 = 0x99;
452 printf("%d\n", *(int *)0x30000000);
453 printf("%d\n", *(int *)0x96008000);
455 printf("hello2\n");
456 #endif
458 printf("done\n");
459 return 0;
462 int s2_mmu_remap_pagetable(kernel_args *ka)
464 unsigned long i;
465 unsigned long new_ptable;
467 // find a new spot to allocate the page table
468 // XXX make better
469 new_ptable = ka->virt_alloc_range[0].start + ka->virt_alloc_range[0].size;
471 for(i = 0; i < ptable_size; i += PAGE_SIZE) {
472 mmu_map_page(ka, ka->arch_args.page_table.start + i, new_ptable + i, true);
475 ka->arch_args.page_table_virt.start = new_ptable;
476 ka->arch_args.page_table_virt.size = ka->arch_args.page_table.size;
479 int s2_mmu_remove_fb_bat_entries(kernel_args *ka)
481 unsigned int ibat[8];
482 unsigned int dbat[8];
484 // zero out the 2nd bat entry, used to map the framebuffer
485 getibats(ibat);
486 getdbats(dbat);
487 ibat[2] = ibat[3] = dbat[2] = dbat[3] = 0;
488 setibats(ibat);
489 setdbats(dbat);
491 return NO_ERROR;
495 static void print_pte(struct ppc_pte *e)
497 printf("entry %p: ", e);
498 printf("v %d ", e->v);
499 if(e->v) {
500 printf("vsid 0x%x ", e->vsid);
501 printf("hash %d ", e->hash);
502 printf("api 0x%x ", e->api);
504 printf("ppn 0x%x ", e->ppn);
505 printf("r %d ", e->r);
506 printf("c %d ", e->c);
507 printf("wimg 0x%x ", e->wimg);
508 printf("pp 0x%x ", e->pp);
510 printf("\n");
513 void mmu_map_page(kernel_args *ka, unsigned long pa, unsigned long va, bool cached)
515 unsigned int hash;
516 struct ppc_pteg *pteg;
517 int i;
518 unsigned int vsid;
520 // mark it used if this is in the kernel area
521 if(va >= KERNEL_BASE) {
522 mark_used_virt_mem_range(ka, va, PAGE_SIZE);
525 // lookup the vsid based off the va
526 vsid = getsr(va) & 0xffffff;
528 // printf("mmu_map_page: vsid %d, pa 0x%x, va 0x%x\n", vsid, pa, va);
530 hash = primary_hash(vsid, va);
531 // printf("hash = 0x%x\n", hash);
533 pteg = &ptable[hash];
534 // printf("pteg @ 0x%x\n", pteg);
536 // search for the first free slot for this pte
537 for(i=0; i<8; i++) {
538 // printf("trying pteg[%i]\n", i);
539 if(pteg->pte[i].v == 0) {
540 // upper word
541 pteg->pte[i].ppn = pa / PAGE_SIZE;
542 pteg->pte[i].unused = 0;
543 pteg->pte[i].r = 0;
544 pteg->pte[i].c = 0;
545 pteg->pte[i].wimg = cached ? 0 : (1 << 3);
546 pteg->pte[i].unused1 = 0;
547 pteg->pte[i].pp = 0x2; // RW
548 asm volatile("eieio");
549 // lower word
550 pteg->pte[i].vsid = vsid;
551 pteg->pte[i].hash = 0; // primary
552 pteg->pte[i].api = (va >> 22) & 0x3f;
553 pteg->pte[i].v = 1;
554 tlbia();
555 // printf("set pteg to ");
556 // print_pte(&pteg->pte[i]);
557 // printf("set pteg to 0x%x 0x%x\n", *((int *)&pteg->pte[i]), *(((int *)&pteg->pte[i])+1));
558 return;
563 static unsigned int primary_hash(unsigned int vsid, unsigned int vaddr)
565 unsigned int page_index;
567 vsid &= 0x7ffff;
568 page_index = (vaddr >> 12) & 0xffff;
570 return (vsid ^ page_index) & ptable_hash_mask;
573 static unsigned int secondary_hash(unsigned int primary_hash)
575 return ~primary_hash;