diff options
Diffstat (limited to 'init.c')
-rw-r--r-- | init.c | 206 |
1 files changed, 206 insertions, 0 deletions
@@ -0,0 +1,206 @@ +#include <string.h> +#include <stddef.h> +#include "hwrpb.h" +#include "osf.h" + +#define PAGE_SHIFT 13 +#define PAGE_SIZE (1ul << PAGE_SHIFT) +#define PAGE_OFFSET 0xfffffc0000000000UL + +#define HZ 1024 + +struct pcb_struct pal_pcb __attribute__((section(".sbss"))); + +unsigned long page_dir[1024] __attribute__((aligned(PAGE_SIZE))); +unsigned long page_L1[1024] __attribute__((aligned(PAGE_SIZE))); +unsigned long page_L2[1024][1024] __attribute__((aligned(PAGE_SIZE))); + +struct hwrpb_combine { + struct hwrpb_struct hwrpb; + struct percpu_struct processor; + struct memdesc_struct md; + struct memclust_struct mc[2]; +} hwrpb __attribute__((section(".data.hwrpb"))) = { + /* This is HWRPB\0\0\0. */ + .hwrpb.id = 0x4857525042000000, + + .hwrpb.size = sizeof(struct hwrpb_struct), + .hwrpb.pagesize = PAGE_SIZE, + .hwrpb.ssn = "QEMU", + + /* ??? This should match TARGET_PHYS_ADDR_SPACE_BITS from qemu. */ + .hwrpb.pa_bits = 44, + + /* ??? Should we be truethful and say 1 asn, or simply pretend we + have ASNs but ignore them? */ + .hwrpb.max_asn = 127, + + /* For now, hard-code emulation of sx164. */ + .hwrpb.cpuid = PCA56_CPU, + .processor.type = PCA56_CPU, + .hwrpb.sys_type = ST_DEC_EB164, + .hwrpb.sys_variation = 15 << 10, + .hwrpb.sys_revision = 0, + + .hwrpb.intr_freq = HZ * 4096, + + /* ??? What the hell should we put here. Measure like the kernel will? */ + .hwrpb.cycle_freq = 400000000, + + .hwrpb.vptb = 0x200000000UL, + .hwrpb.nr_processors = 1, + .hwrpb.processor_size = sizeof(struct percpu_struct), + .hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor), + + .hwrpb.mddt_offset = offsetof(struct hwrpb_combine, md), + .md.numclusters = 2, + .mc[0].usage = 2 +}; + +static unsigned long +init_cpuid (void) +{ + unsigned long implver, amask; + + __asm ("implver %0" : "=r"(implver)); + __asm ("amask %1,%0" : "=r"(amask) : "r"(-1)); + amask = ~amask; + + switch (implver) + { + case 0: /* EV4 */ + return EV4_CPU; + + case 1: /* EV5 */ + if ((amask & 0x101) == 0x101) /* MAX + BWX */ + return PCA56_CPU; + if (amask & 1) /* BWX */ + return EV56_CPU; + return EV5_CPU; + + case 2: /* EV6 */ + if (amask & 4) /* CIX */ + return EV67_CPU; + return EV6_CPU; + } + return 0; +} + +static void +hwrpb_update_checksum (void) +{ + unsigned long sum = 0, *l; + for (l = (unsigned long *) &hwrpb.hwrpb; l < &hwrpb.hwrpb.chksum; ++l) + sum += *l; + hwrpb.hwrpb.chksum = sum; +} + +static void +init_hwrpb (unsigned long memsize) +{ + unsigned long end, pal_pages; + + hwrpb.hwrpb.phys_addr = (unsigned int)(unsigned long)&hwrpb; + + /* ??? For some reason GCC wants to use a LITERAL relocation for + _end instead of gp-relative relocations. */ + __asm ("ldah %0,_end($gp) !gprelhigh\n\tlda %0,_end(%0) !gprellow" + : "=r"(end)); + + pal_pages = ((unsigned int)end + PAGE_SIZE - 1) >> PAGE_SHIFT; + + hwrpb.mc[0].numpages = pal_pages; + hwrpb.mc[1].start_pfn = pal_pages; + hwrpb.mc[1].numpages = (memsize >> PAGE_SHIFT) - pal_pages; + + hwrpb_update_checksum (); +} + +static void +init_pcb (void) +{ + pal_pcb.ptbr = (unsigned int)(unsigned long)page_dir; + pal_pcb.flags = 1; +} + +static inline unsigned long +build_pte (void *page) +{ + unsigned long bits; + + bits = ((unsigned long)page - PAGE_OFFSET) << (32 - PAGE_SHIFT); + bits |= _PAGE_VALID; + + return bits; +} + +static inline void * +pte_page (unsigned long pte) +{ + return (void *)((pte >> 32 << PAGE_SHIFT) + PAGE_OFFSET); +} + +static void +set_pte (unsigned long addr, void *page) +{ + unsigned long *pt = page_dir; + unsigned long index; + + index = (addr >> (PAGE_SHIFT+20)) % 1024; + if (pt[index] != 0) + pt = pte_page (pt[index]); + else + __builtin_trap(); + + index = (addr >> (PAGE_SHIFT+10)) % 1024; + if (pt[index] != 0) + pt = pte_page (pt[index]); + else + __builtin_trap(); + + index = (addr >> PAGE_SHIFT) % 1024; + pt[index] = build_pte (page); +} + +static void +init_page_table (unsigned long memsize, unsigned long pal_pages) +{ + unsigned long i, addr, max_addr, page; + + page_dir[0] = build_pte (page_L1); + + for (i = 0; i < 1024; ++i) + page_L1[i] = build_pte (page_L2[i]); + + set_pte ((unsigned long)INIT_HWRPB, &hwrpb); + + addr = 0x20000000ul; + max_addr = 1ul << (PAGE_SHIFT + 20); + page = pal_pages << PAGE_SHIFT; + + while (addr < max_addr && page < memsize) + { + set_pte (addr, (void *)(PAGE_OFFSET + page)); + addr += PAGE_SIZE; + page += PAGE_SIZE; + } + + /* SRM places the self-map for the VPTBR in the second entry. */ + page_dir[1] = build_pte (page_dir); + + /* Write the SRM vptptr. */ + { + register unsigned long a0 __asm__("$16") = max_addr; + __asm ("call_pal 0x2d" : : "r"(a0)); + } +} + +void +do_start(unsigned long memsize) +{ + init_hwrpb (memsize); + init_pcb (); + init_page_table (memsize, hwrpb.mc[0].numpages); + + while (1) ; +} |