2 * Creation Date: <1999/11/07 19:02:11 samuel>
3 * Time-stamp: <2004/01/07 19:42:36 samuel>
9 * Copyright (C) 1999-2004 Samuel Rydh (samuel@ibrium.se)
10 * Copyright (C) 2004 Stefan Reinauer
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation
19 #include "libopenbios/bindings.h"
20 #include "libc/string.h"
21 #include "libopenbios/ofmem.h"
24 #include "asm/processor.h"
26 #define BIT(n) (1U << (31 - (n)))
28 #define SLB_VSID_SHIFT 12
30 /* called from assembly */
31 extern void dsi_exception(void);
32 extern void isi_exception(void);
33 extern void setup_mmu(unsigned long code_base);
36 * From Apple's BootX source comments:
38 * 96 MB map (currently unused - 4363357 tracks re-adoption)
39 * 00000000 - 00003FFF : Exception Vectors
40 * 00004000 - 03FFFFFF : Kernel Image, Boot Struct and Drivers (~64 MB)
41 * 04000000 - 04FFFFFF : File Load Area (16 MB) [80 MB]
42 * 05000000 - 053FFFFF : FS Cache (4 MB) [84 MB]
43 * 05400000 - 055FFFFF : Malloc Zone (2 MB) [86 MB]
44 * 05600000 - 057FFFFF : BootX Image (2 MB) [88 MB]
45 * 05800000 - 05FFFFFF : Unused/OF (8 MB) [96 MB]
49 #define FREE_BASE 0x00004000UL
50 #define OF_CODE_START 0xfff00000UL
51 #define OF_CODE_SIZE 0x00100000
52 #define IO_BASE 0x80000000UL
59 #define HASH_SIZE (2 << HASH_BITS)
60 #define OFMEM_SIZE (1 * 1024 * 1024 + 512 * 1024)
62 #define SEGR_USER BIT(2)
63 #define SEGR_BASE 0x0400
65 static inline unsigned long
68 return (mfsdr1() & SDR1_HTABORG_MASK);
71 static inline unsigned long
74 ofmem_t *ofmem = ofmem_arch_get_private();
75 return ofmem->ramsize - OF_CODE_SIZE;
81 return get_hash_base() - (32 + 64 + 64) * 1024 - OFMEM_SIZE;
90 static unsigned long get_heap_top(void)
92 return get_hash_base() - (32 + 64 + 64) * 1024;
95 static inline size_t ALIGN_SIZE(size_t x, size_t a)
97 return (x + a - 1) & ~(a - 1);
100 ofmem_t* ofmem_arch_get_private(void)
102 return (ofmem_t*)cell2pointer(get_heap_top() - OFMEM_SIZE);
105 void* ofmem_arch_get_malloc_base(void)
107 return (char*)ofmem_arch_get_private() + ALIGN_SIZE(sizeof(ofmem_t), 4);
110 ucell ofmem_arch_get_heap_top(void)
112 return get_heap_top();
115 ucell ofmem_arch_get_virt_top(void)
120 void ofmem_arch_unmap_pages(ucell virt, ucell size)
122 /* kill page mappings in provided range */
125 void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode)
130 ucell ofmem_arch_get_iomem_base(void)
132 /* Currently unused */
136 ucell ofmem_arch_get_iomem_top(void)
138 /* Currently unused */
142 retain_t *ofmem_arch_get_retained(void)
144 /* not implemented */
148 int ofmem_arch_get_physaddr_cellsize(void)
157 int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value)
161 p[n++] = value >> 32;
167 /* Return size of a single MMU package translation property entry in cells */
168 int ofmem_arch_get_translation_entry_size(void)
170 return 3 + ofmem_arch_get_physaddr_cellsize();
173 /* Generate translation property entry for PPC.
174 * According to the platform bindings for PPC
175 * (http://www.openfirmware.org/1275/bindings/ppc/release/ppc-2_1.html#REF34579)
176 * a translation property entry has the following layout:
183 void ofmem_arch_create_translation_entry(ucell *transentry, translation_t *t)
187 transentry[i++] = t->virt;
188 transentry[i++] = t->size;
189 i += ofmem_arch_encode_physaddr(&transentry[i], t->phys);
190 transentry[i++] = t->mode;
193 /* Return the size of a memory available entry given the phandle in cells */
194 int ofmem_arch_get_available_entry_size(phandle_t ph)
196 if (ph == s_phandle_memory) {
197 return 1 + ofmem_arch_get_physaddr_cellsize();
203 /* Generate memory available property entry for PPC */
204 void ofmem_arch_create_available_entry(phandle_t ph, ucell *availentry, phys_addr_t start, ucell size)
208 if (ph == s_phandle_memory) {
209 i += ofmem_arch_encode_physaddr(availentry, start);
211 availentry[i++] = start;
214 availentry[i] = size;
217 /************************************************************************/
218 /* OF private allocations */
219 /************************************************************************/
221 /* Private functions for mapping between physical/virtual addresses */
223 va2pa(unsigned long va)
225 if (va >= OF_CODE_START && va < OF_CODE_START + OF_CODE_SIZE) {
226 return (phys_addr_t)get_rom_base() - OF_CODE_START + va;
228 return (phys_addr_t)va;
233 pa2va(phys_addr_t pa)
235 if ((pa - get_rom_base() + OF_CODE_START >= OF_CODE_START) &&
236 (pa - get_rom_base() + OF_CODE_START < OF_CODE_START + OF_CODE_SIZE))
237 return (unsigned long)pa - get_rom_base() + OF_CODE_START;
239 return (unsigned long)pa;
245 return ofmem_malloc(size);
255 realloc(void *ptr, size_t size)
257 return ofmem_realloc(ptr, size);
261 /************************************************************************/
263 /************************************************************************/
265 ucell ofmem_arch_default_translation_mode(phys_addr_t phys)
267 /* XXX: Guard bit not set as it should! */
269 return 0x02; /*0xa*/ /* wim GxPp */
270 return 0x6a; /* WIm GxPp, I/O */
273 ucell ofmem_arch_io_translation_mode(phys_addr_t phys)
275 return 0x6a; /* WIm GxPp, I/O */
278 /************************************************************************/
279 /* page fault handler */
280 /************************************************************************/
283 ea_to_phys(unsigned long ea, ucell *mode)
287 if (ea >= OF_CODE_START && ea <= 0xffffffffUL) {
290 phys = get_rom_base() + ea;
295 phys = ofmem_translate(ea, mode);
298 *mode = ofmem_arch_default_translation_mode(phys);
300 /* print_virt_range(); */
301 /* print_phys_range(); */
307 /* Converts a global variable (from .data or .bss) into a pointer that
308 can be accessed from real mode */
310 global_ptr_real(void *p)
312 return (void*)((uintptr_t)p - OF_CODE_START + get_rom_base());
315 /* Return the next slot to evict, in the range of [0..7] */
317 next_evicted_slot(void)
319 static int next_grab_slot;
320 int *next_grab_slot_va;
323 next_grab_slot_va = global_ptr_real(&next_grab_slot);
324 r = *next_grab_slot_va;
325 *next_grab_slot_va = (r + 1) % 8;
331 hash_page_64(unsigned long ea, phys_addr_t phys, ucell mode)
333 uint64_t vsid_mask, page_mask, pgidx, hash;
334 uint64_t htab_mask, mask, avpn;
335 unsigned long pgaddr;
337 unsigned int vsid, vsid_sh, sdr, sdr_sh, sdr_mask;
340 vsid = (ea >> 28) + SEGR_BASE;
342 vsid_mask = 0x00003FFFFFFFFF80ULL;
346 page_mask = 0x0FFFFFFF; // XXX correct?
347 pgidx = (ea & page_mask) >> PAGE_SHIFT;
348 avpn = (vsid << 12) | ((pgidx >> 4) & 0x0F80);;
350 hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
351 htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F));
352 mask = (htab_mask << sdr_sh) | sdr_mask;
353 pgaddr = sdr | (hash & mask);
354 pp = (mPTE_64_t *)pgaddr;
356 /* replace old translation */
357 for (found = 0, i = 0; !found && i < 8; i++)
358 if (pp[i].avpn == avpn)
361 /* otherwise use a free slot */
362 for (i = 0; !found && i < 8; i++)
366 /* out of slots, just evict one */
368 i = next_evicted_slot() + 1;
377 .rpn = (phys & ~0xfffUL) >> 12,
378 .r = mode & (1 << 8) ? 1 : 0,
379 .c = mode & (1 << 7) ? 1 : 0,
380 .w = mode & (1 << 6) ? 1 : 0,
381 .i = mode & (1 << 5) ? 1 : 0,
382 .m = mode & (1 << 4) ? 1 : 0,
383 .g = mode & (1 << 3) ? 1 : 0,
384 .n = mode & (1 << 2) ? 1 : 0,
390 asm volatile("tlbie %0" :: "r"(ea));
394 hash_page_32(unsigned long ea, phys_addr_t phys, ucell mode)
396 #ifndef __powerpc64__
397 unsigned long *upte, cmp, hash1;
401 vsid = (ea >> 28) + SEGR_BASE;
402 cmp = BIT(0) | (vsid << 7) | ((ea & 0x0fffffff) >> 22);
405 hash1 ^= (ea >> 12) & 0xffff;
406 hash1 &= (((mfsdr1() & 0x1ff) << 16) | 0xffff) >> 6;
408 pp = (mPTE_t*)(get_hash_base() + (hash1 << 6));
409 upte = (unsigned long*)pp;
411 /* replace old translation */
412 for (found = 0, i = 0; !found && i < 8; i++)
413 if (cmp == upte[i*2])
416 /* otherwise use a free slot */
417 for (i = 0; !found && i < 8; i++)
421 /* out of slots, just evict one */
423 i = next_evicted_slot() + 1;
426 upte[i * 2 + 1] = (phys & ~0xfff) | mode;
428 asm volatile("tlbie %0" :: "r"(ea));
432 static int is_ppc64(void)
436 #elif defined(CONFIG_PPC_64BITSUPPORT)
437 unsigned int pvr = mfpvr();
438 return ((pvr >= 0x330000) && (pvr < 0x70330000));
444 /* XXX Remove these ugly constructs when legacy 64-bit support is dropped. */
445 static void hash_page(unsigned long ea, phys_addr_t phys, ucell mode)
448 hash_page_64(ea, phys, mode);
450 hash_page_32(ea, phys, mode);
456 unsigned long dar, dsisr;
460 asm volatile("mfdar %0" : "=r" (dar) : );
461 asm volatile("mfdsisr %0" : "=r" (dsisr) : );
463 phys = ea_to_phys(dar, &mode);
464 hash_page(dar, phys, mode);
470 unsigned long nip, srr1;
474 asm volatile("mfsrr0 %0" : "=r" (nip) : );
475 asm volatile("mfsrr1 %0" : "=r" (srr1) : );
477 phys = ea_to_phys(nip, &mode);
478 hash_page(nip, phys, mode);
482 /************************************************************************/
484 /************************************************************************/
487 setup_mmu(unsigned long ramsize)
490 #ifndef __powerpc64__
491 unsigned long sr_base;
493 unsigned long hash_base;
494 unsigned long hash_mask = ~0x000fffffUL; /* alignment for ppc64 */
497 /* SDR1: Storage Description Register 1 */
499 hash_base = (ramsize - OF_CODE_SIZE - HASH_SIZE) & hash_mask;
500 memset((void *)hash_base, 0, HASH_SIZE);
502 mtsdr1(hash_base | MAX(HASH_BITS - 18, 0));
504 mtsdr1(hash_base | ((HASH_SIZE - 1) >> 16));
508 /* Segment Lookaside Buffer */
510 slbia(); /* Invalidate all SLBs except SLB 0 */
511 for (i = 0; i < 16; i++) {
512 unsigned long rs = (0x400 + i) << SLB_VSID_SHIFT;
513 unsigned long rb = ((unsigned long)i << 28) | (1 << 27) | i;
519 /* Segment Register */
521 sr_base = SEGR_USER | SEGR_BASE ;
522 for (i = 0; i < 16; i++) {
524 asm volatile("mtsrin %0,%1" :: "r" (sr_base + i), "r" (j));
529 ofmem = ofmem_arch_get_private();
530 memset(ofmem, 0, sizeof(ofmem_t));
531 ofmem->ramsize = ramsize;
533 memcpy((void *)get_rom_base(), (void *)OF_CODE_START, OF_CODE_SIZE);
537 mtmsr(mfmsr() | MSR_IR | MSR_DR);
543 ofmem_t *ofmem = ofmem_arch_get_private();
545 /* Map the memory (don't map page 0 to allow catching of NULL dereferences) */
546 ofmem_claim_phys(PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0);
547 ofmem_claim_virt(PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0);
548 ofmem_map(PAGE_SIZE, PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0);
550 /* Mark the first page as non-free */
551 ofmem_claim_phys(0, PAGE_SIZE, 0);
552 ofmem_claim_virt(0, PAGE_SIZE, 0);
554 /* Map everything at the top of physical RAM 1:1, minus the OpenBIOS ROM in RAM copy */
555 ofmem_claim_phys(get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0);
556 ofmem_claim_virt(get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0);
557 ofmem_map(get_ram_top(), get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0);
559 /* Map the OpenBIOS ROM in RAM copy */
560 ofmem_claim_phys(ofmem->ramsize - OF_CODE_SIZE, OF_CODE_SIZE, 0);
561 ofmem_claim_virt(OF_CODE_START, OF_CODE_SIZE, 0);
562 ofmem_map(ofmem->ramsize - OF_CODE_SIZE, OF_CODE_START, OF_CODE_SIZE, 0);