| /* |
| * <ofmem_sparc64.c> |
| * |
| * OF Memory manager |
| * |
| * Copyright (C) 1999-2004 Samuel Rydh (samuel@ibrium.se) |
| * Copyright (C) 2004 Stefan Reinauer |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation |
| * |
| */ |
| |
| #include "config.h" |
| #include "libopenbios/bindings.h" |
| #include "libc/string.h" |
| #include "arch/sparc64/ofmem_sparc64.h" |
| #include "spitfire.h" |
| |
| #define OF_MALLOC_BASE ((char*)OFMEM + ALIGN_SIZE(sizeof(ofmem_t), 8)) |
| |
| #define MEMSIZE (192 * 1024) |
| static union { |
| char memory[MEMSIZE]; |
| ofmem_t ofmem; |
| } s_ofmem_data; |
| |
| #define OFMEM (&s_ofmem_data.ofmem) |
| #define TOP_OF_RAM (s_ofmem_data.memory + MEMSIZE) |
| |
| static retain_t s_retained; |
| translation_t **g_ofmem_translations = &s_ofmem_data.ofmem.trans; |
| |
| ucell *va2ttedata = 0; |
| extern uint64_t qemu_mem_size; |
| |
| static inline size_t ALIGN_SIZE(size_t x, size_t a) |
| { |
| return (x + a - 1) & ~(a-1); |
| } |
| |
| static ucell get_heap_top( void ) |
| { |
| return (ucell)TOP_OF_RAM; |
| } |
| |
| ofmem_t* ofmem_arch_get_private(void) |
| { |
| return OFMEM; |
| } |
| |
| void* ofmem_arch_get_malloc_base(void) |
| { |
| return OF_MALLOC_BASE; |
| } |
| |
| ucell ofmem_arch_get_heap_top(void) |
| { |
| return get_heap_top(); |
| } |
| |
| ucell ofmem_arch_get_virt_top(void) |
| { |
| return (ucell)OFMEM_VIRT_TOP; |
| } |
| |
| ucell ofmem_arch_get_iomem_base(void) |
| { |
| return (ucell)&_iomem; |
| } |
| |
| ucell ofmem_arch_get_iomem_top(void) |
| { |
| return (ucell)&_iomem + 0x8000; |
| } |
| |
| retain_t *ofmem_arch_get_retained(void) |
| { |
| return (&s_retained); |
| } |
| |
| int ofmem_arch_get_translation_entry_size(void) |
| { |
| /* Return size of a single MMU package translation property entry in cells */ |
| return 3; |
| } |
| |
| void ofmem_arch_create_translation_entry(ucell *transentry, translation_t *t) |
| { |
| /* Generate translation property entry for SPARC. While there is no |
| formal documentation for this, both Linux kernel and OpenSolaris sources |
| expect a translation property entry to have the following layout: |
| |
| virtual address |
| length |
| mode (valid TTE for start of translation region) |
| */ |
| |
| transentry[0] = t->virt; |
| transentry[1] = t->size; |
| transentry[2] = t->phys | t->mode | SPITFIRE_TTE_VALID; |
| } |
| |
| /* Return the size of a memory available entry given the phandle in cells */ |
| int ofmem_arch_get_available_entry_size(phandle_t ph) |
| { |
| if (ph == s_phandle_memory) { |
| return 1 + ofmem_arch_get_physaddr_cellsize(); |
| } else { |
| return 1 + 1; |
| } |
| } |
| |
| /* Generate memory available property entry for Sparc64 */ |
| void ofmem_arch_create_available_entry(phandle_t ph, ucell *availentry, phys_addr_t start, ucell size) |
| { |
| int i = 0; |
| |
| if (ph == s_phandle_memory) { |
| i += ofmem_arch_encode_physaddr(availentry, start); |
| } else { |
| availentry[i++] = start; |
| } |
| |
| availentry[i] = size; |
| } |
| |
| /* Unmap a set of pages */ |
| void ofmem_arch_unmap_pages(ucell virt, ucell size) |
| { |
| ucell va; |
| |
| /* align address to 8k */ |
| virt &= ~PAGE_MASK_8K; |
| |
| /* align size to 8k */ |
| size = (size + PAGE_MASK_8K) & ~PAGE_MASK_8K; |
| |
| for (va = virt; va < virt + size; va += PAGE_SIZE_8K) { |
| itlb_demap(va); |
| dtlb_demap(va); |
| } |
| } |
| |
| /* Map a set of pages */ |
| void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode) |
| { |
| unsigned long tte_data, currsize; |
| |
| /* Install locked tlb entries now */ |
| if (mode & SPITFIRE_TTE_LOCKED) { |
| |
| /* aligned to 8k page */ |
| size = (size + PAGE_MASK_8K) & ~PAGE_MASK_8K; |
| |
| while (size > 0) { |
| currsize = size; |
| if (currsize >= PAGE_SIZE_4M && |
| (virt & PAGE_MASK_4M) == 0 && |
| (phys & PAGE_MASK_4M) == 0) { |
| currsize = PAGE_SIZE_4M; |
| tte_data = 6ULL << 60; |
| } else if (currsize >= PAGE_SIZE_512K && |
| (virt & PAGE_MASK_512K) == 0 && |
| (phys & PAGE_MASK_512K) == 0) { |
| currsize = PAGE_SIZE_512K; |
| tte_data = 4ULL << 60; |
| } else if (currsize >= PAGE_SIZE_64K && |
| (virt & PAGE_MASK_64K) == 0 && |
| (phys & PAGE_MASK_64K) == 0) { |
| currsize = PAGE_SIZE_64K; |
| tte_data = 2ULL << 60; |
| } else { |
| currsize = PAGE_SIZE_8K; |
| tte_data = 0; |
| } |
| |
| tte_data |= phys | mode | SPITFIRE_TTE_VALID; |
| |
| itlb_load2(virt, tte_data); |
| dtlb_load2(virt, tte_data); |
| |
| size -= currsize; |
| phys += currsize; |
| virt += currsize; |
| } |
| } |
| } |
| |
| /************************************************************************/ |
| /* misc */ |
| /************************************************************************/ |
| |
| int ofmem_arch_get_physaddr_cellsize(void) |
| { |
| return 1; |
| } |
| |
| int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value) |
| { |
| p[0] = value; |
| return 1; |
| } |
| |
| ucell ofmem_arch_default_translation_mode( phys_addr_t phys ) |
| { |
| /* Writable, cacheable */ |
| /* Privileged and not locked */ |
| return SPITFIRE_TTE_CP | SPITFIRE_TTE_CV | SPITFIRE_TTE_WRITABLE | SPITFIRE_TTE_PRIVILEGED; |
| } |
| |
| ucell ofmem_arch_io_translation_mode( phys_addr_t phys ) |
| { |
| /* Writable, privileged and not locked */ |
| return SPITFIRE_TTE_CV | SPITFIRE_TTE_WRITABLE | SPITFIRE_TTE_PRIVILEGED | SPITFIRE_TTE_EFFECT; |
| } |
| |
| /* Architecture-specific OFMEM helpers */ |
| unsigned long |
| find_tte(unsigned long va) |
| { |
| translation_t *t = *g_ofmem_translations; |
| unsigned long tte_data; |
| |
| /* Search the ofmem linked list for this virtual address */ |
| while (t != NULL) { |
| /* Find the correct range */ |
| if (va >= t->virt && va < (t->virt + t->size)) { |
| |
| /* valid tte, 8k size */ |
| tte_data = SPITFIRE_TTE_VALID; |
| |
| /* mix in phys address mode */ |
| tte_data |= t->mode; |
| |
| /* mix in page physical address = t->phys + offset */ |
| tte_data |= t->phys + (va - t->virt); |
| |
| /* return tte_data */ |
| return tte_data; |
| } |
| t = t->next; |
| } |
| |
| /* Couldn't find tte */ |
| return -1; |
| } |
| |
| /* ITLB handlers */ |
| void |
| itlb_load2(unsigned long vaddr, unsigned long tte_data) |
| { |
| asm("stxa %0, [%1] %2\n" |
| "stxa %3, [%%g0] %4\n" |
| : : "r" (vaddr), "r" (48), "i" (ASI_IMMU), |
| "r" (tte_data), "i" (ASI_ITLB_DATA_IN)); |
| } |
| |
| void |
| itlb_load3(unsigned long vaddr, unsigned long tte_data, |
| unsigned long tte_index) |
| { |
| asm("stxa %0, [%1] %2\n" |
| "stxa %3, [%4] %5\n" |
| : : "r" (vaddr), "r" (48), "i" (ASI_IMMU), |
| "r" (tte_data), "r" (tte_index << 3), "i" (ASI_ITLB_DATA_ACCESS)); |
| } |
| |
| unsigned long |
| itlb_faultva(void) |
| { |
| unsigned long faultva; |
| |
| asm("ldxa [%1] %2, %0\n" |
| : "=r" (faultva) |
| : "r" (48), "i" (ASI_IMMU)); |
| |
| return faultva; |
| } |
| |
| void |
| itlb_demap(unsigned long vaddr) |
| { |
| asm("stxa %0, [%0] %1\n" |
| : : "r" (vaddr), "i" (ASI_IMMU_DEMAP)); |
| } |
| |
| /* DTLB handlers */ |
| void |
| dtlb_load2(unsigned long vaddr, unsigned long tte_data) |
| { |
| asm("stxa %0, [%1] %2\n" |
| "stxa %3, [%%g0] %4\n" |
| : : "r" (vaddr), "r" (48), "i" (ASI_DMMU), |
| "r" (tte_data), "i" (ASI_DTLB_DATA_IN)); |
| } |
| |
| void |
| dtlb_load3(unsigned long vaddr, unsigned long tte_data, |
| unsigned long tte_index) |
| { |
| asm("stxa %0, [%1] %2\n" |
| "stxa %3, [%4] %5\n" |
| : : "r" (vaddr), "r" (48), "i" (ASI_DMMU), |
| "r" (tte_data), "r" (tte_index << 3), "i" (ASI_DTLB_DATA_ACCESS)); |
| } |
| |
| unsigned long |
| dtlb_faultva(void) |
| { |
| unsigned long faultva; |
| |
| asm("ldxa [%1] %2, %0\n" |
| : "=r" (faultva) |
| : "r" (48), "i" (ASI_DMMU)); |
| |
| return faultva; |
| } |
| |
| void |
| dtlb_demap(unsigned long vaddr) |
| { |
| asm("stxa %0, [%0] %1\n" |
| : : "r" (vaddr), "i" (ASI_DMMU_DEMAP)); |
| } |
| |
| /************************************************************************/ |
| /* init / cleanup */ |
| /************************************************************************/ |
| |
| static int remap_page_range( phys_addr_t phys, ucell virt, ucell size, ucell mode ) |
| { |
| ofmem_claim_phys(phys, size, 0); |
| ofmem_claim_virt(virt, size, 0); |
| ofmem_map_page_range(phys, virt, size, mode); |
| if (!(mode & SPITFIRE_TTE_LOCKED)) { |
| OFMEM_TRACE("remap_page_range clearing translation " FMT_ucellx |
| " -> " FMT_ucellx " " FMT_ucellx " mode " FMT_ucellx "\n", |
| virt, phys, size, mode ); |
| ofmem_arch_unmap_pages(virt, size); |
| } |
| return 0; |
| } |
| |
| #define RETAIN_MAGIC 0x1100220033004400 |
| |
| void ofmem_init( void ) |
| { |
| retain_t *retained = ofmem_arch_get_retained(); |
| int i; |
| |
| memset(&s_ofmem_data, 0, sizeof(s_ofmem_data)); |
| s_ofmem_data.ofmem.ramsize = qemu_mem_size; |
| |
| /* inherit translations set up by entry.S */ |
| ofmem_walk_boot_map(remap_page_range); |
| |
| /* Map the memory */ |
| ofmem_map_page_range(PAGE_SIZE, PAGE_SIZE, 0x800000, 0x36); |
| |
| if (!(retained->magic == RETAIN_MAGIC)) { |
| OFMEM_TRACE("ofmem_init: no retained magic found, creating\n"); |
| retained->magic = RETAIN_MAGIC; |
| retained->numentries = 0; |
| } else { |
| OFMEM_TRACE("ofmem_init: retained magic found, total %lld mappings\n", retained->numentries); |
| |
| /* Mark physical addresses as used so they are not reallocated */ |
| for (i = 0; i < retained->numentries; i++) { |
| ofmem_claim_phys(retained->retain_phys_range[i].start, |
| retained->retain_phys_range[i].size, 0); |
| } |
| |
| /* Reset retained area for next reset */ |
| retained->magic = RETAIN_MAGIC; |
| retained->numentries = 0; |
| } |
| } |