diff options
Diffstat (limited to 'hw/alpha')
| -rw-r--r-- | hw/alpha/Makefile.objs | 1 | ||||
| -rw-r--r-- | hw/alpha/alpha_sys.h | 21 | ||||
| -rw-r--r-- | hw/alpha/dp264.c | 184 | ||||
| -rw-r--r-- | hw/alpha/pci.c | 91 | ||||
| -rw-r--r-- | hw/alpha/typhoon.c | 956 | 
5 files changed, 1253 insertions, 0 deletions
diff --git a/hw/alpha/Makefile.objs b/hw/alpha/Makefile.objs new file mode 100644 index 00000000..5c742756 --- /dev/null +++ b/hw/alpha/Makefile.objs @@ -0,0 +1 @@ +obj-y += dp264.o pci.o typhoon.o diff --git a/hw/alpha/alpha_sys.h b/hw/alpha/alpha_sys.h new file mode 100644 index 00000000..e11025b4 --- /dev/null +++ b/hw/alpha/alpha_sys.h @@ -0,0 +1,21 @@ +/* Alpha cores and system support chips.  */ + +#ifndef HW_ALPHA_H +#define HW_ALPHA_H 1 + +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ide.h" +#include "hw/i386/pc.h" +#include "hw/irq.h" + + +PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, AlphaCPU *[4], +                     pci_map_irq_fn); + +/* alpha_pci.c.  */ +extern const MemoryRegionOps alpha_pci_ignore_ops; +extern const MemoryRegionOps alpha_pci_conf1_ops; +extern const MemoryRegionOps alpha_pci_iack_ops; + +#endif diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c new file mode 100644 index 00000000..f86e7bb8 --- /dev/null +++ b/hw/alpha/dp264.c @@ -0,0 +1,184 @@ +/* + * QEMU Alpha DP264/CLIPPER hardware system emulator. + * + * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK + * variants because CLIPPER doesn't have an SMC669 SuperIO controller + * that we need to emulate as well. + */ + +#include "hw/hw.h" +#include "elf.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "alpha_sys.h" +#include "sysemu/sysemu.h" +#include "hw/timer/mc146818rtc.h" +#include "hw/ide.h" +#include "hw/timer/i8254.h" +#include "hw/char/serial.h" + +#define MAX_IDE_BUS 2 + +static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) +{ +    if (((addr >> 41) & 3) == 2) { +        addr &= 0xffffffffffull; +    } +    return addr; +} + +/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems. +    (0) The dev_irq_n lines into the cpu, which we totally ignore, +    (1) The DRIR lines in the typhoon chipset, +    (2) The "vector" aka mangled interrupt number reported by SRM PALcode, +    (3) The interrupt number assigned by the kernel. +   The following function is concerned with (1) only.  */ + +static int clipper_pci_map_irq(PCIDevice *d, int irq_num) +{ +    int slot = d->devfn >> 3; + +    assert(irq_num >= 0 && irq_num <= 3); + +    return (slot + 1) * 4 + irq_num; +} + +static void clipper_init(MachineState *machine) +{ +    ram_addr_t ram_size = machine->ram_size; +    const char *cpu_model = machine->cpu_model; +    const char *kernel_filename = machine->kernel_filename; +    const char *kernel_cmdline = machine->kernel_cmdline; +    const char *initrd_filename = machine->initrd_filename; +    AlphaCPU *cpus[4]; +    PCIBus *pci_bus; +    ISABus *isa_bus; +    qemu_irq rtc_irq; +    long size, i; +    char *palcode_filename; +    uint64_t palcode_entry, palcode_low, palcode_high; +    uint64_t kernel_entry, kernel_low, kernel_high; + +    /* Create up to 4 cpus.  */ +    memset(cpus, 0, sizeof(cpus)); +    for (i = 0; i < smp_cpus; ++i) { +        cpus[i] = cpu_alpha_init(cpu_model ? cpu_model : "ev67"); +    } + +    cpus[0]->env.trap_arg0 = ram_size; +    cpus[0]->env.trap_arg1 = 0; +    cpus[0]->env.trap_arg2 = smp_cpus; + +    /* Init the chipset.  */ +    pci_bus = typhoon_init(ram_size, &isa_bus, &rtc_irq, cpus, +                           clipper_pci_map_irq); + +    /* Since we have an SRM-compatible PALcode, use the SRM epoch.  */ +    rtc_init(isa_bus, 1900, rtc_irq); + +    pit_init(isa_bus, 0x40, 0, NULL); +    isa_create_simple(isa_bus, "i8042"); + +    /* VGA setup.  Don't bother loading the bios.  */ +    pci_vga_init(pci_bus); + +    /* Serial code setup.  */ +    serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + +    /* Network setup.  e1000 is good enough, failing Tulip support.  */ +    for (i = 0; i < nb_nics; i++) { +        pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); +    } + +    /* IDE disk setup.  */ +    { +        DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; +        ide_drive_get(hd, ARRAY_SIZE(hd)); + +        pci_cmd646_ide_init(pci_bus, hd, 0); +    } + +    /* Load PALcode.  Given that this is not "real" cpu palcode, +       but one explicitly written for the emulation, we might as +       well load it directly from and ELF image.  */ +    palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, +                                bios_name ? bios_name : "palcode-clipper"); +    if (palcode_filename == NULL) { +        hw_error("no palcode provided\n"); +        exit(1); +    } +    size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, +                    NULL, &palcode_entry, &palcode_low, &palcode_high, +                    0, EM_ALPHA, 0); +    if (size < 0) { +        hw_error("could not load palcode '%s'\n", palcode_filename); +        exit(1); +    } +    g_free(palcode_filename); + +    /* Start all cpus at the PALcode RESET entry point.  */ +    for (i = 0; i < smp_cpus; ++i) { +        cpus[i]->env.pal_mode = 1; +        cpus[i]->env.pc = palcode_entry; +        cpus[i]->env.palbr = palcode_entry; +    } + +    /* Load a kernel.  */ +    if (kernel_filename) { +        uint64_t param_offset; + +        size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, +                        NULL, &kernel_entry, &kernel_low, &kernel_high, +                        0, EM_ALPHA, 0); +        if (size < 0) { +            hw_error("could not load kernel '%s'\n", kernel_filename); +            exit(1); +        } + +        cpus[0]->env.trap_arg1 = kernel_entry; + +        param_offset = kernel_low - 0x6000; + +        if (kernel_cmdline) { +            pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline); +        } + +        if (initrd_filename) { +            long initrd_base, initrd_size; + +            initrd_size = get_image_size(initrd_filename); +            if (initrd_size < 0) { +                hw_error("could not load initial ram disk '%s'\n", +                         initrd_filename); +                exit(1); +            } + +            /* Put the initrd image as high in memory as possible.  */ +            initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; +            load_image_targphys(initrd_filename, initrd_base, +                                ram_size - initrd_base); + +            address_space_stq(&address_space_memory, param_offset + 0x100, +                              initrd_base + 0xfffffc0000000000ULL, +                              MEMTXATTRS_UNSPECIFIED, +                              NULL); +            address_space_stq(&address_space_memory, param_offset + 0x108, +                              initrd_size, MEMTXATTRS_UNSPECIFIED, NULL); +        } +    } +} + +static QEMUMachine clipper_machine = { +    .name = "clipper", +    .desc = "Alpha DP264/CLIPPER", +    .init = clipper_init, +    .max_cpus = 4, +    .is_default = 1, +}; + +static void clipper_machine_init(void) +{ +    qemu_register_machine(&clipper_machine); +} + +machine_init(clipper_machine_init); diff --git a/hw/alpha/pci.c b/hw/alpha/pci.c new file mode 100644 index 00000000..d839dd55 --- /dev/null +++ b/hw/alpha/pci.c @@ -0,0 +1,91 @@ +/* + * QEMU Alpha PCI support functions. + * + * Some of this isn't very Alpha specific at all. + * + * ??? Sparse memory access not implemented. + */ + +#include "config.h" +#include "alpha_sys.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + + +/* Fallback for unassigned PCI I/O operations.  Avoids MCHK.  */ + +static uint64_t ignore_read(void *opaque, hwaddr addr, unsigned size) +{ +    return 0; +} + +static void ignore_write(void *opaque, hwaddr addr, uint64_t v, unsigned size) +{ +} + +const MemoryRegionOps alpha_pci_ignore_ops = { +    .read = ignore_read, +    .write = ignore_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .valid = { +        .min_access_size = 1, +        .max_access_size = 8, +    }, +    .impl = { +        .min_access_size = 1, +        .max_access_size = 8, +    }, +}; + + +/* PCI config space reads/writes, to byte-word addressable memory.  */ +static uint64_t bw_conf1_read(void *opaque, hwaddr addr, +                              unsigned size) +{ +    PCIBus *b = opaque; +    return pci_data_read(b, addr, size); +} + +static void bw_conf1_write(void *opaque, hwaddr addr, +                           uint64_t val, unsigned size) +{ +    PCIBus *b = opaque; +    pci_data_write(b, addr, val, size); +} + +const MemoryRegionOps alpha_pci_conf1_ops = { +    .read = bw_conf1_read, +    .write = bw_conf1_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .impl = { +        .min_access_size = 1, +        .max_access_size = 4, +    }, +}; + +/* PCI/EISA Interrupt Acknowledge Cycle.  */ + +static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size) +{ +    return pic_read_irq(isa_pic); +} + +static void special_write(void *opaque, hwaddr addr, +                          uint64_t val, unsigned size) +{ +    qemu_log("pci: special write cycle"); +} + +const MemoryRegionOps alpha_pci_iack_ops = { +    .read = iack_read, +    .write = special_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .valid = { +        .min_access_size = 4, +        .max_access_size = 4, +    }, +    .impl = { +        .min_access_size = 4, +        .max_access_size = 4, +    }, +}; diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c new file mode 100644 index 00000000..421162e1 --- /dev/null +++ b/hw/alpha/typhoon.c @@ -0,0 +1,956 @@ +/* + * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. + * + * Written by Richard Henderson. + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "cpu.h" +#include "hw/hw.h" +#include "hw/devices.h" +#include "sysemu/sysemu.h" +#include "alpha_sys.h" +#include "exec/address-spaces.h" + + +#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" + +typedef struct TyphoonCchip { +    MemoryRegion region; +    uint64_t misc; +    uint64_t drir; +    uint64_t dim[4]; +    uint32_t iic[4]; +    AlphaCPU *cpu[4]; +} TyphoonCchip; + +typedef struct TyphoonWindow { +    uint64_t wba; +    uint64_t wsm; +    uint64_t tba; +} TyphoonWindow; +  +typedef struct TyphoonPchip { +    MemoryRegion region; +    MemoryRegion reg_iack; +    MemoryRegion reg_mem; +    MemoryRegion reg_io; +    MemoryRegion reg_conf; + +    AddressSpace iommu_as; +    MemoryRegion iommu; + +    uint64_t ctl; +    TyphoonWindow win[4]; +} TyphoonPchip; + +#define TYPHOON_PCI_HOST_BRIDGE(obj) \ +    OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE) + +typedef struct TyphoonState { +    PCIHostState parent_obj; + +    TyphoonCchip cchip; +    TyphoonPchip pchip; +    MemoryRegion dchip_region; +    MemoryRegion ram_region; +} TyphoonState; + +/* Called when one of DRIR or DIM changes.  */ +static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) +{ +    /* If there are any non-masked interrupts, tell the cpu.  */ +    if (cpu != NULL) { +        CPUState *cs = CPU(cpu); +        if (req) { +            cpu_interrupt(cs, CPU_INTERRUPT_HARD); +        } else { +            cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); +        } +    } +} + +static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) +{ +    CPUState *cpu = current_cpu; +    TyphoonState *s = opaque; +    uint64_t ret = 0; + +    switch (addr) { +    case 0x0000: +        /* CSC: Cchip System Configuration Register.  */ +        /* All sorts of data here; probably the only thing relevant is +           PIP<14> Pchip 1 Present = 0.  */ +        break; + +    case 0x0040: +        /* MTR: Memory Timing Register.  */ +        /* All sorts of stuff related to real DRAM.  */ +        break; + +    case 0x0080: +        /* MISC: Miscellaneous Register.  */ +        ret = s->cchip.misc | (cpu->cpu_index & 3); +        break; + +    case 0x00c0: +        /* MPD: Memory Presence Detect Register.  */ +        break; + +    case 0x0100: /* AAR0 */ +    case 0x0140: /* AAR1 */ +    case 0x0180: /* AAR2 */ +    case 0x01c0: /* AAR3 */ +        /* AAR: Array Address Register.  */ +        /* All sorts of information about DRAM.  */ +        break; + +    case 0x0200: +        /* DIM0: Device Interrupt Mask Register, CPU0.  */ +        ret = s->cchip.dim[0]; +        break; +    case 0x0240: +        /* DIM1: Device Interrupt Mask Register, CPU1.  */ +        ret = s->cchip.dim[1]; +        break; +    case 0x0280: +        /* DIR0: Device Interrupt Request Register, CPU0.  */ +        ret = s->cchip.dim[0] & s->cchip.drir; +        break; +    case 0x02c0: +        /* DIR1: Device Interrupt Request Register, CPU1.  */ +        ret = s->cchip.dim[1] & s->cchip.drir; +        break; +    case 0x0300: +        /* DRIR: Device Raw Interrupt Request Register.  */ +        ret = s->cchip.drir; +        break; + +    case 0x0340: +        /* PRBEN: Probe Enable Register.  */ +        break; + +    case 0x0380: +        /* IIC0: Interval Ignore Count Register, CPU0.  */ +        ret = s->cchip.iic[0]; +        break; +    case 0x03c0: +        /* IIC1: Interval Ignore Count Register, CPU1.  */ +        ret = s->cchip.iic[1]; +        break; + +    case 0x0400: /* MPR0 */ +    case 0x0440: /* MPR1 */ +    case 0x0480: /* MPR2 */ +    case 0x04c0: /* MPR3 */ +        /* MPR: Memory Programming Register.  */ +        break; + +    case 0x0580: +        /* TTR: TIGbus Timing Register.  */ +        /* All sorts of stuff related to interrupt delivery timings.  */ +        break; +    case 0x05c0: +        /* TDR: TIGbug Device Timing Register.  */ +        break; + +    case 0x0600: +        /* DIM2: Device Interrupt Mask Register, CPU2.  */ +        ret = s->cchip.dim[2]; +        break; +    case 0x0640: +        /* DIM3: Device Interrupt Mask Register, CPU3.  */ +        ret = s->cchip.dim[3]; +        break; +    case 0x0680: +        /* DIR2: Device Interrupt Request Register, CPU2.  */ +        ret = s->cchip.dim[2] & s->cchip.drir; +        break; +    case 0x06c0: +        /* DIR3: Device Interrupt Request Register, CPU3.  */ +        ret = s->cchip.dim[3] & s->cchip.drir; +        break; + +    case 0x0700: +        /* IIC2: Interval Ignore Count Register, CPU2.  */ +        ret = s->cchip.iic[2]; +        break; +    case 0x0740: +        /* IIC3: Interval Ignore Count Register, CPU3.  */ +        ret = s->cchip.iic[3]; +        break; + +    case 0x0780: +        /* PWR: Power Management Control.   */ +        break; +     +    case 0x0c00: /* CMONCTLA */ +    case 0x0c40: /* CMONCTLB */ +    case 0x0c80: /* CMONCNT01 */ +    case 0x0cc0: /* CMONCNT23 */ +        break; + +    default: +        cpu_unassigned_access(cpu, addr, false, false, 0, size); +        return -1; +    } + +    return ret; +} + +static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) +{ +    /* Skip this.  It's all related to DRAM timing and setup.  */ +    return 0; +} + +static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) +{ +    TyphoonState *s = opaque; +    uint64_t ret = 0; + +    switch (addr) { +    case 0x0000: +        /* WSBA0: Window Space Base Address Register.  */ +        ret = s->pchip.win[0].wba; +        break; +    case 0x0040: +        /* WSBA1 */ +        ret = s->pchip.win[1].wba; +        break; +    case 0x0080: +        /* WSBA2 */ +        ret = s->pchip.win[2].wba; +        break; +    case 0x00c0: +        /* WSBA3 */ +        ret = s->pchip.win[3].wba; +        break; + +    case 0x0100: +        /* WSM0: Window Space Mask Register.  */ +        ret = s->pchip.win[0].wsm; +        break; +    case 0x0140: +        /* WSM1 */ +        ret = s->pchip.win[1].wsm; +        break; +    case 0x0180: +        /* WSM2 */ +        ret = s->pchip.win[2].wsm; +        break; +    case 0x01c0: +        /* WSM3 */ +        ret = s->pchip.win[3].wsm; +        break; + +    case 0x0200: +        /* TBA0: Translated Base Address Register.  */ +        ret = s->pchip.win[0].tba; +        break; +    case 0x0240: +        /* TBA1 */ +        ret = s->pchip.win[1].tba; +        break; +    case 0x0280: +        /* TBA2 */ +        ret = s->pchip.win[2].tba; +        break; +    case 0x02c0: +        /* TBA3 */ +        ret = s->pchip.win[3].tba; +        break; + +    case 0x0300: +        /* PCTL: Pchip Control Register.  */ +        ret = s->pchip.ctl; +        break; +    case 0x0340: +        /* PLAT: Pchip Master Latency Register.  */ +        break; +    case 0x03c0: +        /* PERROR: Pchip Error Register.  */ +        break; +    case 0x0400: +        /* PERRMASK: Pchip Error Mask Register.  */ +        break; +    case 0x0440: +        /* PERRSET: Pchip Error Set Register.  */ +        break; +    case 0x0480: +        /* TLBIV: Translation Buffer Invalidate Virtual Register (WO).  */ +        break; +    case 0x04c0: +        /* TLBIA: Translation Buffer Invalidate All Register (WO).  */ +        break; +    case 0x0500: /* PMONCTL */ +    case 0x0540: /* PMONCNT */ +    case 0x0800: /* SPRST */ +        break; + +    default: +        cpu_unassigned_access(current_cpu, addr, false, false, 0, size); +        return -1; +    } + +    return ret; +} + +static void cchip_write(void *opaque, hwaddr addr, +                        uint64_t val, unsigned size) +{ +    TyphoonState *s = opaque; +    uint64_t oldval, newval; + +    switch (addr) { +    case 0x0000: +        /* CSC: Cchip System Configuration Register.  */ +        /* All sorts of data here; nothing relevant RW.  */ +        break; + +    case 0x0040: +        /* MTR: Memory Timing Register.  */ +        /* All sorts of stuff related to real DRAM.  */ +        break; + +    case 0x0080: +        /* MISC: Miscellaneous Register.  */ +        newval = oldval = s->cchip.misc; +        newval &= ~(val & 0x10000ff0);     /* W1C fields */ +        if (val & 0x100000) { +            newval &= ~0xff0000ull;        /* ACL clears ABT and ABW */ +        } else { +            newval |= val & 0x00f00000;    /* ABT field is W1S */ +            if ((newval & 0xf0000) == 0) { +                newval |= val & 0xf0000;   /* ABW field is W1S iff zero */ +            } +        } +        newval |= (val & 0xf000) >> 4;     /* IPREQ field sets IPINTR.  */ + +        newval &= ~0xf0000000000ull;       /* WO and RW fields */ +        newval |= val & 0xf0000000000ull; +        s->cchip.misc = newval; + +        /* Pass on changes to IPI and ITI state.  */ +        if ((newval ^ oldval) & 0xff0) { +            int i; +            for (i = 0; i < 4; ++i) { +                AlphaCPU *cpu = s->cchip.cpu[i]; +                if (cpu != NULL) { +                    CPUState *cs = CPU(cpu); +                    /* IPI can be either cleared or set by the write.  */ +                    if (newval & (1 << (i + 8))) { +                        cpu_interrupt(cs, CPU_INTERRUPT_SMP); +                    } else { +                        cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP); +                    } + +                    /* ITI can only be cleared by the write.  */ +                    if ((newval & (1 << (i + 4))) == 0) { +                        cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); +                    } +                } +            } +        } +        break; + +    case 0x00c0: +        /* MPD: Memory Presence Detect Register.  */ +        break; + +    case 0x0100: /* AAR0 */ +    case 0x0140: /* AAR1 */ +    case 0x0180: /* AAR2 */ +    case 0x01c0: /* AAR3 */ +        /* AAR: Array Address Register.  */ +        /* All sorts of information about DRAM.  */ +        break; + +    case 0x0200: /* DIM0 */ +        /* DIM: Device Interrupt Mask Register, CPU0.  */ +        s->cchip.dim[0] = val; +        cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); +        break; +    case 0x0240: /* DIM1 */ +        /* DIM: Device Interrupt Mask Register, CPU1.  */ +        s->cchip.dim[0] = val; +        cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); +        break; + +    case 0x0280: /* DIR0 (RO) */ +    case 0x02c0: /* DIR1 (RO) */ +    case 0x0300: /* DRIR (RO) */ +        break; + +    case 0x0340: +        /* PRBEN: Probe Enable Register.  */ +        break; + +    case 0x0380: /* IIC0 */ +        s->cchip.iic[0] = val & 0xffffff; +        break; +    case 0x03c0: /* IIC1 */ +        s->cchip.iic[1] = val & 0xffffff; +        break; + +    case 0x0400: /* MPR0 */ +    case 0x0440: /* MPR1 */ +    case 0x0480: /* MPR2 */ +    case 0x04c0: /* MPR3 */ +        /* MPR: Memory Programming Register.  */ +        break; + +    case 0x0580: +        /* TTR: TIGbus Timing Register.  */ +        /* All sorts of stuff related to interrupt delivery timings.  */ +        break; +    case 0x05c0: +        /* TDR: TIGbug Device Timing Register.  */ +        break; + +    case 0x0600: +        /* DIM2: Device Interrupt Mask Register, CPU2.  */ +        s->cchip.dim[2] = val; +        cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); +        break; +    case 0x0640: +        /* DIM3: Device Interrupt Mask Register, CPU3.  */ +        s->cchip.dim[3] = val; +        cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); +        break; + +    case 0x0680: /* DIR2 (RO) */ +    case 0x06c0: /* DIR3 (RO) */ +        break; + +    case 0x0700: /* IIC2 */ +        s->cchip.iic[2] = val & 0xffffff; +        break; +    case 0x0740: /* IIC3 */ +        s->cchip.iic[3] = val & 0xffffff; +        break; + +    case 0x0780: +        /* PWR: Power Management Control.   */ +        break; +     +    case 0x0c00: /* CMONCTLA */ +    case 0x0c40: /* CMONCTLB */ +    case 0x0c80: /* CMONCNT01 */ +    case 0x0cc0: /* CMONCNT23 */ +        break; + +    default: +        cpu_unassigned_access(current_cpu, addr, true, false, 0, size); +        return; +    } +} + +static void dchip_write(void *opaque, hwaddr addr, +                        uint64_t val, unsigned size) +{ +    /* Skip this.  It's all related to DRAM timing and setup.  */ +} + +static void pchip_write(void *opaque, hwaddr addr, +                        uint64_t val, unsigned size) +{ +    TyphoonState *s = opaque; +    uint64_t oldval; + +    switch (addr) { +    case 0x0000: +        /* WSBA0: Window Space Base Address Register.  */ +        s->pchip.win[0].wba = val & 0xfff00003u; +        break; +    case 0x0040: +        /* WSBA1 */ +        s->pchip.win[1].wba = val & 0xfff00003u; +        break; +    case 0x0080: +        /* WSBA2 */ +        s->pchip.win[2].wba = val & 0xfff00003u; +        break; +    case 0x00c0: +        /* WSBA3 */ +        s->pchip.win[3].wba = (val & 0x80fff00001ull) | 2; +        break; + +    case 0x0100: +        /* WSM0: Window Space Mask Register.  */ +        s->pchip.win[0].wsm = val & 0xfff00000u; +        break; +    case 0x0140: +        /* WSM1 */ +        s->pchip.win[1].wsm = val & 0xfff00000u; +        break; +    case 0x0180: +        /* WSM2 */ +        s->pchip.win[2].wsm = val & 0xfff00000u; +        break; +    case 0x01c0: +        /* WSM3 */ +        s->pchip.win[3].wsm = val & 0xfff00000u; +        break; + +    case 0x0200: +        /* TBA0: Translated Base Address Register.  */ +        s->pchip.win[0].tba = val & 0x7fffffc00ull; +        break; +    case 0x0240: +        /* TBA1 */ +        s->pchip.win[1].tba = val & 0x7fffffc00ull; +        break; +    case 0x0280: +        /* TBA2 */ +        s->pchip.win[2].tba = val & 0x7fffffc00ull; +        break; +    case 0x02c0: +        /* TBA3 */ +        s->pchip.win[3].tba = val & 0x7fffffc00ull; +        break; + +    case 0x0300: +        /* PCTL: Pchip Control Register.  */ +        oldval = s->pchip.ctl; +        oldval &= ~0x00001cff0fc7ffull;       /* RW fields */ +        oldval |= val & 0x00001cff0fc7ffull; +        s->pchip.ctl = oldval; +        break; + +    case 0x0340: +        /* PLAT: Pchip Master Latency Register.  */ +        break; +    case 0x03c0: +        /* PERROR: Pchip Error Register.  */ +        break; +    case 0x0400: +        /* PERRMASK: Pchip Error Mask Register.  */ +        break; +    case 0x0440: +        /* PERRSET: Pchip Error Set Register.  */ +        break; + +    case 0x0480: +        /* TLBIV: Translation Buffer Invalidate Virtual Register.  */ +        break; + +    case 0x04c0: +        /* TLBIA: Translation Buffer Invalidate All Register (WO).  */ +        break; + +    case 0x0500: +        /* PMONCTL */ +    case 0x0540: +        /* PMONCNT */ +    case 0x0800: +        /* SPRST */ +        break; + +    default: +        cpu_unassigned_access(current_cpu, addr, true, false, 0, size); +        return; +    } +} + +static const MemoryRegionOps cchip_ops = { +    .read = cchip_read, +    .write = cchip_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .valid = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +    .impl = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +}; + +static const MemoryRegionOps dchip_ops = { +    .read = dchip_read, +    .write = dchip_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .valid = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +    .impl = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +}; + +static const MemoryRegionOps pchip_ops = { +    .read = pchip_read, +    .write = pchip_write, +    .endianness = DEVICE_LITTLE_ENDIAN, +    .valid = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +    .impl = { +        .min_access_size = 8, +        .max_access_size = 8, +    }, +}; + +/* A subroutine of typhoon_translate_iommu that builds an IOMMUTLBEntry +   using the given translated address and mask.  */ +static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret) +{ +    *ret = (IOMMUTLBEntry) { +        .target_as = &address_space_memory, +        .translated_addr = taddr, +        .addr_mask = mask, +        .perm = IOMMU_RW, +    }; +    return true; +} + +/* A subroutine of typhoon_translate_iommu that handles scatter-gather +   translation, given the address of the PTE.  */ +static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret) +{ +    uint64_t pte = address_space_ldq(&address_space_memory, pte_addr, +                                     MEMTXATTRS_UNSPECIFIED, NULL); + +    /* Check valid bit.  */ +    if ((pte & 1) == 0) { +        return false; +    } + +    return make_iommu_tlbe((pte & 0x3ffffe) << 12, 0x1fff, ret); +} + +/* A subroutine of typhoon_translate_iommu that handles one of the +   four single-address-cycle translation windows.  */ +static bool window_translate(TyphoonWindow *win, hwaddr addr, +                             IOMMUTLBEntry *ret) +{ +    uint32_t wba = win->wba; +    uint64_t wsm = win->wsm; +    uint64_t tba = win->tba; +    uint64_t wsm_ext = wsm | 0xfffff; + +    /* Check for window disabled.  */ +    if ((wba & 1) == 0) { +        return false; +    } + +    /* Check for window hit.  */ +    if ((addr & ~wsm_ext) != (wba & 0xfff00000u)) { +        return false; +    } + +    if (wba & 2) { +        /* Scatter-gather translation.  */ +        hwaddr pte_addr; + +        /* See table 10-6, Generating PTE address for PCI DMA Address.  */ +        pte_addr  = tba & ~(wsm >> 10); +        pte_addr |= (addr & (wsm | 0xfe000)) >> 10; +        return pte_translate(pte_addr, ret); +    } else { +	/* Direct-mapped translation.  */ +	return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret); +    } +} + +/* Handle PCI-to-system address translation.  */ +/* TODO: A translation failure here ought to set PCI error codes on the +   Pchip and generate a machine check interrupt.  */ +static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, +                                             bool is_write) +{ +    TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); +    IOMMUTLBEntry ret; +    int i; + +    if (addr <= 0xffffffffu) { +        /* Single-address cycle.  */ + +        /* Check for the Window Hole, inhibiting matching.  */ +        if ((pchip->ctl & 0x20) +            && addr >= 0x80000 +            && addr <= 0xfffff) { +            goto failure; +        } + +        /* Check the first three windows.  */ +        for (i = 0; i < 3; ++i) { +            if (window_translate(&pchip->win[i], addr, &ret)) { +                goto success; +            } +        } + +        /* Check the fourth window for DAC disable.  */ +        if ((pchip->win[3].wba & 0x80000000000ull) == 0 +	    && window_translate(&pchip->win[3], addr, &ret)) { +            goto success; +        } +    } else { +        /* Double-address cycle.  */ + +        if (addr >= 0x10000000000ull && addr < 0x20000000000ull) { +            /* Check for the DMA monster window.  */ +            if (pchip->ctl & 0x40) { +                /* See 10.1.4.4; in particular <39:35> is ignored.  */ +                make_iommu_tlbe(0, 0x007ffffffffull, &ret); +		goto success; +            } +        } + +        if (addr >= 0x80000000000ull && addr <= 0xfffffffffffull) { +            /* Check the fourth window for DAC enable and window enable.  */ +            if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) { +                uint64_t pte_addr; + +                pte_addr  = pchip->win[3].tba & 0x7ffc00000ull; +                pte_addr |= (addr & 0xffffe000u) >> 10; +                if (pte_translate(pte_addr, &ret)) { +			goto success; +		} +            } +        } +    } + + failure: +    ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE }; + success: +    return ret; +} + +static const MemoryRegionIOMMUOps typhoon_iommu_ops = { +    .translate = typhoon_translate_iommu, +}; + +static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ +    TyphoonState *s = opaque; +    return &s->pchip.iommu_as; +} + +static void typhoon_set_irq(void *opaque, int irq, int level) +{ +    TyphoonState *s = opaque; +    uint64_t drir; +    int i; + +    /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL.  */ +    drir = s->cchip.drir; +    if (level) { +        drir |= 1ull << irq; +    } else { +        drir &= ~(1ull << irq); +    } +    s->cchip.drir = drir; + +    for (i = 0; i < 4; ++i) { +        cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); +    } +} + +static void typhoon_set_isa_irq(void *opaque, int irq, int level) +{ +    typhoon_set_irq(opaque, 55, level); +} + +static void typhoon_set_timer_irq(void *opaque, int irq, int level) +{ +    TyphoonState *s = opaque; +    int i; + +    /* Thankfully, the mc146818rtc code doesn't track the IRQ state, +       and so we don't have to worry about missing interrupts just +       because we never actually ACK the interrupt.  Just ignore any +       case of the interrupt level going low.  */ +    if (level == 0) { +        return; +    } + +    /* Deliver the interrupt to each CPU, considering each CPU's IIC.  */ +    for (i = 0; i < 4; ++i) { +        AlphaCPU *cpu = s->cchip.cpu[i]; +        if (cpu != NULL) { +            uint32_t iic = s->cchip.iic[i]; + +            /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. +               Bit 24 is the OverFlow bit, RO, and set when the count +               decrements past 0.  When is OF cleared?  My guess is that +               OF is actually cleared when the IIC is written, and that +               the ICNT field always decrements.  At least, that's an +               interpretation that makes sense, and "allows the CPU to +               determine exactly how mant interval timer ticks were +               skipped".  At least within the next 4M ticks...  */ + +            iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); +            s->cchip.iic[i] = iic; + +            if (iic & 0x1000000) { +                /* Set the ITI bit for this cpu.  */ +                s->cchip.misc |= 1 << (i + 4); +                /* And signal the interrupt.  */ +                cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER); +            } +        } +    } +} + +static void typhoon_alarm_timer(void *opaque) +{ +    TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); +    int cpu = (uintptr_t)opaque & 3; + +    /* Set the ITI bit for this cpu.  */ +    s->cchip.misc |= 1 << (cpu + 4); +    cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); +} + +PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, +                     qemu_irq *p_rtc_irq, +                     AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) +{ +    const uint64_t MB = 1024 * 1024; +    const uint64_t GB = 1024 * MB; +    MemoryRegion *addr_space = get_system_memory(); +    DeviceState *dev; +    TyphoonState *s; +    PCIHostState *phb; +    PCIBus *b; +    int i; + +    dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); +    qdev_init_nofail(dev); + +    s = TYPHOON_PCI_HOST_BRIDGE(dev); +    phb = PCI_HOST_BRIDGE(dev); + +    s->cchip.misc = 0x800000000ull; /* Revision: Typhoon.  */ +    s->pchip.win[3].wba = 2;        /* Window 3 SG always enabled. */ + +    /* Remember the CPUs so that we can deliver interrupts to them.  */ +    for (i = 0; i < 4; i++) { +        AlphaCPU *cpu = cpus[i]; +        s->cchip.cpu[i] = cpu; +        if (cpu != NULL) { +            cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, +                                                 typhoon_alarm_timer, +                                                 (void *)((uintptr_t)s + i)); +        } +    } + +    *p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0); + +    /* Main memory region, 0x00.0000.0000.  Real hardware supports 32GB, +       but the address space hole reserved at this point is 8TB.  */ +    memory_region_allocate_system_memory(&s->ram_region, OBJECT(s), "ram", +                                         ram_size); +    memory_region_add_subregion(addr_space, 0, &s->ram_region); + +    /* TIGbus, 0x801.0000.0000, 1GB.  */ +    /* ??? The TIGbus is used for delivering interrupts, and access to +       the flash ROM.  I'm not sure that we need to implement it at all.  */ + +    /* Pchip0 CSRs, 0x801.8000.0000, 256MB.  */ +    memory_region_init_io(&s->pchip.region, OBJECT(s), &pchip_ops, s, "pchip0", +                          256*MB); +    memory_region_add_subregion(addr_space, 0x80180000000ULL, +                                &s->pchip.region); + +    /* Cchip CSRs, 0x801.A000.0000, 256MB.  */ +    memory_region_init_io(&s->cchip.region, OBJECT(s), &cchip_ops, s, "cchip0", +                          256*MB); +    memory_region_add_subregion(addr_space, 0x801a0000000ULL, +                                &s->cchip.region); + +    /* Dchip CSRs, 0x801.B000.0000, 256MB.  */ +    memory_region_init_io(&s->dchip_region, OBJECT(s), &dchip_ops, s, "dchip0", +                          256*MB); +    memory_region_add_subregion(addr_space, 0x801b0000000ULL, +                                &s->dchip_region); + +    /* Pchip0 PCI memory, 0x800.0000.0000, 4GB.  */ +    memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4*GB); +    memory_region_add_subregion(addr_space, 0x80000000000ULL, +                                &s->pchip.reg_mem); + +    /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB.  */ +    memory_region_init_io(&s->pchip.reg_io, OBJECT(s), &alpha_pci_ignore_ops, +                          NULL, "pci0-io", 32*MB); +    memory_region_add_subregion(addr_space, 0x801fc000000ULL, +                                &s->pchip.reg_io); + +    b = pci_register_bus(dev, "pci", +                         typhoon_set_irq, sys_map_irq, s, +                         &s->pchip.reg_mem, &s->pchip.reg_io, +                         0, 64, TYPE_PCI_BUS); +    phb->bus = b; + +    /* Host memory as seen from the PCI side, via the IOMMU.  */ +    memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, +                             "iommu-typhoon", UINT64_MAX); +    address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); +    pci_setup_iommu(b, typhoon_pci_dma_iommu, s); + +    /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB.  */ +    memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, +                          b, "pci0-iack", 64*MB); +    memory_region_add_subregion(addr_space, 0x801f8000000ULL, +                                &s->pchip.reg_iack); + +    /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB.  */ +    memory_region_init_io(&s->pchip.reg_conf, OBJECT(s), &alpha_pci_conf1_ops, +                          b, "pci0-conf", 16*MB); +    memory_region_add_subregion(addr_space, 0x801fe000000ULL, +                                &s->pchip.reg_conf); + +    /* For the record, these are the mappings for the second PCI bus. +       We can get away with not implementing them because we indicate +       via the Cchip.CSC<PIP> bit that Pchip1 is not present.  */ +    /* Pchip1 PCI memory, 0x802.0000.0000, 4GB.  */ +    /* Pchip1 CSRs, 0x802.8000.0000, 256MB.  */ +    /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB.  */ +    /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB.  */ +    /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB.  */ + +    /* Init the ISA bus.  */ +    /* ??? Technically there should be a cy82c693ub pci-isa bridge.  */ +    { +        qemu_irq *isa_irqs; + +        *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io); +        isa_irqs = i8259_init(*isa_bus, +                              qemu_allocate_irq(typhoon_set_isa_irq, s, 0)); +        isa_bus_irqs(*isa_bus, isa_irqs); +    } + +    return b; +} + +static int typhoon_pcihost_init(SysBusDevice *dev) +{ +    return 0; +} + +static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) +{ +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + +    k->init = typhoon_pcihost_init; +} + +static const TypeInfo typhoon_pcihost_info = { +    .name          = TYPE_TYPHOON_PCI_HOST_BRIDGE, +    .parent        = TYPE_PCI_HOST_BRIDGE, +    .instance_size = sizeof(TyphoonState), +    .class_init    = typhoon_pcihost_class_init, +}; + +static void typhoon_register_types(void) +{ +    type_register_static(&typhoon_pcihost_info); +} + +type_init(typhoon_register_types)  | 
