Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / ppc / qemu / ofmem.c
diff --git a/qemu/roms/openbios/arch/ppc/qemu/ofmem.c b/qemu/roms/openbios/arch/ppc/qemu/ofmem.c
new file mode 100644 (file)
index 0000000..6f346a3
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ *   Creation Date: <1999/11/07 19:02:11 samuel>
+ *   Time-stamp: <2004/01/07 19:42:36 samuel>
+ *
+ *     <ofmem.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 "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);
+}