X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Froms%2Fopenbios%2Farch%2Fppc%2Fqemu%2Fofmem.c;fp=qemu%2Froms%2Fopenbios%2Farch%2Fppc%2Fqemu%2Fofmem.c;h=6f346a3d4d241e83271a97fdb62156419aa36f25;hb=e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb;hp=0000000000000000000000000000000000000000;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/qemu/roms/openbios/arch/ppc/qemu/ofmem.c b/qemu/roms/openbios/arch/ppc/qemu/ofmem.c new file mode 100644 index 000000000..6f346a3d4 --- /dev/null +++ b/qemu/roms/openbios/arch/ppc/qemu/ofmem.c @@ -0,0 +1,563 @@ +/* + * Creation Date: <1999/11/07 19:02:11 samuel> + * Time-stamp: <2004/01/07 19:42:36 samuel> + * + * + * + * 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 "libopenbios/ofmem.h" +#include "kernel.h" +#include "mmutypes.h" +#include "asm/processor.h" + +#define BIT(n) (1U << (31 - (n))) + +#define SLB_VSID_SHIFT 12 + +/* called from assembly */ +extern void dsi_exception(void); +extern void isi_exception(void); +extern void setup_mmu(unsigned long code_base); + +/* + * From Apple's BootX source comments: + * + * 96 MB map (currently unused - 4363357 tracks re-adoption) + * 00000000 - 00003FFF : Exception Vectors + * 00004000 - 03FFFFFF : Kernel Image, Boot Struct and Drivers (~64 MB) + * 04000000 - 04FFFFFF : File Load Area (16 MB) [80 MB] + * 05000000 - 053FFFFF : FS Cache (4 MB) [84 MB] + * 05400000 - 055FFFFF : Malloc Zone (2 MB) [86 MB] + * 05600000 - 057FFFFF : BootX Image (2 MB) [88 MB] + * 05800000 - 05FFFFFF : Unused/OF (8 MB) [96 MB] + * + */ + +#define FREE_BASE 0x00004000UL +#define OF_CODE_START 0xfff00000UL +#define OF_CODE_SIZE 0x00100000 +#define IO_BASE 0x80000000UL + +#ifdef __powerpc64__ +#define HASH_BITS 18 +#else +#define HASH_BITS 15 +#endif +#define HASH_SIZE (2 << HASH_BITS) +#define OFMEM_SIZE (1 * 1024 * 1024 + 512 * 1024) + +#define SEGR_USER BIT(2) +#define SEGR_BASE 0x0400 + +static inline unsigned long +get_hash_base(void) +{ + return (mfsdr1() & SDR1_HTABORG_MASK); +} + +static inline unsigned long +get_rom_base(void) +{ + ofmem_t *ofmem = ofmem_arch_get_private(); + return ofmem->ramsize - OF_CODE_SIZE; +} + +static unsigned long +get_ram_top(void) +{ + return get_hash_base() - (32 + 64 + 64) * 1024 - OFMEM_SIZE; +} + +static unsigned long +get_ram_bottom(void) +{ + return FREE_BASE; +} + +static unsigned long get_heap_top(void) +{ + return get_hash_base() - (32 + 64 + 64) * 1024; +} + +static inline size_t ALIGN_SIZE(size_t x, size_t a) +{ + return (x + a - 1) & ~(a - 1); +} + +ofmem_t* ofmem_arch_get_private(void) +{ + return (ofmem_t*)cell2pointer(get_heap_top() - OFMEM_SIZE); +} + +void* ofmem_arch_get_malloc_base(void) +{ + return (char*)ofmem_arch_get_private() + ALIGN_SIZE(sizeof(ofmem_t), 4); +} + +ucell ofmem_arch_get_heap_top(void) +{ + return get_heap_top(); +} + +ucell ofmem_arch_get_virt_top(void) +{ + return IO_BASE; +} + +void ofmem_arch_unmap_pages(ucell virt, ucell size) +{ + /* kill page mappings in provided range */ +} + +void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode) +{ + /* none yet */ +} + +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) +{ + /* not implemented */ + return NULL; +} + +int ofmem_arch_get_physaddr_cellsize(void) +{ +#ifdef CONFIG_PPC64 + return 2; +#else + return 1; +#endif +} + +int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value) +{ + int n = 0; +#ifdef CONFIG_PPC64 + p[n++] = value >> 32; +#endif + p[n++] = value; + return n; +} + +/* Return size of a single MMU package translation property entry in cells */ +int ofmem_arch_get_translation_entry_size(void) +{ + return 3 + ofmem_arch_get_physaddr_cellsize(); +} + +/* Generate translation property entry for PPC. + * According to the platform bindings for PPC + * (http://www.openfirmware.org/1275/bindings/ppc/release/ppc-2_1.html#REF34579) + * a translation property entry has the following layout: + * + * virtual address + * length + * physical address + * mode + */ +void ofmem_arch_create_translation_entry(ucell *transentry, translation_t *t) +{ + int i = 0; + + transentry[i++] = t->virt; + transentry[i++] = t->size; + i += ofmem_arch_encode_physaddr(&transentry[i], t->phys); + transentry[i++] = 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) +{ + if (ph == s_phandle_memory) { + return 1 + ofmem_arch_get_physaddr_cellsize(); + } else { + return 1 + 1; + } +} + +/* Generate memory available property entry for PPC */ +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; +} + +/************************************************************************/ +/* OF private allocations */ +/************************************************************************/ + +/* Private functions for mapping between physical/virtual addresses */ +phys_addr_t +va2pa(unsigned long va) +{ + if (va >= OF_CODE_START && va < OF_CODE_START + OF_CODE_SIZE) { + return (phys_addr_t)get_rom_base() - OF_CODE_START + va; + } else { + return (phys_addr_t)va; + } +} + +unsigned long +pa2va(phys_addr_t pa) +{ + if ((pa - get_rom_base() + OF_CODE_START >= OF_CODE_START) && + (pa - get_rom_base() + OF_CODE_START < OF_CODE_START + OF_CODE_SIZE)) + return (unsigned long)pa - get_rom_base() + OF_CODE_START; + else + return (unsigned long)pa; +} + +void * +malloc(int size) +{ + return ofmem_malloc(size); +} + +void +free(void *ptr) +{ + ofmem_free(ptr); +} + +void * +realloc(void *ptr, size_t size) +{ + return ofmem_realloc(ptr, size); +} + + +/************************************************************************/ +/* misc */ +/************************************************************************/ + +ucell ofmem_arch_default_translation_mode(phys_addr_t phys) +{ + /* XXX: Guard bit not set as it should! */ + if (phys < IO_BASE) + return 0x02; /*0xa*/ /* wim GxPp */ + return 0x6a; /* WIm GxPp, I/O */ +} + +ucell ofmem_arch_io_translation_mode(phys_addr_t phys) +{ + return 0x6a; /* WIm GxPp, I/O */ +} + +/************************************************************************/ +/* page fault handler */ +/************************************************************************/ + +static phys_addr_t +ea_to_phys(unsigned long ea, ucell *mode) +{ + phys_addr_t phys; + + if (ea >= OF_CODE_START && ea <= 0xffffffffUL) { + /* ROM into RAM */ + ea -= OF_CODE_START; + phys = get_rom_base() + ea; + *mode = 0x02; + return phys; + } + + phys = ofmem_translate(ea, mode); + if (phys == -1) { + phys = ea; + *mode = ofmem_arch_default_translation_mode(phys); + + /* print_virt_range(); */ + /* print_phys_range(); */ + /* print_trans(); */ + } + return phys; +} + +/* Converts a global variable (from .data or .bss) into a pointer that + can be accessed from real mode */ +static void * +global_ptr_real(void *p) +{ + return (void*)((uintptr_t)p - OF_CODE_START + get_rom_base()); +} + +/* Return the next slot to evict, in the range of [0..7] */ +static int +next_evicted_slot(void) +{ + static int next_grab_slot; + int *next_grab_slot_va; + int r; + + next_grab_slot_va = global_ptr_real(&next_grab_slot); + r = *next_grab_slot_va; + *next_grab_slot_va = (r + 1) % 8; + + return r; +} + +static void +hash_page_64(unsigned long ea, phys_addr_t phys, ucell mode) +{ + uint64_t vsid_mask, page_mask, pgidx, hash; + uint64_t htab_mask, mask, avpn; + unsigned long pgaddr; + int i, found; + unsigned int vsid, vsid_sh, sdr, sdr_sh, sdr_mask; + mPTE_64_t *pp; + + vsid = (ea >> 28) + SEGR_BASE; + vsid_sh = 7; + vsid_mask = 0x00003FFFFFFFFF80ULL; + sdr = mfsdr1(); + sdr_sh = 18; + sdr_mask = 0x3FF80; + page_mask = 0x0FFFFFFF; // XXX correct? + pgidx = (ea & page_mask) >> PAGE_SHIFT; + avpn = (vsid << 12) | ((pgidx >> 4) & 0x0F80);; + + hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; + htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F)); + mask = (htab_mask << sdr_sh) | sdr_mask; + pgaddr = sdr | (hash & mask); + pp = (mPTE_64_t *)pgaddr; + + /* replace old translation */ + for (found = 0, i = 0; !found && i < 8; i++) + if (pp[i].avpn == avpn) + found = 1; + + /* otherwise use a free slot */ + for (i = 0; !found && i < 8; i++) + if (!pp[i].v) + found = 1; + + /* out of slots, just evict one */ + if (!found) + i = next_evicted_slot() + 1; + i--; + { + mPTE_64_t p = { + // .avpn_low = avpn, + .avpn = avpn >> 7, + .h = 0, + .v = 1, + + .rpn = (phys & ~0xfffUL) >> 12, + .r = mode & (1 << 8) ? 1 : 0, + .c = mode & (1 << 7) ? 1 : 0, + .w = mode & (1 << 6) ? 1 : 0, + .i = mode & (1 << 5) ? 1 : 0, + .m = mode & (1 << 4) ? 1 : 0, + .g = mode & (1 << 3) ? 1 : 0, + .n = mode & (1 << 2) ? 1 : 0, + .pp = mode & 3, + }; + pp[i] = p; + } + + asm volatile("tlbie %0" :: "r"(ea)); +} + +static void +hash_page_32(unsigned long ea, phys_addr_t phys, ucell mode) +{ +#ifndef __powerpc64__ + unsigned long *upte, cmp, hash1; + int i, vsid, found; + mPTE_t *pp; + + vsid = (ea >> 28) + SEGR_BASE; + cmp = BIT(0) | (vsid << 7) | ((ea & 0x0fffffff) >> 22); + + hash1 = vsid; + hash1 ^= (ea >> 12) & 0xffff; + hash1 &= (((mfsdr1() & 0x1ff) << 16) | 0xffff) >> 6; + + pp = (mPTE_t*)(get_hash_base() + (hash1 << 6)); + upte = (unsigned long*)pp; + + /* replace old translation */ + for (found = 0, i = 0; !found && i < 8; i++) + if (cmp == upte[i*2]) + found = 1; + + /* otherwise use a free slot */ + for (i = 0; !found && i < 8; i++) + if (!pp[i].v) + found = 1; + + /* out of slots, just evict one */ + if (!found) + i = next_evicted_slot() + 1; + i--; + upte[i * 2] = cmp; + upte[i * 2 + 1] = (phys & ~0xfff) | mode; + + asm volatile("tlbie %0" :: "r"(ea)); +#endif +} + +static int is_ppc64(void) +{ +#ifdef __powerpc64__ + return 1; +#elif defined(CONFIG_PPC_64BITSUPPORT) + unsigned int pvr = mfpvr(); + return ((pvr >= 0x330000) && (pvr < 0x70330000)); +#else + return 0; +#endif +} + +/* XXX Remove these ugly constructs when legacy 64-bit support is dropped. */ +static void hash_page(unsigned long ea, phys_addr_t phys, ucell mode) +{ + if (is_ppc64()) + hash_page_64(ea, phys, mode); + else + hash_page_32(ea, phys, mode); +} + +void +dsi_exception(void) +{ + unsigned long dar, dsisr; + ucell mode; + phys_addr_t phys; + + asm volatile("mfdar %0" : "=r" (dar) : ); + asm volatile("mfdsisr %0" : "=r" (dsisr) : ); + + phys = ea_to_phys(dar, &mode); + hash_page(dar, phys, mode); +} + +void +isi_exception(void) +{ + unsigned long nip, srr1; + ucell mode; + phys_addr_t phys; + + asm volatile("mfsrr0 %0" : "=r" (nip) : ); + asm volatile("mfsrr1 %0" : "=r" (srr1) : ); + + phys = ea_to_phys(nip, &mode); + hash_page(nip, phys, mode); +} + + +/************************************************************************/ +/* init / cleanup */ +/************************************************************************/ + +void +setup_mmu(unsigned long ramsize) +{ + ofmem_t *ofmem; +#ifndef __powerpc64__ + unsigned long sr_base; +#endif + unsigned long hash_base; + unsigned long hash_mask = ~0x000fffffUL; /* alignment for ppc64 */ + int i; + + /* SDR1: Storage Description Register 1 */ + + hash_base = (ramsize - OF_CODE_SIZE - HASH_SIZE) & hash_mask; + memset((void *)hash_base, 0, HASH_SIZE); + if (is_ppc64()) + mtsdr1(hash_base | MAX(HASH_BITS - 18, 0)); + else + mtsdr1(hash_base | ((HASH_SIZE - 1) >> 16)); + +#ifdef __powerpc64__ + + /* Segment Lookaside Buffer */ + + slbia(); /* Invalidate all SLBs except SLB 0 */ + for (i = 0; i < 16; i++) { + unsigned long rs = (0x400 + i) << SLB_VSID_SHIFT; + unsigned long rb = ((unsigned long)i << 28) | (1 << 27) | i; + slbmte(rs, rb); + } + +#else + + /* Segment Register */ + + sr_base = SEGR_USER | SEGR_BASE ; + for (i = 0; i < 16; i++) { + int j = i << 28; + asm volatile("mtsrin %0,%1" :: "r" (sr_base + i), "r" (j)); + } + +#endif + + ofmem = ofmem_arch_get_private(); + memset(ofmem, 0, sizeof(ofmem_t)); + ofmem->ramsize = ramsize; + + memcpy((void *)get_rom_base(), (void *)OF_CODE_START, OF_CODE_SIZE); + + /* Enable MMU */ + + mtmsr(mfmsr() | MSR_IR | MSR_DR); +} + +void +ofmem_init(void) +{ + ofmem_t *ofmem = ofmem_arch_get_private(); + + /* Map the memory (don't map page 0 to allow catching of NULL dereferences) */ + ofmem_claim_phys(PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0); + ofmem_claim_virt(PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0); + ofmem_map(PAGE_SIZE, PAGE_SIZE, get_ram_bottom() - PAGE_SIZE, 0); + + /* Mark the first page as non-free */ + ofmem_claim_phys(0, PAGE_SIZE, 0); + ofmem_claim_virt(0, PAGE_SIZE, 0); + + /* Map everything at the top of physical RAM 1:1, minus the OpenBIOS ROM in RAM copy */ + ofmem_claim_phys(get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0); + ofmem_claim_virt(get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0); + ofmem_map(get_ram_top(), get_ram_top(), get_hash_base() + HASH_SIZE - get_ram_top(), 0); + + /* Map the OpenBIOS ROM in RAM copy */ + ofmem_claim_phys(ofmem->ramsize - OF_CODE_SIZE, OF_CODE_SIZE, 0); + ofmem_claim_virt(OF_CODE_START, OF_CODE_SIZE, 0); + ofmem_map(ofmem->ramsize - OF_CODE_SIZE, OF_CODE_START, OF_CODE_SIZE, 0); +}