work in progress on ppc port
[newos.git] / boot / ppc / stage2_mmu.c
blob7ed048d31c003e28a01898c453dd36ffee84395c
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 <libc/string.h>
7 #include "stage2_priv.h"
9 #define PAGE_SIZE 4096
11 static unsigned int primary_hash(unsigned int vsid, unsigned int vaddr);
12 static unsigned int secondary_hash(unsigned int primary_hash);
14 // BAT register defs
15 #define BATU_BEPI_MASK 0xfffe0000
16 #define BATU_LEN_256M 0x1ffc
17 #define BATU_VS 0x2
18 #define BATU_VP 0x1
20 #define BATL_BRPN_MASK 0xfffe0000
21 #define BATL_WIMG_MASK 0x78
22 #define BATL_WT 0x40
23 #define BATL_CI 0x20
24 #define BATL_MC 0x10
25 #define BATL_G 0x08
26 #define BATL_PP_MASK 0x3
27 #define BATL_PP_RO 0x1
28 #define BATL_PP_RW 0x2
30 struct pte {
31 // pte upper word
32 unsigned int v : 1;
33 unsigned int vsid : 24;
34 unsigned int hash : 1;
35 unsigned int api : 6;
36 // pte lower word
37 unsigned int ppn : 20;
38 unsigned int unused : 3;
39 unsigned int r : 1;
40 unsigned int c : 1;
41 unsigned int wimg : 4;
42 unsigned int unused1 : 1;
43 unsigned int pp : 2;
45 struct pteg {
46 struct pte pte[8];
48 static struct pteg *ptable = 0;
49 static int ptable_size = 0;
50 static unsigned int ptable_hash_mask = 0;
53 static unsigned long total_ram_size = 0;
55 static void print_pte(struct pte *e);
58 static void find_phys_memory_map(kernel_args *ka)
60 int handle;
61 int i;
62 struct mem_region {
63 unsigned long pa;
64 int len;
65 } mem_regions[33];
66 int mem_regions_len = 0;
68 // get the physical memory map of the system
69 handle = of_finddevice("/memory");
70 memset(mem_regions, 0, sizeof(mem_regions));
71 mem_regions_len = of_getprop(handle, "reg", mem_regions, sizeof(mem_regions[0]) * 32);
72 mem_regions_len /= sizeof(struct mem_region);
74 // copy these regions over to the kernel args structure
75 ka->num_phys_mem_ranges = 0;
76 for(i=0; i<mem_regions_len; i++) {
77 if(mem_regions[i].len > 0) {
78 total_ram_size += mem_regions[i].len;
79 if(i > 0) {
80 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) {
81 // this range just extends the old one
82 ka->phys_mem_range[ka->num_phys_mem_ranges-1].size += mem_regions[i].len;
83 break;
86 ka->phys_mem_range[ka->num_phys_mem_ranges].start = mem_regions[i].pa;
87 ka->phys_mem_range[ka->num_phys_mem_ranges].size = mem_regions[i].len;
88 ka->num_phys_mem_ranges++;
89 if(ka->num_phys_mem_ranges == MAX_PHYS_MEM_ADDR_RANGE) {
90 printf("too many physical memory maps, increase MAX_PHYS_MEM_ADDR_RANGE\n");
91 for(;;);
95 for(i=0; i<ka->num_phys_mem_ranges; i++) {
96 printf("phys map %d: pa 0x%x, len %d\n", i, ka->phys_mem_range[i].start, ka->phys_mem_range[i].size);
100 static void find_used_phys_memory_map(kernel_args *ka)
102 int handle;
103 int i;
104 struct translation_map {
105 unsigned long va;
106 int len;
107 unsigned long pa;
108 int mode;
109 } memmap[64];
110 int translation_map_len = 0;
112 // get the current translation map of the system,
113 // to find how much memory was mapped to load the stage1 and bootdir
114 handle = of_finddevice("/chosen");
115 of_getprop(handle, "mmu", &handle, sizeof(handle));
116 handle = of_instance_to_package(handle);
117 memset(&memmap, 0, sizeof(memmap));
118 translation_map_len = of_getprop(handle, "translations", memmap, sizeof(memmap));
119 translation_map_len /= sizeof(struct translation_map);
120 for(i=0; i<translation_map_len; i++) {
121 printf("package loaded at pa 0x%x va 0x%x, len 0x%x\n", memmap[i].pa, memmap[i].va, memmap[i].len);
122 if(memmap[i].va == LOAD_ADDR) {
123 // we found the translation that covers the loaded package. Save this.
124 ka->phys_alloc_range[0].start = memmap[i].pa;
125 ka->phys_alloc_range[0].size = memmap[i].len;
126 ka->num_phys_alloc_ranges = 1;
131 static void tlbia()
133 unsigned long i;
135 asm volatile("sync");
136 for(i=0; i< 0x40000; i += 0x1000) {
137 asm volatile("tlbie %0" :: "r" (i));
138 asm volatile("eieio");
139 asm volatile("sync");
141 asm volatile("tlbsync");
142 asm volatile("sync");
145 #define CACHELINE 64
147 void syncicache(void *address, int len)
149 int l, off;
150 char *p;
152 off = (unsigned int)address & (CACHELINE - 1);
153 len += off;
155 l = len;
156 p = (char *)address - off;
157 do {
158 asm volatile ("dcbst 0,%0" :: "r"(p));
159 p += CACHELINE;
160 } while((l -= CACHELINE) > 0);
161 asm volatile ("sync");
162 p = (char *)address - off;
163 do {
164 asm volatile ("icbi 0,%0" :: "r"(p));
165 p += CACHELINE;
166 } while((len -= CACHELINE) > 0);
167 asm volatile ("sync");
168 asm volatile ("isync");
171 int s2_mmu_init(kernel_args *ka)
173 unsigned int ibats[8];
174 unsigned int dbats[8];
175 int i;
177 getibats(ibats);
178 getdbats(dbats);
180 for(i=0; i<8; i++) {
181 ibats[0] = 0;
182 dbats[0] = 0;
184 // identity map the first 512Mb of RAM
185 ibats[0] = BATU_LEN_256M | BATU_VS;
186 dbats[0] = BATU_LEN_256M | BATU_VS;
187 ibats[1] = BATL_MC | BATL_PP_RW;
188 dbats[1] = BATL_MC | BATL_PP_RW;
189 ibats[2] = 0x10000000 | BATU_LEN_256M | BATU_VS;
190 dbats[2] = 0x10000000 | BATU_LEN_256M | BATU_VS;
191 ibats[3] = 0x10000000 | BATL_MC | BATL_PP_RW;
192 dbats[3] = 0x10000000 | BATL_MC | BATL_PP_RW;
194 /* map 0x90000000 down to a usable spot in memory */
195 ibats[4] = 0x20000000 | BATU_LEN_256M | BATU_VS;
196 dbats[4] = 0x20000000 | BATU_LEN_256M | BATU_VS;
197 ibats[5] = 0x90000000 | BATL_CI | BATL_PP_RW;
198 dbats[5] = 0x90000000 | BATL_CI | BATL_PP_RW;
200 setibats(ibats);
201 setdbats(dbats);
203 tlbia();
205 // XXX remove
206 s2_change_framebuffer_addr(0x26008000);
208 printf("msr = 0x%x\n", getmsr());
210 // figure out where physical memory is and what is being used
211 find_phys_memory_map(ka);
212 find_used_phys_memory_map(ka);
214 // allocate a page table
216 unsigned long top_ram = 0;
218 // find the largest address of physical memory, but with a max of 512 MB,
219 // so it'll be within our 512 MB BAT window
220 for(i=0; i<ka->num_phys_mem_ranges; i++) {
221 if(ka->phys_mem_range[i].start + ka->phys_mem_range[i].size > top_ram) {
222 if(ka->phys_mem_range[i].start + ka->phys_mem_range[i].size > 512*1024*1024) {
223 if(ka->phys_mem_range[i].start < 512*1024*1024) {
224 top_ram = 512*1024*1024;
225 break;
228 top_ram = ka->phys_mem_range[i].start + ka->phys_mem_range[i].size;
231 printf("top of ram (but under 512MB) is 0x%x\n", top_ram);
233 // figure the size of the new pagetable, as recommended by Motorola
234 if(total_ram_size <= 8*1024*1024) {
235 ptable_size = 64*1024;
236 } else if(total_ram_size <= 16*1024*1024) {
237 ptable_size = 128*1024;
238 } else if(total_ram_size <= 32*1024*1024) {
239 ptable_size = 256*1024;
240 } else if(total_ram_size <= 64*1024*1024) {
241 ptable_size = 512*1024;
242 } else if(total_ram_size <= 128*1024*1024) {
243 ptable_size = 1024*1024;
244 } else if(total_ram_size <= 256*1024*1024) {
245 ptable_size = 2*1024*1024;
246 } else if(total_ram_size <= 512*1024*1024) {
247 ptable_size = 4*1024*1024;
248 } else if(total_ram_size <= 1024*1024*1024) {
249 ptable_size = 8*1024*1024;
250 } else if(total_ram_size <= 2*1024*1024*1024) {
251 ptable_size = 16*1024*1024;
252 } else {
253 ptable_size = 32*1024*1024;
256 // figure out where to put the page table
257 ptable_hash_mask = (ptable_size >> 6) - 1;
258 ptable = (struct pteg *)(top_ram - ptable_size);
259 printf("ptable at pa 0x%x, size 0x%x\n", ptable, ptable_size);
260 printf("mask = 0x%x\n", ptable_hash_mask);
262 // save it's new location in the kernel args
263 ka->arch_args.page_table.start = (unsigned long)ptable;
264 ka->arch_args.page_table.size = ptable_size;
265 ka->arch_args.page_table_mask = ptable_hash_mask;
267 #if 0
269 struct pteg *old_ptable;
270 int j;
272 printf("sdr1 = 0x%x\n", getsdr1());
274 old_ptable = (struct pteg *)((unsigned int)getsdr1() & 0xffff0000);
275 printf("old_ptable %p\n", old_ptable);
276 for(i=0; i< (64*1024) >> 6 ; i++) {
277 for(j=0; j< 8; j++)
278 if(old_ptable[i].pte[j].v && old_ptable[i].pte[j].vsid == 0)
279 print_pte(&old_ptable[i].pte[j]);
282 #endif
284 printf("memsetting pagetable and performing switch\n");
285 memset(ptable, 0, ptable_size);
287 // set up the segment registers
288 for(i=0; i<16; i++) {
289 setsr(i * 0x10000000, i);
292 printf("done, setting sdr1\n");
293 setsdr1(((unsigned int)ptable & 0xffff0000) | (ptable_hash_mask >> 10));
294 tlbia();
295 printf("sdr1 = 0x%x\n", getsdr1());
297 #if 0
298 mmu_map_page(0x96008000, 0x96008000);
299 mmu_map_page(0x96009000, 0x96009000);
300 mmu_map_page(0x9600a000, 0x9600a000);
301 mmu_map_page(0x0, 0x30000000);
303 printf("testing...\n");
304 printf("hello\n");
305 printf("%d\n", *(int *)0x30000000);
306 printf("%d\n", *(int *)0x96008000);
307 printf("hello2\n");
308 #endif
310 return 0;
313 static void print_pte(struct pte *e)
315 printf("entry %p: ", e);
316 printf("v %d ", e->v);
317 if(e->v) {
318 printf("vsid 0x%x ", e->vsid);
319 printf("hash %d ", e->hash);
320 printf("api 0x%x ", e->api);
322 printf("ppn 0x%x ", e->ppn);
323 printf("r %d ", e->r);
324 printf("c %d ", e->c);
325 printf("wimg 0x%x ", e->wimg);
326 printf("pp 0x%x ", e->pp);
328 printf("\n");
331 void mmu_map_page(unsigned long pa, unsigned long va)
333 unsigned int hash;
334 struct pteg *pteg;
335 int i;
336 unsigned int vsid;
338 // lookup the vsid based off the va
339 vsid = getsr(va) & 0xffffff;
341 // printf("mmu_map_page: vsid %d, pa 0x%x, va 0x%x\n", vsid, pa, va);
343 hash = primary_hash(vsid, va);
344 // printf("hash = 0x%x\n", hash);
346 pteg = &ptable[hash];
347 // printf("pteg @ 0x%x\n", pteg);
349 // search for the first free slot for this pte
350 for(i=0; i<8; i++) {
351 // printf("trying pteg[%i]\n", i);
352 if(pteg->pte[i].v == 0) {
353 // upper word
354 pteg->pte[i].ppn = pa / PAGE_SIZE;
355 pteg->pte[i].unused = 0;
356 pteg->pte[i].r = 0;
357 pteg->pte[i].c = 0;
358 pteg->pte[i].wimg = 0x0;
359 pteg->pte[i].unused1 = 0;
360 pteg->pte[i].pp = 0x2; // RW
361 asm volatile("eieio");
362 // lower word
363 pteg->pte[i].vsid = vsid;
364 pteg->pte[i].hash = 0; // primary
365 pteg->pte[i].api = (va >> 22) & 0x3f;
366 pteg->pte[i].v = 1;
367 tlbia();
368 // printf("set pteg to ");
369 // print_pte(&pteg->pte[i]);
370 // printf("set pteg to 0x%x 0x%x\n", *((int *)&pteg->pte[i]), *(((int *)&pteg->pte[i])+1));
371 return;
376 static unsigned int primary_hash(unsigned int vsid, unsigned int vaddr)
378 unsigned int page_index;
380 vsid &= 0x7ffff;
381 page_index = (vaddr >> 12) & 0xffff;
383 return (vsid ^ page_index) & ptable_hash_mask;
386 static unsigned int secondary_hash(unsigned int primary_hash)
388 return ~primary_hash;