diff options
Diffstat (limited to 'qemu/roms/qemu-palcode/init.c')
-rw-r--r-- | qemu/roms/qemu-palcode/init.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/qemu/roms/qemu-palcode/init.c b/qemu/roms/qemu-palcode/init.c new file mode 100644 index 000000000..324bc911a --- /dev/null +++ b/qemu/roms/qemu-palcode/init.c @@ -0,0 +1,310 @@ +/* Initialization of the system and the HWRPB. + + Copyright (C) 2011 Richard Henderson + + This file is part of QEMU PALcode. + + 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; either version 2 of the License or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text + of the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. */ + +#include <string.h> +#include <stddef.h> +#include "hwrpb.h" +#include "osf.h" +#include "ioport.h" +#include "uart.h" +#include "protos.h" +#include SYSTEM_H + +#define PAGE_SHIFT 13 +#define PAGE_SIZE (1ul << PAGE_SHIFT) +#define PAGE_OFFSET 0xfffffc0000000000UL + +#define VPTPTR 0xfffffffe00000000UL + +#define PA(VA) ((unsigned long)(VA) & 0xfffffffffful) +#define VA(PA) ((void *)(PA) + PAGE_OFFSET) + +#define HZ 1024 + +struct hwrpb_combine { + struct hwrpb_struct hwrpb; + struct percpu_struct processor; + struct memdesc_struct md; + struct memclust_struct mc[2]; + struct crb_struct crb; + struct procdesc_struct proc_dispatch; + struct procdesc_struct proc_fixup; +}; + +extern char stack[PAGE_SIZE] __attribute__((section(".sbss"))); +extern char _end[] __attribute__((visibility("hidden"), nocommon)); + +struct pcb_struct pcb __attribute__((section(".sbss"))); + +static unsigned long page_dir[1024] + __attribute__((aligned(PAGE_SIZE), section(".bss.page_dir"))); + +/* The HWRPB must be aligned because it is exported at INIT_HWRPB. */ +struct hwrpb_combine hwrpb __attribute__((aligned(PAGE_SIZE))); + +void *last_alloc; +bool have_vga; + +static void * +alloc (unsigned long size, unsigned long align) +{ + void *p = (void *)(((unsigned long)last_alloc + align - 1) & ~(align - 1)); + last_alloc = p + size; + return memset (p, 0, size); +} + +static inline unsigned long +pt_index(unsigned long addr, int level) +{ + return (addr >> (PAGE_SHIFT + (10 * level))) & 0x3ff; +} + +static inline unsigned long +build_pte (void *page) +{ + unsigned long bits; + + bits = PA((unsigned long)page) << (32 - PAGE_SHIFT); + bits += _PAGE_VALID | _PAGE_KRE | _PAGE_KWE; + + return bits; +} + +static inline void * +pte_page (unsigned long pte) +{ + return VA(pte >> 32 << PAGE_SHIFT); +} + +static void +set_pte (unsigned long addr, void *page) +{ + unsigned long *pt = page_dir; + unsigned long index; + + index = pt_index(addr, 2); + if (pt[index] != 0) + pt = pte_page (pt[index]); + else + { + unsigned long *npt = alloc(PAGE_SIZE, PAGE_SIZE); + pt[index] = build_pte (npt); + pt = npt; + } + + index = pt_index(addr, 1); + if (pt[index] != 0) + pt = pte_page (pt[index]); + else + { + unsigned long *npt = alloc(PAGE_SIZE, PAGE_SIZE); + pt[index] = build_pte (npt); + pt = npt; + } + + index = pt_index(addr, 0); + pt[index] = build_pte (page); +} + +static void +init_page_table(void) +{ + /* Install the self-reference for the virtual page table base register. */ + page_dir[pt_index(VPTPTR, 2)] = build_pte(page_dir); + + set_pte ((unsigned long)INIT_HWRPB, &hwrpb); + + /* ??? SRM maps some amount of memory at 0x20000000 for use by programs + started from the console prompt. Including the bootloader. While + we're emulating MILO, don't bother as we jump straight to the kernel + loaded into KSEG. */ +} + +static void +init_hwrpb (unsigned long memsize) +{ + unsigned long pal_pages; + unsigned long amask; + + hwrpb.hwrpb.phys_addr = PA(&hwrpb); + + /* Yes, the 'HWRPB' magic is in big-endian byte ordering. */ + hwrpb.hwrpb.id = ( (long)'H' << 56 + | (long)'W' << 48 + | (long)'R' << 40 + | (long)'P' << 32 + | (long)'B' << 24); + + hwrpb.hwrpb.size = sizeof(struct hwrpb_struct); + + ((int *)hwrpb.hwrpb.ssn)[0] = ( 'Q' << 0 + | 'E' << 8 + | 'M' << 16 + | 'U' << 24); + + amask = ~__builtin_alpha_amask(-1); + switch (__builtin_alpha_implver()) + { + case 0: /* EV4 */ + hwrpb.hwrpb.cpuid = EV4_CPU; + hwrpb.hwrpb.max_asn = 63; + break; + + case 1: /* EV5 */ + hwrpb.hwrpb.cpuid + = ((amask & 0x101) == 0x101 ? PCA56_CPU /* MAX+BWX */ + : amask & 1 ? EV56_CPU /* BWX */ + : EV5_CPU); + hwrpb.hwrpb.max_asn = 127; + break; + + case 2: /* EV6 */ + hwrpb.hwrpb.cpuid = (amask & 4 ? EV67_CPU : EV6_CPU); /* CIX */ + hwrpb.hwrpb.max_asn = 255; + break; + } + + hwrpb.hwrpb.pagesize = PAGE_SIZE; + hwrpb.hwrpb.pa_bits = 40; + hwrpb.hwrpb.sys_type = SYS_TYPE; + hwrpb.hwrpb.sys_variation = SYS_VARIATION; + hwrpb.hwrpb.sys_revision = SYS_REVISION; + hwrpb.processor.type = hwrpb.hwrpb.cpuid; + + hwrpb.hwrpb.intr_freq = HZ * 4096; + hwrpb.hwrpb.cycle_freq = 250000000; /* QEMU architects 250MHz. */ + + hwrpb.hwrpb.vptb = VPTPTR; + + hwrpb.hwrpb.nr_processors = 1; + hwrpb.hwrpb.processor_size = sizeof(struct percpu_struct); + hwrpb.hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor); + + hwrpb.hwrpb.mddt_offset = offsetof(struct hwrpb_combine, md); + hwrpb.md.numclusters = 2; + + pal_pages = (PA(last_alloc) + PAGE_SIZE - 1) >> PAGE_SHIFT; + + hwrpb.mc[0].numpages = pal_pages; + hwrpb.mc[0].usage = 1; + hwrpb.mc[1].start_pfn = pal_pages; + hwrpb.mc[1].numpages = (memsize >> PAGE_SHIFT) - pal_pages; + + hwrpb.hwrpb.crb_offset = offsetof(struct hwrpb_combine, crb); + hwrpb.crb.dispatch_va = &hwrpb.proc_dispatch; + hwrpb.crb.dispatch_pa = PA(&hwrpb.proc_dispatch); + hwrpb.crb.fixup_va = &hwrpb.proc_fixup; + hwrpb.crb.fixup_pa = PA(&hwrpb.proc_fixup); + hwrpb.crb.map_entries = 1; + hwrpb.crb.map_pages = 1; + hwrpb.crb.map[0].va = &hwrpb; + hwrpb.crb.map[0].pa = PA(&hwrpb); + hwrpb.crb.map[0].count = 1; + + /* See crb.c for how we match the VMS calling conventions to Unix. */ + hwrpb.proc_dispatch.address = (unsigned long)crb_dispatch; + hwrpb.proc_fixup.address = (unsigned long)crb_fixup; + + hwrpb_update_checksum(&hwrpb.hwrpb); +} + +static void +init_pcb (void) +{ + pcb.ksp = (unsigned long)stack + sizeof(stack); + pcb.ptbr = PA(page_dir) >> PAGE_SHIFT; + pcb.flags = 1; /* FEN */ +} + +static void +init_i8259 (void) +{ + /* ??? MILO initializes the PIC as edge triggered; I do not know how SRM + initializes them. However, Linux seems to expect that these are level + triggered. That may be a kernel bug, but level triggers are more + reliable anyway so lets go with that. */ + + /* Initialize the slave PIC. */ + outb(0x11, PORT_PIC2_CMD); /* ICW1: edge trigger, cascade, ICW4 req */ + outb(0x08, PORT_PIC2_DATA); /* ICW2: irq offset = 8 */ + outb(0x02, PORT_PIC2_DATA); /* ICW3: slave ID 2 */ + outb(0x01, PORT_PIC2_DATA); /* ICW4: not special nested, normal eoi */ + + /* Initialize the master PIC. */ + outb(0x11, PORT_PIC1_CMD); /* ICW1 */ + outb(0x00, PORT_PIC1_DATA); /* ICW2: irq offset = 0 */ + outb(0x04, PORT_PIC1_DATA); /* ICW3: slave control INTC2 */ + outb(0x01, PORT_PIC1_DATA); /* ICW4 */ + + /* Initialize level triggers. The CY82C693UB that's on real alpha + hardware doesn't have this; this is a PIIX extension. However, + QEMU doesn't implement regular level triggers. */ + outb(0xff, PORT_PIC2_ELCR); + outb(0xff, PORT_PIC1_ELCR); + + /* Disable all interrupts. */ + outb(0xff, PORT_PIC2_DATA); + outb(0xff, PORT_PIC1_DATA); + + /* Non-specific EOI, clearing anything the might be pending. */ + outb(0x20, PORT_PIC2_CMD); + outb(0x20, PORT_PIC1_CMD); +} + +void +do_start(unsigned long memsize, void (*kernel_entry)(void), long cpus) +{ + last_alloc = _end; + + init_page_table(); + init_hwrpb(memsize); + init_pcb(); + init_i8259(); + uart_init(); + ps2port_setup(); + pci_setup(); + vgahw_init(); + + { + register int variant __asm__("$16") = 2; /* OSF/1 PALcode */ + register void (*pc)(void) __asm__("$17"); + register unsigned long pa_pcb __asm__("$18"); + register unsigned long vptptr __asm__("$19"); + + pc = (kernel_entry ? kernel_entry : do_console); + pa_pcb = PA(&pcb); + vptptr = VPTPTR; + asm("call_pal 0x0a" : : "r"(variant), "r"(pc), "r"(pa_pcb), "r"(vptptr)); + } + __builtin_unreachable (); +} + +void +do_start_wait(void) +{ + while (1) + { + // WtInt with interrupts off. Rely on the fact that QEMU will + // un-halt the CPU when an interrupt arrives. + asm("lda $16,-1\n\tcall_pal 0x3e" : : : "$0", "$16"); + + // FIXME do something with the IPI. + } +} |