aboutsummaryrefslogtreecommitdiffstats
path: root/hw/alpha
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
committerTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
commite02cda008591317b1625707ff8e115a4841aa889 (patch)
treeaee302e3cf8b59ec2d32ec481be3d1afddfc8968 /hw/alpha
parentcc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff)
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback design to work with QEMU and rust-vmm vhost-user backend without require any changes. Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'hw/alpha')
-rw-r--r--hw/alpha/Kconfig11
-rw-r--r--hw/alpha/alpha_sys.h21
-rw-r--r--hw/alpha/dp264.c222
-rw-r--r--hw/alpha/meson.build8
-rw-r--r--hw/alpha/pci.c91
-rw-r--r--hw/alpha/trace-events4
-rw-r--r--hw/alpha/trace.h1
-rw-r--r--hw/alpha/typhoon.c952
8 files changed, 1310 insertions, 0 deletions
diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig
new file mode 100644
index 000000000..9af650c94
--- /dev/null
+++ b/hw/alpha/Kconfig
@@ -0,0 +1,11 @@
+config DP264
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ imply E1000_PCI
+ select I82378
+ select IDE_CMD646
+ select MC146818RTC
+ select PCI
+ select PCKBD
+ select SMC37C669
diff --git a/hw/alpha/alpha_sys.h b/hw/alpha/alpha_sys.h
new file mode 100644
index 000000000..2263e821d
--- /dev/null
+++ b/hw/alpha/alpha_sys.h
@@ -0,0 +1,21 @@
+/* Alpha cores and system support chips. */
+
+#ifndef HW_ALPHA_SYS_H
+#define HW_ALPHA_SYS_H
+
+#include "target/alpha/cpu-qom.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/boards.h"
+#include "hw/intc/i8259.h"
+
+
+PCIBus *typhoon_init(MemoryRegion *, qemu_irq *, qemu_irq *, AlphaCPU *[4],
+ pci_map_irq_fn, uint8_t devfn_min);
+
+/* 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 000000000..c78ed96d0
--- /dev/null
+++ b/hw/alpha/dp264.c
@@ -0,0 +1,222 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "elf.h"
+#include "hw/loader.h"
+#include "alpha_sys.h"
+#include "qemu/error-report.h"
+#include "hw/rtc/mc146818rtc.h"
+#include "hw/ide/pci.h"
+#include "hw/isa/superio.h"
+#include "net/net.h"
+#include "qemu/cutils.h"
+#include "qemu/datadir.h"
+#include "net/net.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 *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;
+ PCIDevice *pci_dev;
+ DeviceState *i82378_dev;
+ ISABus *isa_bus;
+ qemu_irq rtc_irq;
+ qemu_irq isa_irq;
+ long size, i;
+ char *palcode_filename;
+ uint64_t palcode_entry;
+ uint64_t kernel_entry, kernel_low;
+ unsigned int smp_cpus = machine->smp.cpus;
+
+ /* Create up to 4 cpus. */
+ memset(cpus, 0, sizeof(cpus));
+ for (i = 0; i < smp_cpus; ++i) {
+ cpus[i] = ALPHA_CPU(cpu_create(machine->cpu_type));
+ }
+
+ /*
+ * arg0 -> memory size
+ * arg1 -> kernel entry point
+ * arg2 -> config word
+ *
+ * Config word: bits 0-5 -> ncpus
+ * bit 6 -> nographics option (for HWRPB CTB)
+ *
+ * See init_hwrpb() in the PALcode.
+ */
+ cpus[0]->env.trap_arg0 = ram_size;
+ cpus[0]->env.trap_arg1 = 0;
+ cpus[0]->env.trap_arg2 = smp_cpus | (!machine->enable_graphics << 6);
+
+ /*
+ * Init the chipset. Because we're using CLIPPER IRQ mappings,
+ * the minimum PCI device IdSel is 1.
+ */
+ pci_bus = typhoon_init(machine->ram, &isa_irq, &rtc_irq, cpus,
+ clipper_pci_map_irq, PCI_DEVFN(1, 0));
+
+ /*
+ * Init the PCI -> ISA bridge.
+ *
+ * Technically, PCI-based Alphas shipped with one of three different
+ * PCI-ISA bridges:
+ *
+ * - Intel i82378 SIO
+ * - Cypress CY82c693UB
+ * - ALI M1533
+ *
+ * (An Intel i82375 PCI-EISA bridge was also used on some models.)
+ *
+ * For simplicity, we model an i82378 here, even though it wouldn't
+ * have been on any Tsunami/Typhoon systems; it's close enough, and
+ * we don't want to deal with modelling the CY82c693UB (which has
+ * incompatible edge/level control registers, plus other peripherals
+ * like IDE and USB) or the M1533 (which also has IDE and USB).
+ *
+ * Importantly, we need to provide a PCI device node for it, otherwise
+ * some operating systems won't notice there's an ISA bus to configure.
+ */
+ i82378_dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(7, 0), "i82378"));
+ isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0"));
+
+ /* Connect the ISA PIC to the Typhoon IRQ used for ISA interrupts. */
+ qdev_connect_gpio_out(i82378_dev, 0, isa_irq);
+
+ /* Since we have an SRM-compatible PALcode, use the SRM epoch. */
+ mc146818_rtc_init(isa_bus, 1900, rtc_irq);
+
+ /* VGA setup. Don't bother loading the bios. */
+ pci_vga_init(pci_bus);
+
+ /* 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);
+ }
+
+ /* Super I/O */
+ isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO);
+
+ /* IDE disk setup. */
+ pci_dev = pci_create_simple(pci_bus, -1, "cmd646-ide");
+ pci_ide_create_devs(pci_dev);
+
+ /* 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,
+ machine->firmware ?: "palcode-clipper");
+ if (palcode_filename == NULL) {
+ error_report("no palcode provided");
+ exit(1);
+ }
+ size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys,
+ NULL, &palcode_entry, NULL, NULL, NULL,
+ 0, EM_ALPHA, 0, 0);
+ if (size < 0) {
+ error_report("could not load palcode '%s'", 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.pc = palcode_entry;
+ cpus[i]->env.palbr = palcode_entry;
+ }
+
+ /* Load a kernel. */
+ if (kernel_filename) {
+ uint64_t param_offset;
+
+ size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys,
+ NULL, &kernel_entry, &kernel_low, NULL, NULL,
+ 0, EM_ALPHA, 0, 0);
+ if (size < 0) {
+ error_report("could not load kernel '%s'", 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;
+ int64_t initrd_size;
+
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ error_report("could not load initial ram disk '%s'",
+ 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 void clipper_machine_init(MachineClass *mc)
+{
+ mc->desc = "Alpha DP264/CLIPPER";
+ mc->init = clipper_init;
+ mc->block_default_type = IF_IDE;
+ mc->max_cpus = 4;
+ mc->is_default = true;
+ mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67");
+ mc->default_ram_id = "ram";
+}
+
+DEFINE_MACHINE("clipper", clipper_machine_init)
diff --git a/hw/alpha/meson.build b/hw/alpha/meson.build
new file mode 100644
index 000000000..81ca21577
--- /dev/null
+++ b/hw/alpha/meson.build
@@ -0,0 +1,8 @@
+alpha_ss = ss.source_set()
+alpha_ss.add(when: 'CONFIG_DP264', if_true: files(
+ 'dp264.c',
+ 'pci.c',
+ 'typhoon.c',
+))
+
+hw_arch += {'alpha': alpha_ss}
diff --git a/hw/alpha/pci.c b/hw/alpha/pci.c
new file mode 100644
index 000000000..72251fcdf
--- /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 "qemu/osdep.h"
+#include "alpha_sys.h"
+#include "qemu/log.h"
+#include "trace.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)
+{
+ trace_alpha_pci_iack_write();
+}
+
+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/trace-events b/hw/alpha/trace-events
new file mode 100644
index 000000000..952a81640
--- /dev/null
+++ b/hw/alpha/trace-events
@@ -0,0 +1,4 @@
+# See docs/devel/tracing.rst for syntax documentation.
+
+# pci.c
+alpha_pci_iack_write(void) ""
diff --git a/hw/alpha/trace.h b/hw/alpha/trace.h
new file mode 100644
index 000000000..20fe69819
--- /dev/null
+++ b/hw/alpha/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_alpha.h"
diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c
new file mode 100644
index 000000000..bd39c8ca8
--- /dev/null
+++ b/hw/alpha/typhoon.c
@@ -0,0 +1,952 @@
+/*
+ * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
+ *
+ * Written by Richard Henderson.
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "hw/irq.h"
+#include "alpha_sys.h"
+#include "qom/object.h"
+
+
+#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost"
+#define TYPE_TYPHOON_IOMMU_MEMORY_REGION "typhoon-iommu-memory-region"
+
+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;
+ IOMMUMemoryRegion iommu;
+
+ uint64_t ctl;
+ TyphoonWindow win[4];
+} TyphoonPchip;
+
+OBJECT_DECLARE_SIMPLE_TYPE(TyphoonState, TYPHOON_PCI_HOST_BRIDGE)
+
+struct TyphoonState {
+ PCIHostState parent_obj;
+
+ TyphoonCchip cchip;
+ TyphoonPchip pchip;
+ MemoryRegion dchip_region;
+};
+
+/* 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 MemTxResult cchip_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ 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:
+ return MEMTX_ERROR;
+ }
+
+ *data = ret;
+ return MEMTX_OK;
+}
+
+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 MemTxResult pchip_read(void *opaque, hwaddr addr, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ 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:
+ return MEMTX_ERROR;
+ }
+
+ *data = ret;
+ return MEMTX_OK;
+}
+
+static MemTxResult cchip_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ 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[1] = 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:
+ return MEMTX_ERROR;
+ }
+
+ return MEMTX_OK;
+}
+
+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 MemTxResult pchip_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ 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:
+ return MEMTX_ERROR;
+ }
+
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps cchip_ops = {
+ .read_with_attrs = cchip_read,
+ .write_with_attrs = 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_with_attrs = pchip_read,
+ .write_with_attrs = 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(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flag,
+ int iommu_idx)
+{
+ 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 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(MemoryRegion *ram, qemu_irq *p_isa_irq,
+ qemu_irq *p_rtc_irq, AlphaCPU *cpus[4],
+ pci_map_irq_fn sys_map_irq, uint8_t devfn_min)
+{
+ MemoryRegion *addr_space = get_system_memory();
+ DeviceState *dev;
+ TyphoonState *s;
+ PCIHostState *phb;
+ PCIBus *b;
+ int i;
+
+ dev = qdev_new(TYPE_TYPHOON_PCI_HOST_BRIDGE);
+
+ 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_isa_irq = qemu_allocate_irq(typhoon_set_isa_irq, s, 0);
+ *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_add_subregion(addr_space, 0, ram);
+
+ /* 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 * MiB);
+ 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 * MiB);
+ 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 * MiB);
+ 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 * GiB);
+ 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 * MiB);
+ memory_region_add_subregion(addr_space, 0x801fc000000ULL,
+ &s->pchip.reg_io);
+
+ b = pci_register_root_bus(dev, "pci",
+ typhoon_set_irq, sys_map_irq, s,
+ &s->pchip.reg_mem, &s->pchip.reg_io,
+ devfn_min, 64, TYPE_PCI_BUS);
+ phb->bus = b;
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ /* Host memory as seen from the PCI side, via the IOMMU. */
+ memory_region_init_iommu(&s->pchip.iommu, sizeof(s->pchip.iommu),
+ TYPE_TYPHOON_IOMMU_MEMORY_REGION, OBJECT(s),
+ "iommu-typhoon", UINT64_MAX);
+ address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&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 * MiB);
+ 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 * MiB);
+ 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. */
+
+ return b;
+}
+
+static const TypeInfo typhoon_pcihost_info = {
+ .name = TYPE_TYPHOON_PCI_HOST_BRIDGE,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(TyphoonState),
+};
+
+static void typhoon_iommu_memory_region_class_init(ObjectClass *klass,
+ void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = typhoon_translate_iommu;
+}
+
+static const TypeInfo typhoon_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_TYPHOON_IOMMU_MEMORY_REGION,
+ .class_init = typhoon_iommu_memory_region_class_init,
+};
+
+static void typhoon_register_types(void)
+{
+ type_register_static(&typhoon_pcihost_info);
+ type_register_static(&typhoon_iommu_memory_region_info);
+}
+
+type_init(typhoon_register_types)