2 ** Copyright 2001, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
5 #include <boot/stage2.h>
6 #include <boot/shared/openfirmware.h>
7 #include <kernel/kernel.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
41 static void find_phys_memory_map(kernel_args
*ka
)
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
;
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");
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
)
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))
99 static void mark_used_phys_mem_range(kernel_args
*ka
, unsigned long base
, unsigned long len
)
104 base
= ROUNDOWN(base
, PAGE_SIZE
);
105 len
= ROUNDUP(len
, PAGE_SIZE
);
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
) {
114 ka
->phys_alloc_range
[i
].size
+= PAGE_SIZE
;
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
;
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
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
);
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
++;
145 static void find_used_phys_memory_map(kernel_args
*ka
)
149 struct translation_map
{
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
)
187 base
= ROUNDOWN(base
, PAGE_SIZE
);
188 len
= ROUNDUP(len
, PAGE_SIZE
);
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
) {
197 ka
->virt_alloc_range
[i
].size
+= PAGE_SIZE
;
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
;
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
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
);
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
++;
228 unsigned long mmu_allocate_page(kernel_args
*ka
)
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
);
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
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");
265 void syncicache(void *address
, int len
)
270 off
= (unsigned int)address
& (CACHELINE
- 1);
274 p
= (char *)address
- off
;
276 asm volatile ("dcbst 0,%0" :: "r"(p
));
278 } while((l
-= CACHELINE
) > 0);
279 asm volatile ("sync");
280 p
= (char *)address
- off
;
282 asm volatile ("icbi 0,%0" :: "r"(p
));
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;
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
);
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;
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
);
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;
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
);
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
;
363 struct ppc_pteg
*old_ptable
;
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
++) {
372 if(old_ptable
[i
].pte
[j
].v
&& old_ptable
[i
].pte
[j
].vsid
== 0)
373 print_pte(&old_ptable
[i
].pte
[j
]);
379 asm volatile("mr %0,1" : "=r"(sp
));
380 printf("sp = 0x%x\n", sp
);
382 /* set up the new BATs */
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
;
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
;
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
);
420 printf("unsetting the old page table\n");
424 printf("memsetting new pagetable\n");
425 memset(ptable
, 0, ptable_size
);
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));
438 printf("sdr1 = 0x%x\n", getsdr1());
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");
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);
462 int s2_mmu_remap_pagetable(kernel_args
*ka
)
465 unsigned long new_ptable
;
467 // find a new spot to allocate the page table
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
487 ibat
[2] = ibat
[3] = dbat
[2] = dbat
[3] = 0;
495 static void print_pte(struct ppc_pte
*e
)
497 printf("entry %p: ", e
);
498 printf("v %d ", 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
);
513 void mmu_map_page(kernel_args
*ka
, unsigned long pa
, unsigned long va
, bool cached
)
516 struct ppc_pteg
*pteg
;
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
538 // printf("trying pteg[%i]\n", i);
539 if(pteg
->pte
[i
].v
== 0) {
541 pteg
->pte
[i
].ppn
= pa
/ PAGE_SIZE
;
542 pteg
->pte
[i
].unused
= 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");
550 pteg
->pte
[i
].vsid
= vsid
;
551 pteg
->pte
[i
].hash
= 0; // primary
552 pteg
->pte
[i
].api
= (va
>> 22) & 0x3f;
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));
563 static unsigned int primary_hash(unsigned int vsid
, unsigned int vaddr
)
565 unsigned int page_index
;
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
;