diff options
Diffstat (limited to 'qemu/roms/openbios/arch/sparc64/ofmem_sparc64.c')
-rw-r--r-- | qemu/roms/openbios/arch/sparc64/ofmem_sparc64.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/sparc64/ofmem_sparc64.c b/qemu/roms/openbios/arch/sparc64/ofmem_sparc64.c new file mode 100644 index 000000000..bdfaf5636 --- /dev/null +++ b/qemu/roms/openbios/arch/sparc64/ofmem_sparc64.c @@ -0,0 +1,381 @@ +/* + * <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 (128 * 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)TOP_OF_RAM; +} + +ucell ofmem_arch_get_iomem_base(void) +{ + /* Currently unused */ + return 0; +} + +ucell ofmem_arch_get_iomem_top(void) +{ + /* Currently unused */ + return 0; +} + +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; +} + +/* 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; + } +} |