| /* lib.c |
| * tag: simple function library |
| * |
| * Copyright (C) 2003 Stefan Reinauer |
| * |
| * See the file "COPYING" for further information about |
| * the copyright and warranty status of this work. |
| */ |
| |
| #include "config.h" |
| #include "libc/vsprintf.h" |
| #include "libopenbios/bindings.h" |
| #include "spitfire.h" |
| #include "libopenbios/sys_info.h" |
| #include "boot.h" |
| |
| #include "arch/sparc64/ofmem_sparc64.h" |
| |
| /* Format a string and print it on the screen, just like the libc |
| * function printf. |
| */ |
| int printk( const char *fmt, ... ) |
| { |
| char *p, buf[512]; |
| va_list args; |
| int i; |
| |
| va_start(args, fmt); |
| i = vsnprintf(buf, sizeof(buf), fmt, args); |
| va_end(args); |
| |
| for( p=buf; *p; p++ ) |
| putchar(*p); |
| return i; |
| } |
| |
| /* Private functions for mapping between physical/virtual addresses */ |
| phys_addr_t |
| va2pa(unsigned long va) |
| { |
| if ((va >= (unsigned long)&_start) && |
| (va < (unsigned long)&_end)) |
| return va - va_shift; |
| else |
| return va; |
| } |
| |
| unsigned long |
| pa2va(phys_addr_t pa) |
| { |
| if ((pa + va_shift >= (unsigned long)&_start) && |
| (pa + va_shift < (unsigned long)&_end)) |
| return pa + va_shift; |
| else |
| return pa; |
| } |
| |
| void *malloc(int size) |
| { |
| return ofmem_malloc(size); |
| } |
| |
| void* realloc( void *ptr, size_t size ) |
| { |
| return ofmem_realloc(ptr, size); |
| } |
| |
| void free(void *ptr) |
| { |
| ofmem_free(ptr); |
| } |
| |
| static void |
| mmu_open(void) |
| { |
| RET(-1); |
| } |
| |
| static void |
| mmu_close(void) |
| { |
| } |
| |
| void ofmem_walk_boot_map(translation_entry_cb cb) |
| { |
| unsigned long phys, virt, size, mode, data, mask; |
| unsigned int i; |
| |
| for (i = 0; i < 64; i++) { |
| data = spitfire_get_dtlb_data(i); |
| if (data & SPITFIRE_TTE_VALID) { |
| switch ((data >> 61) & 3) { |
| default: |
| case 0x0: /* 8k */ |
| mask = 0xffffffffffffe000ULL; |
| size = PAGE_SIZE_8K; |
| break; |
| case 0x1: /* 64k */ |
| mask = 0xffffffffffff0000ULL; |
| size = PAGE_SIZE_64K; |
| break; |
| case 0x2: /* 512k */ |
| mask = 0xfffffffffff80000ULL; |
| size = PAGE_SIZE_512K; |
| break; |
| case 0x3: /* 4M */ |
| mask = 0xffffffffffc00000ULL; |
| size = PAGE_SIZE_4M; |
| break; |
| } |
| |
| virt = spitfire_get_dtlb_tag(i); |
| virt &= mask; |
| |
| /* extract 41bit physical address */ |
| phys = data & 0x000001fffffff000ULL; |
| phys &= mask; |
| |
| mode = data & 0xfff; |
| |
| cb(phys, virt, size, mode); |
| } |
| } |
| } |
| |
| /* |
| 3.6.5 translate |
| ( virt -- false | phys.lo ... phys.hi mode true ) |
| */ |
| static void |
| mmu_translate(void) |
| { |
| ucell virt, mode; |
| phys_addr_t phys; |
| |
| virt = POP(); |
| |
| phys = ofmem_translate(virt, &mode); |
| |
| if (phys != -1UL) { |
| PUSH(phys & 0xffffffff); |
| PUSH(phys >> 32); |
| PUSH(mode); |
| PUSH(-1); |
| } |
| else { |
| PUSH(0); |
| } |
| } |
| |
| /* |
| * D5.3 pgmap@ ( va -- tte ) |
| */ |
| static void |
| pgmap_fetch(void) |
| { |
| unsigned long va, tte_data; |
| |
| va = POP(); |
| |
| tte_data = find_tte(va); |
| if (tte_data == -1) |
| goto error; |
| |
| /* return tte_data */ |
| PUSH(tte_data); |
| return; |
| |
| error: |
| /* If we get here, there was no entry */ |
| PUSH(0); |
| } |
| |
| /* |
| ( index tte_data vaddr -- ? ) |
| */ |
| static void |
| dtlb_load(void) |
| { |
| unsigned long vaddr, tte_data, idx; |
| |
| vaddr = POP(); |
| tte_data = POP(); |
| idx = POP(); |
| dtlb_load3(vaddr, tte_data, idx); |
| } |
| |
| /* MMU D-TLB miss handler */ |
| void |
| dtlb_miss_handler(void) |
| { |
| unsigned long faultva, tte_data = 0; |
| |
| /* Grab fault address from MMU and round to nearest 8k page */ |
| faultva = dtlb_faultva(); |
| faultva >>= 13; |
| faultva <<= 13; |
| |
| /* If a valid va>tte-data routine has been set, invoke that Forth xt instead */ |
| if (va2ttedata && *va2ttedata != 0) { |
| |
| /* va>tte-data ( addr cnum -- false | tte-data true ) */ |
| PUSH(faultva); |
| PUSH(0); |
| enterforth(*va2ttedata); |
| |
| /* Check the result first... */ |
| tte_data = POP(); |
| if (!tte_data) { |
| bug(); |
| } else { |
| /* Grab the real data */ |
| tte_data = POP(); |
| } |
| } else { |
| /* Search the ofmem linked list for this virtual address */ |
| tte_data = find_tte(faultva); |
| } |
| |
| if (tte_data) { |
| /* Update MMU */ |
| dtlb_load2(faultva, tte_data); |
| } else { |
| /* If we got here, there was no translation so fail */ |
| bug(); |
| } |
| |
| } |
| |
| /* |
| ( index tte_data vaddr -- ? ) |
| */ |
| static void |
| itlb_load(void) |
| { |
| unsigned long vaddr, tte_data, idx; |
| |
| vaddr = POP(); |
| tte_data = POP(); |
| idx = POP(); |
| itlb_load3(vaddr, tte_data, idx); |
| } |
| |
| /* MMU I-TLB miss handler */ |
| void |
| itlb_miss_handler(void) |
| { |
| unsigned long faultva, tte_data = 0; |
| |
| /* Grab fault address from MMU and round to nearest 8k page */ |
| faultva = itlb_faultva(); |
| faultva >>= 13; |
| faultva <<= 13; |
| |
| /* If a valid va>tte-data routine has been set, invoke that Forth xt instead */ |
| if (va2ttedata && *va2ttedata != 0) { |
| |
| /* va>tte-data ( addr cnum -- false | tte-data true ) */ |
| PUSH(faultva); |
| PUSH(0); |
| enterforth(*va2ttedata); |
| |
| /* Check the result first... */ |
| tte_data = POP(); |
| if (!tte_data) { |
| bug(); |
| } else { |
| /* Grab the real data */ |
| tte_data = POP(); |
| } |
| } else { |
| /* Search the ofmem linked list for this virtual address */ |
| tte_data = find_tte(faultva); |
| } |
| |
| if (tte_data) { |
| /* Update MMU */ |
| itlb_load2(faultva, tte_data); |
| } else { |
| /* If we got here, there was no translation so fail */ |
| bug(); |
| } |
| } |
| |
| void |
| prom_debug_handler(void) |
| { |
| /* Execute the current debugger-hook */ |
| feval("debugger-hook"); |
| } |
| |
| /* |
| 3.6.5 map |
| ( phys.lo ... phys.hi virt size mode -- ) |
| */ |
| static void |
| mmu_map(void) |
| { |
| ucell virt, size, mode; |
| phys_addr_t phys; |
| |
| mode = POP(); |
| size = POP(); |
| virt = POP(); |
| phys = POP(); |
| phys <<= 32; |
| phys |= POP(); |
| |
| ofmem_map(phys, virt, size, mode); |
| } |
| |
| /* |
| 3.6.5 unmap |
| ( virt size -- ) |
| */ |
| static void |
| mmu_unmap(void) |
| { |
| ucell virt, size; |
| |
| size = POP(); |
| virt = POP(); |
| ofmem_unmap(virt, size); |
| } |
| |
| /* |
| 3.6.5 claim |
| ( virt size align -- base ) |
| */ |
| static void |
| mmu_claim(void) |
| { |
| ucell virt=-1UL, size, align; |
| |
| align = POP(); |
| size = POP(); |
| if (!align) { |
| virt = POP(); |
| } |
| |
| virt = ofmem_claim_virt(virt, size, align); |
| |
| PUSH(virt); |
| } |
| |
| /* |
| 3.6.5 release |
| ( virt size -- ) |
| */ |
| static void |
| mmu_release(void) |
| { |
| ucell virt, size; |
| |
| size = POP(); |
| virt = POP(); |
| |
| ofmem_release_virt(virt, size); |
| } |
| |
| /* ( phys size align --- base ) */ |
| static void |
| mem_claim( void ) |
| { |
| ucell size, align; |
| phys_addr_t phys=-1UL; |
| |
| align = POP(); |
| size = POP(); |
| if (!align) { |
| phys = POP(); |
| phys <<= 32; |
| phys |= POP(); |
| } |
| |
| phys = ofmem_claim_phys(phys, size, align); |
| |
| PUSH(phys & 0xffffffffUL); |
| PUSH(phys >> 32); |
| } |
| |
| /* ( phys size --- ) */ |
| static void |
| mem_release( void ) |
| { |
| phys_addr_t phys; |
| ucell size; |
| |
| size = POP(); |
| phys = POP(); |
| phys <<= 32; |
| phys |= POP(); |
| |
| ofmem_release_phys(phys, size); |
| } |
| |
| /* ( name-cstr phys size align --- phys ) */ |
| static void |
| mem_retain ( void ) |
| { |
| ucell size, align; |
| phys_addr_t phys=-1UL; |
| |
| align = POP(); |
| size = POP(); |
| if (!align) { |
| phys = POP(); |
| phys <<= 32; |
| phys |= POP(); |
| } |
| |
| /* Currently do nothing with the name */ |
| POP(); |
| |
| phys = ofmem_retain(phys, size, align); |
| |
| PUSH(phys & 0xffffffffUL); |
| PUSH(phys >> 32); |
| } |
| |
| /* ( virt size align -- baseaddr|-1 ) */ |
| static void |
| ciface_claim( void ) |
| { |
| ucell align = POP(); |
| ucell size = POP(); |
| ucell virt = POP(); |
| ucell ret = ofmem_claim( virt, size, align ); |
| |
| /* printk("ciface_claim: %08x %08x %x\n", virt, size, align ); */ |
| PUSH( ret ); |
| } |
| |
| /* ( virt size -- ) */ |
| static void |
| ciface_release( void ) |
| { |
| ucell size = POP(); |
| ucell virt = POP(); |
| ofmem_release(virt, size); |
| } |
| |
| DECLARE_NODE(memory, INSTALL_OPEN, 0, "/memory"); |
| |
| NODE_METHODS( memory ) = { |
| { "claim", mem_claim }, |
| { "release", mem_release }, |
| { "SUNW,retain", mem_retain }, |
| }; |
| |
| DECLARE_NODE(mmu, INSTALL_OPEN, 0, "/virtual-memory"); |
| |
| NODE_METHODS(mmu) = { |
| { "open", mmu_open }, |
| { "close", mmu_close }, |
| { "translate", mmu_translate }, |
| { "SUNW,dtlb-load", dtlb_load }, |
| { "SUNW,itlb-load", itlb_load }, |
| { "map", mmu_map }, |
| { "unmap", mmu_unmap }, |
| { "claim", mmu_claim }, |
| { "release", mmu_release }, |
| }; |
| |
| void ob_mmu_init(const char *cpuname, uint64_t ram_size) |
| { |
| /* memory node */ |
| REGISTER_NODE(memory); |
| |
| /* MMU node */ |
| REGISTER_NODE(mmu); |
| |
| ofmem_register(find_dev("/memory"), find_dev("/virtual-memory")); |
| |
| push_str("/chosen"); |
| fword("find-device"); |
| |
| push_str("/virtual-memory"); |
| fword("open-dev"); |
| fword("encode-int"); |
| push_str("mmu"); |
| fword("property"); |
| |
| push_str("/memory"); |
| fword("find-device"); |
| |
| /* All memory: 0 to RAM_size */ |
| PUSH(0); |
| fword("encode-int"); |
| PUSH(0); |
| fword("encode-int"); |
| fword("encode+"); |
| PUSH((int)(ram_size >> 32)); |
| fword("encode-int"); |
| fword("encode+"); |
| PUSH((int)(ram_size & 0xffffffff)); |
| fword("encode-int"); |
| fword("encode+"); |
| push_str("reg"); |
| fword("property"); |
| |
| push_str("/openprom/client-services"); |
| fword("find-device"); |
| bind_func("cif-claim", ciface_claim); |
| bind_func("cif-release", ciface_release); |
| |
| /* Other MMU functions */ |
| PUSH(0); |
| fword("active-package!"); |
| bind_func("pgmap@", pgmap_fetch); |
| |
| /* Find address of va2ttedata defer word contents for MMU miss handlers */ |
| va2ttedata = (ucell *)findword("va>tte-data"); |
| va2ttedata++; |
| } |