| /* |
| * <ofmem_sparc32.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/sparc32/ofmem_sparc32.h" |
| #include "asm/asi.h" |
| #include "pgtsrmmu.h" |
| |
| #define OF_MALLOC_BASE ((char*)OFMEM + ALIGN_SIZE(sizeof(ofmem_t), 8)) |
| |
| #define MEMSIZE (256 * 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) |
| |
| #define OFMEM_PHYS_RESERVED 0x1000000 |
| |
| translation_t **g_ofmem_translations = &s_ofmem_data.ofmem.trans; |
| |
| extern uint32_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 pointer2cell(&_end); |
| } |
| |
| ucell ofmem_arch_get_iomem_top(void) |
| { |
| return pointer2cell(&_iomem); |
| } |
| |
| retain_t *ofmem_arch_get_retained(void) |
| { |
| /* Not used */ |
| return 0; |
| } |
| |
| int ofmem_arch_get_physaddr_cellsize(void) |
| { |
| return 2; |
| } |
| |
| int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value) |
| { |
| int n = 0; |
| |
| p[n++] = value >> 32; |
| p[n++] = value; |
| |
| return n; |
| } |
| |
| 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 |
| */ |
| |
| transentry[0] = t->virt; |
| transentry[1] = t->size; |
| transentry[2] = t->mode; |
| } |
| |
| /* Return the size of a memory available entry given the phandle in cells */ |
| int ofmem_arch_get_available_entry_size(phandle_t ph) |
| { |
| return 1 + ofmem_arch_get_physaddr_cellsize(); |
| } |
| |
| /* Generate memory available property entry for Sparc32 */ |
| void ofmem_arch_create_available_entry(phandle_t ph, ucell *availentry, phys_addr_t start, ucell size) |
| { |
| int i = 0; |
| |
| i += ofmem_arch_encode_physaddr(availentry, start); |
| availentry[i] = size; |
| } |
| |
| /* Unmap a set of pages */ |
| void ofmem_arch_unmap_pages(ucell virt, ucell size) |
| { |
| unsigned long pa; |
| ucell i; |
| |
| for (i = 0; i < size; i += PAGE_SIZE) { |
| pa = find_pte(virt, 0); |
| *(uint32_t *)pa = 0; |
| virt += PAGE_SIZE; |
| } |
| |
| srmmu_flush_whole_tlb(); |
| } |
| |
| /* Map a set of pages */ |
| void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode) |
| { |
| unsigned long npages, off; |
| uint32_t pte; |
| unsigned long pa; |
| |
| off = phys & (PAGE_SIZE - 1); |
| npages = (off + (size - 1) + (PAGE_SIZE - 1)) / PAGE_SIZE; |
| phys &= ~(uint64_t)(PAGE_SIZE - 1); |
| |
| while (npages-- != 0) { |
| pa = find_pte(virt, 1); |
| |
| pte = SRMMU_ET_PTE | ((phys & PAGE_MASK) >> 4); |
| pte |= mode; |
| |
| *(uint32_t *)pa = pte; |
| |
| virt += PAGE_SIZE; |
| phys += PAGE_SIZE; |
| } |
| } |
| |
| /* Architecture-specific OFMEM helpers */ |
| unsigned long |
| find_pte(unsigned long va, int alloc) |
| { |
| uint32_t pte; |
| void *p; |
| unsigned long pa; |
| int ret; |
| |
| pte = l1[(va >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1)]; |
| if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) { |
| if (alloc) { |
| ret = ofmem_posix_memalign(&p, SRMMU_PTRS_PER_PMD * sizeof(int), |
| SRMMU_PTRS_PER_PMD * sizeof(int)); |
| if (ret != 0) |
| return ret; |
| pte = SRMMU_ET_PTD | ((va2pa((unsigned long)p)) >> 4); |
| l1[(va >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1)] = pte; |
| /* barrier() */ |
| } else { |
| return -1; |
| } |
| } |
| |
| pa = (pte & 0xFFFFFFF0) << 4; |
| pa += ((va >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)) << 2; |
| pte = *(uint32_t *)pa2va(pa); |
| if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) { |
| if (alloc) { |
| ret = ofmem_posix_memalign(&p, SRMMU_PTRS_PER_PTE * sizeof(void *), |
| SRMMU_PTRS_PER_PTE * sizeof(void *)); |
| if (ret != 0) |
| return ret; |
| pte = SRMMU_ET_PTD | ((va2pa((unsigned int)p)) >> 4); |
| *(uint32_t *)pa2va(pa) = pte; |
| } else { |
| return -2; |
| } |
| } |
| |
| pa = (pte & 0xFFFFFFF0) << 4; |
| pa += ((va >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)) << 2; |
| |
| return pa2va(pa); |
| } |
| |
| /************************************************************************/ |
| /* misc */ |
| /************************************************************************/ |
| |
| ucell ofmem_arch_default_translation_mode( phys_addr_t phys ) |
| { |
| return SRMMU_REF | SRMMU_CACHE | SRMMU_PRIV; |
| } |
| |
| ucell ofmem_arch_io_translation_mode( phys_addr_t phys ) |
| { |
| return SRMMU_REF | SRMMU_PRIV; |
| } |
| |
| /************************************************************************/ |
| /* init / cleanup */ |
| /************************************************************************/ |
| |
| void ofmem_init( void ) |
| { |
| memset(&s_ofmem_data, 0, sizeof(s_ofmem_data)); |
| s_ofmem_data.ofmem.ramsize = qemu_mem_size; |
| |
| /* Mark the first page as non-free */ |
| ofmem_claim_virt(0, PAGE_SIZE, 0); |
| |
| /* Claim reserved physical addresses at top of RAM */ |
| ofmem_claim_phys(s_ofmem_data.ofmem.ramsize - OFMEM_PHYS_RESERVED, OFMEM_PHYS_RESERVED, 0); |
| |
| /* Claim OpenBIOS reserved space */ |
| ofmem_claim_virt(0xffd00000, 0x200000, 0); |
| } |