summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/virtio-9p.c8
-rw-r--r--hw/alpha_dp264.c2
-rw-r--r--hw/alpha_sys.h3
-rw-r--r--hw/alpha_typhoon.c14
-rw-r--r--hw/an5206.c2
-rw-r--r--hw/apb_pci.c49
-rw-r--r--hw/apb_pci.h3
-rw-r--r--hw/apic.c126
-rw-r--r--hw/apic.h4
-rw-r--r--hw/apic_common.c69
-rw-r--r--hw/apic_internal.h27
-rw-r--r--hw/arm-misc.h9
-rw-r--r--hw/arm11mpcore.c22
-rw-r--r--hw/arm_boot.c112
-rw-r--r--hw/arm_gic.c58
-rw-r--r--hw/arm_mptimer.c2
-rw-r--r--hw/arm_pic.c4
-rw-r--r--hw/armv7m.c4
-rw-r--r--hw/axis_dev88.c2
-rw-r--r--hw/cadence_gem.c1233
-rw-r--r--hw/cadence_ttc.c489
-rw-r--r--hw/cadence_uart.c513
-rw-r--r--hw/cris-boot.c6
-rw-r--r--hw/cris-boot.h2
-rw-r--r--hw/cris_pic_cpu.c4
-rw-r--r--hw/cuda.c2
-rw-r--r--hw/ds1338.c1
-rw-r--r--hw/dummy_m68k.c2
-rw-r--r--hw/etraxfs.h2
-rw-r--r--hw/etraxfs_ser.c4
-rw-r--r--hw/exynos4210.h2
-rw-r--r--hw/exynos4210_mct.c2
-rw-r--r--hw/fdc.c142
-rw-r--r--hw/grlib.h2
-rw-r--r--hw/highbank.c8
-rw-r--r--hw/i8254.c281
-rw-r--r--hw/i8254.h11
-rw-r--r--hw/i8254_common.c311
-rw-r--r--hw/i8254_internal.h85
-rw-r--r--hw/ide/ahci.c4
-rw-r--r--hw/ide/cmd646.c6
-rw-r--r--hw/ide/core.c24
-rw-r--r--hw/ide/macio.c2
-rw-r--r--hw/ide/piix.c2
-rw-r--r--hw/ide/via.c2
-rw-r--r--hw/integratorcp.c2
-rw-r--r--hw/ioapic.c2
-rw-r--r--hw/kvm/apic.c34
-rw-r--r--hw/kvm/clock.c2
-rw-r--r--hw/kvm/i8254.c254
-rw-r--r--hw/kvmvapic.c807
-rw-r--r--hw/leon3.c10
-rw-r--r--hw/lm32_boards.c12
-rw-r--r--hw/mc146818rtc.c7
-rw-r--r--hw/mcf.h4
-rw-r--r--hw/mcf5206.c4
-rw-r--r--hw/mcf5208.c2
-rw-r--r--hw/mcf_intc.c4
-rw-r--r--hw/microblaze_boot.c177
-rw-r--r--hw/microblaze_boot.h10
-rw-r--r--hw/microblaze_pic_cpu.c4
-rw-r--r--hw/microblaze_pic_cpu.h2
-rw-r--r--hw/milkymist.c10
-rw-r--r--hw/mips_cpudevs.h4
-rw-r--r--hw/mips_fulong2e.c19
-rw-r--r--hw/mips_int.c6
-rw-r--r--hw/mips_jazz.c8
-rw-r--r--hw/mips_malta.c99
-rw-r--r--hw/mips_mipssim.c8
-rw-r--r--hw/mips_r4k.c8
-rw-r--r--hw/mips_timer.c20
-rw-r--r--hw/mipsnet.c2
-rw-r--r--hw/mpc8544_guts.c2
-rw-r--r--hw/musicpal.c2
-rw-r--r--hw/nseries.c12
-rw-r--r--hw/omap.h15
-rw-r--r--hw/omap1.c15
-rw-r--r--hw/omap2.c37
-rw-r--r--hw/omap_i2c.c107
-rw-r--r--hw/opencores_eth.c4
-rw-r--r--hw/openpic.c12
-rw-r--r--hw/pc.c39
-rw-r--r--hw/pc_piix.c32
-rw-r--r--hw/pci.c1
-rw-r--r--hw/petalogix_ml605_mmu.c146
-rw-r--r--hw/petalogix_s3adsp1800_mmu.c149
-rw-r--r--hw/ppc.c123
-rw-r--r--hw/ppc.h28
-rw-r--r--hw/ppc405.h8
-rw-r--r--hw/ppc405_uc.c52
-rw-r--r--hw/ppc440_bamboo.c8
-rw-r--r--hw/ppc4xx.h8
-rw-r--r--hw/ppc4xx_devs.c17
-rw-r--r--hw/ppc_booke.c20
-rw-r--r--hw/ppc_newworld.c14
-rw-r--r--hw/ppc_oldworld.c14
-rw-r--r--hw/ppc_prep.c16
-rw-r--r--hw/ppce500_mpc8544ds.c16
-rw-r--r--hw/ppce500_spin.c10
-rw-r--r--hw/pxa.h6
-rw-r--r--hw/pxa2xx.c5
-rw-r--r--hw/pxa2xx_dma.c12
-rw-r--r--hw/pxa2xx_gpio.c4
-rw-r--r--hw/pxa2xx_lcd.c12
-rw-r--r--hw/pxa2xx_pic.c4
-rw-r--r--hw/qdev-properties.c4
-rw-r--r--hw/qxl-render.c167
-rw-r--r--hw/qxl.c367
-rw-r--r--hw/qxl.h31
-rw-r--r--hw/r2d.c8
-rw-r--r--hw/realview.c5
-rw-r--r--hw/s390-virtio-bus.c6
-rw-r--r--hw/s390-virtio.c46
-rw-r--r--hw/scsi-bus.c18
-rw-r--r--hw/scsi-disk.c49
-rw-r--r--hw/sh.h2
-rw-r--r--hw/sh7750.c2
-rw-r--r--hw/sh_intc.c2
-rw-r--r--hw/shix.c2
-rw-r--r--hw/spapr.c22
-rw-r--r--hw/spapr.h17
-rw-r--r--hw/spapr_hcall.c42
-rw-r--r--hw/spapr_llan.c10
-rw-r--r--hw/spapr_pci.c195
-rw-r--r--hw/spapr_pci.h4
-rw-r--r--hw/spapr_rtas.c4
-rw-r--r--hw/spapr_vio.c14
-rw-r--r--hw/spapr_vty.c4
-rw-r--r--hw/strongarm.h2
-rw-r--r--hw/sun4m.c16
-rw-r--r--hw/sun4u.c83
-rw-r--r--hw/usb-ohci.h9
-rw-r--r--hw/usb-uhci.h10
-rw-r--r--hw/usb.h22
-rw-r--r--hw/usb/bus.c (renamed from hw/usb-bus.c)24
-rw-r--r--hw/usb/core.c (renamed from hw/usb.c)133
-rw-r--r--hw/usb/desc.c (renamed from hw/usb-desc.c)24
-rw-r--r--hw/usb/desc.h (renamed from hw/usb-desc.h)0
-rw-r--r--hw/usb/dev-audio.c (renamed from hw/usb-audio.c)8
-rw-r--r--hw/usb/dev-bluetooth.c (renamed from hw/usb-bt.c)10
-rw-r--r--hw/usb/dev-hid.c (renamed from hw/usb-hid.c)11
-rw-r--r--hw/usb/dev-hub.c (renamed from hw/usb-hub.c)4
-rw-r--r--hw/usb/dev-network.c (renamed from hw/usb-net.c)8
-rw-r--r--hw/usb/dev-serial.c (renamed from hw/usb-serial.c)12
-rw-r--r--hw/usb/dev-smartcard-reader.c (renamed from hw/usb-ccid.c)8
-rw-r--r--hw/usb/dev-storage.c (renamed from hw/usb-msd.c)16
-rw-r--r--hw/usb/dev-wacom.c (renamed from hw/usb-wacom.c)6
-rw-r--r--hw/usb/hcd-ehci.c (renamed from hw/usb-ehci.c)263
-rw-r--r--hw/usb/hcd-musb.c (renamed from hw/usb-musb.c)6
-rw-r--r--hw/usb/hcd-ohci.c (renamed from hw/usb-ohci.c)35
-rw-r--r--hw/usb/hcd-uhci.c (renamed from hw/usb-uhci.c)438
-rw-r--r--hw/usb/hcd-xhci.c (renamed from hw/usb-xhci.c)31
-rw-r--r--hw/usb/host-bsd.c647
-rw-r--r--hw/usb/host-linux.c1913
-rw-r--r--hw/usb/host-stub.c52
-rw-r--r--hw/usb/libhw.c (renamed from hw/usb-libhw.c)2
-rw-r--r--hw/usb/redirect.c1485
-rw-r--r--hw/versatilepb.c5
-rw-r--r--hw/vexpress.c4
-rw-r--r--hw/vga.c2
-rw-r--r--hw/vhost.c33
-rw-r--r--hw/virtex_ml507.c12
-rw-r--r--hw/virtio-scsi.c2
-rw-r--r--hw/vmport.c12
-rw-r--r--hw/xen_machine_pv.c2
-rw-r--r--hw/xics.c135
-rw-r--r--hw/xics.h8
-rw-r--r--hw/xilinx_zynq.c157
-rw-r--r--hw/xtensa_lx60.c10
-rw-r--r--hw/xtensa_pic.c16
-rw-r--r--hw/xtensa_sim.c4
-rw-r--r--hw/zynq_slcr.c535
172 files changed, 11246 insertions, 2123 deletions
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index a72ffc3390..c633fb9b7e 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -1349,7 +1349,9 @@ static void v9fs_open(void *opaque)
     if (s->proto_version == V9FS_PROTO_2000L) {
         err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode);
     } else {
-        err = pdu_unmarshal(pdu, offset, "db", &fid, &mode);
+        uint8_t modebyte;
+        err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte);
+        mode = modebyte;
     }
     if (err < 0) {
         goto out_nofid;
@@ -3260,9 +3262,9 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
 
         ptr = pdu->elem.out_sg[0].iov_base;
 
-        memcpy(&pdu->size, ptr, 4);
+        pdu->size = le32_to_cpu(*(uint32_t *)ptr);
         pdu->id = ptr[4];
-        memcpy(&pdu->tag, ptr + 5, 2);
+        pdu->tag = le16_to_cpu(*(uint16_t *)(ptr + 5));
         qemu_co_queue_init(&pdu->complete);
         submit_pdu(s, pdu);
     }
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
index ea0fd95f84..9eb939f383 100644
--- a/hw/alpha_dp264.c
+++ b/hw/alpha_dp264.c
@@ -49,7 +49,7 @@ static void clipper_init(ram_addr_t ram_size,
                          const char *initrd_filename,
                          const char *cpu_model)
 {
-    CPUState *cpus[4];
+    CPUAlphaState *cpus[4];
     PCIBus *pci_bus;
     ISABus *isa_bus;
     qemu_irq rtc_irq;
diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h
index d54b18f8ed..de40f8b613 100644
--- a/hw/alpha_sys.h
+++ b/hw/alpha_sys.h
@@ -8,11 +8,10 @@
 #include "ide.h"
 #include "net.h"
 #include "pc.h"
-#include "usb-ohci.h"
 #include "irq.h"
 
 
-PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, CPUState *[4],
+PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, CPUAlphaState *[4],
                      pci_map_irq_fn);
 
 /* alpha_pci.c.  */
diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c
index b539416589..872e1122e8 100644
--- a/hw/alpha_typhoon.c
+++ b/hw/alpha_typhoon.c
@@ -21,7 +21,7 @@ typedef struct TyphoonCchip {
     uint64_t drir;
     uint64_t dim[4];
     uint32_t iic[4];
-    CPUState *cpu[4];
+    CPUAlphaState *cpu[4];
 } TyphoonCchip;
 
 typedef struct TyphoonWindow {
@@ -52,7 +52,7 @@ typedef struct TyphoonState {
 } TyphoonState;
 
 /* Called when one of DRIR or DIM changes.  */
-static void cpu_irq_change(CPUState *env, uint64_t req)
+static void cpu_irq_change(CPUAlphaState *env, uint64_t req)
 {
     /* If there are any non-masked interrupts, tell the cpu.  */
     if (env) {
@@ -66,7 +66,7 @@ static void cpu_irq_change(CPUState *env, uint64_t req)
 
 static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
 {
-    CPUState *env = cpu_single_env;
+    CPUAlphaState *env = cpu_single_env;
     TyphoonState *s = opaque;
     uint64_t ret = 0;
 
@@ -347,7 +347,7 @@ static void cchip_write(void *opaque, target_phys_addr_t addr,
         if ((newval ^ oldval) & 0xff0) {
             int i;
             for (i = 0; i < 4; ++i) {
-                CPUState *env = s->cchip.cpu[i];
+                CPUAlphaState *env = s->cchip.cpu[i];
                 if (env) {
                     /* IPI can be either cleared or set by the write.  */
                     if (newval & (1 << (i + 8))) {
@@ -655,7 +655,7 @@ static void typhoon_set_timer_irq(void *opaque, int irq, int level)
 
     /* Deliver the interrupt to each CPU, considering each CPU's IIC.  */
     for (i = 0; i < 4; ++i) {
-        CPUState *env = s->cchip.cpu[i];
+        CPUAlphaState *env = s->cchip.cpu[i];
         if (env) {
             uint32_t iic = s->cchip.iic[i];
 
@@ -693,7 +693,7 @@ static void typhoon_alarm_timer(void *opaque)
 
 PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
                      qemu_irq *p_rtc_irq,
-                     CPUState *cpus[4], pci_map_irq_fn sys_map_irq)
+                     CPUAlphaState *cpus[4], pci_map_irq_fn sys_map_irq)
 {
     const uint64_t MB = 1024 * 1024;
     const uint64_t GB = 1024 * MB;
@@ -713,7 +713,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
 
     /* Remember the CPUs so that we can deliver interrupts to them.  */
     for (i = 0; i < 4; i++) {
-        CPUState *env = cpus[i];
+        CPUAlphaState *env = cpus[i];
         s->cchip.cpu[i] = env;
         if (env) {
             env->alarm_timer = qemu_new_timer_ns(rtc_clock,
diff --git a/hw/an5206.c b/hw/an5206.c
index d57306d3ad..25407c0f50 100644
--- a/hw/an5206.c
+++ b/hw/an5206.c
@@ -24,7 +24,7 @@ static void an5206_init(ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUM68KState *env;
     int kernel_size;
     uint64_t elf_entry;
     target_phys_addr_t entry;
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 1d25da8da9..7e28808ec4 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -66,6 +66,8 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
 #define RESET_WCMASK 0x98000000
 #define RESET_WMASK  0x60000000
 
+#define MAX_IVEC 0x30
+
 typedef struct APBState {
     SysBusDevice busdev;
     PCIBus      *bus;
@@ -77,7 +79,8 @@ typedef struct APBState {
     uint32_t pci_control[16];
     uint32_t pci_irq_map[8];
     uint32_t obio_irq_map[32];
-    qemu_irq pci_irqs[32];
+    qemu_irq *pbm_irqs;
+    qemu_irq *ivec_irqs;
     uint32_t reset_control;
     unsigned int nr_resets;
 } APBState;
@@ -87,7 +90,7 @@ static void apb_config_writel (void *opaque, target_phys_addr_t addr,
 {
     APBState *s = opaque;
 
-    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val);
 
     switch (addr & 0xffff) {
     case 0x30 ... 0x4f: /* DMA error registers */
@@ -104,6 +107,12 @@ static void apb_config_writel (void *opaque, target_phys_addr_t addr,
             s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
         }
         break;
+    case 0x1000 ... 0x1080: /* OBIO interrupt control */
+        if (addr & 4) {
+            s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK;
+            s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK;
+        }
+        break;
     case 0x2000 ... 0x202f: /* PCI control */
         s->pci_control[(addr & 0x3f) >> 2] = val;
         break;
@@ -154,6 +163,13 @@ static uint64_t apb_config_readl (void *opaque,
             val = 0;
         }
         break;
+    case 0x1000 ... 0x1080: /* OBIO interrupt control */
+        if (addr & 4) {
+            val = s->obio_irq_map[(addr & 0xff) >> 3];
+        } else {
+            val = 0;
+        }
+        break;
     case 0x2000 ... 0x202f: /* PCI control */
         val = s->pci_control[(addr & 0x3f) >> 2];
         break;
@@ -190,7 +206,7 @@ static void apb_pci_config_write(void *opaque, target_phys_addr_t addr,
     APBState *s = opaque;
 
     val = qemu_bswap_len(val, size);
-    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val);
     pci_data_write(s->bus, addr, val, size);
 }
 
@@ -280,10 +296,19 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level)
     if (irq_num < 32) {
         if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
             APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
-            qemu_set_irq(s->pci_irqs[irq_num], level);
+            qemu_set_irq(s->ivec_irqs[irq_num], level);
         } else {
             APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
-            qemu_irq_lower(s->pci_irqs[irq_num]);
+            qemu_irq_lower(s->ivec_irqs[irq_num]);
+        }
+    } else {
+        /* OBIO IRQ map onto the next 16 INO.  */
+        if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) {
+            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
+            qemu_set_irq(s->ivec_irqs[irq_num], level);
+        } else {
+            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
+            qemu_irq_lower(s->ivec_irqs[irq_num]);
         }
     }
 }
@@ -316,12 +341,12 @@ static int apb_pci_bridge_initfn(PCIDevice *dev)
 
 PCIBus *pci_apb_init(target_phys_addr_t special_base,
                      target_phys_addr_t mem_base,
-                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
+                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
+                     qemu_irq **pbm_irqs)
 {
     DeviceState *dev;
     SysBusDevice *s;
     APBState *d;
-    unsigned int i;
     PCIDevice *pci_dev;
     PCIBridge *br;
 
@@ -346,9 +371,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
                               get_system_io(),
                               0, 32);
 
-    for (i = 0; i < 32; i++) {
-        sysbus_connect_irq(s, i, pic[i]);
-    }
+    *pbm_irqs = d->pbm_irqs;
+    d->ivec_irqs = ivec_irqs;
 
     pci_create_simple(d->bus, 0, "pbm-pci");
 
@@ -402,9 +426,7 @@ static int pci_pbm_init_device(SysBusDevice *dev)
     for (i = 0; i < 8; i++) {
         s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
     }
-    for (i = 0; i < 32; i++) {
-        sysbus_init_irq(dev, &s->pci_irqs[i]);
-    }
+    s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC);
 
     /* apb_config */
     memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config",
@@ -444,7 +466,6 @@ static void pbm_pci_host_class_init(ObjectClass *klass, void *data)
     k->vendor_id = PCI_VENDOR_ID_SUN;
     k->device_id = PCI_DEVICE_ID_SUN_SABRE;
     k->class_id = PCI_CLASS_BRIDGE_HOST;
-    k->is_bridge = 1;
 }
 
 static TypeInfo pbm_pci_host_info = {
diff --git a/hw/apb_pci.h b/hw/apb_pci.h
index 8869f9d326..55f7c4c3b2 100644
--- a/hw/apb_pci.h
+++ b/hw/apb_pci.h
@@ -5,5 +5,6 @@
 
 PCIBus *pci_apb_init(target_phys_addr_t special_base,
                      target_phys_addr_t mem_base,
-                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
+                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
+                     qemu_irq **pbm_irqs);
 #endif
diff --git a/hw/apic.c b/hw/apic.c
index ff9d24e914..4eeaf8801c 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -35,6 +35,10 @@
 #define MSI_ADDR_DEST_ID_SHIFT		12
 #define	MSI_ADDR_DEST_ID_MASK		0x00ffff0
 
+#define SYNC_FROM_VAPIC                 0x1
+#define SYNC_TO_VAPIC                   0x2
+#define SYNC_ISR_IRR_TO_VAPIC           0x4
+
 static APICCommonState *local_apics[MAX_APICS + 1];
 
 static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
@@ -78,6 +82,70 @@ static inline int get_bit(uint32_t *tab, int index)
     return !!(tab[i] & mask);
 }
 
+/* return -1 if no bit is set */
+static int get_highest_priority_int(uint32_t *tab)
+{
+    int i;
+    for (i = 7; i >= 0; i--) {
+        if (tab[i] != 0) {
+            return i * 32 + fls_bit(tab[i]);
+        }
+    }
+    return -1;
+}
+
+static void apic_sync_vapic(APICCommonState *s, int sync_type)
+{
+    VAPICState vapic_state;
+    size_t length;
+    off_t start;
+    int vector;
+
+    if (!s->vapic_paddr) {
+        return;
+    }
+    if (sync_type & SYNC_FROM_VAPIC) {
+        cpu_physical_memory_rw(s->vapic_paddr, (void *)&vapic_state,
+                               sizeof(vapic_state), 0);
+        s->tpr = vapic_state.tpr;
+    }
+    if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) {
+        start = offsetof(VAPICState, isr);
+        length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr);
+
+        if (sync_type & SYNC_TO_VAPIC) {
+            assert(qemu_cpu_is_self(s->cpu_env));
+
+            vapic_state.tpr = s->tpr;
+            vapic_state.enabled = 1;
+            start = 0;
+            length = sizeof(VAPICState);
+        }
+
+        vector = get_highest_priority_int(s->isr);
+        if (vector < 0) {
+            vector = 0;
+        }
+        vapic_state.isr = vector & 0xf0;
+
+        vapic_state.zero = 0;
+
+        vector = get_highest_priority_int(s->irr);
+        if (vector < 0) {
+            vector = 0;
+        }
+        vapic_state.irr = vector & 0xff;
+
+        cpu_physical_memory_write_rom(s->vapic_paddr + start,
+                                      ((void *)&vapic_state) + start, length);
+    }
+}
+
+static void apic_vapic_base_update(APICCommonState *s)
+{
+    apic_sync_vapic(s, SYNC_TO_VAPIC);
+}
+
 static void apic_local_deliver(APICCommonState *s, int vector)
 {
     uint32_t lvt = s->lvt[vector];
@@ -239,20 +307,17 @@ static void apic_set_base(APICCommonState *s, uint64_t val)
 
 static void apic_set_tpr(APICCommonState *s, uint8_t val)
 {
-    s->tpr = (val & 0x0f) << 4;
-    apic_update_irq(s);
+    /* Updates from cr8 are ignored while the VAPIC is active */
+    if (!s->vapic_paddr) {
+        s->tpr = val << 4;
+        apic_update_irq(s);
+    }
 }
 
-/* return -1 if no bit is set */
-static int get_highest_priority_int(uint32_t *tab)
+static uint8_t apic_get_tpr(APICCommonState *s)
 {
-    int i;
-    for(i = 7; i >= 0; i--) {
-        if (tab[i] != 0) {
-            return i * 32 + fls_bit(tab[i]);
-        }
-    }
-    return -1;
+    apic_sync_vapic(s, SYNC_FROM_VAPIC);
+    return s->tpr >> 4;
 }
 
 static int apic_get_ppr(APICCommonState *s)
@@ -312,6 +377,14 @@ static void apic_update_irq(APICCommonState *s)
     }
 }
 
+void apic_poll_irq(DeviceState *d)
+{
+    APICCommonState *s = APIC_COMMON(d);
+
+    apic_sync_vapic(s, SYNC_FROM_VAPIC);
+    apic_update_irq(s);
+}
+
 static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode)
 {
     apic_report_irq_delivered(!get_bit(s->irr, vector_num));
@@ -321,6 +394,16 @@ static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode)
         set_bit(s->tmr, vector_num);
     else
         reset_bit(s->tmr, vector_num);
+    if (s->vapic_paddr) {
+        apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC);
+        /*
+         * The vcpu thread needs to see the new IRR before we pull its current
+         * TPR value. That way, if we miss a lowering of the TRP, the guest
+         * has the chance to notice the new IRR and poll for IRQs on its own.
+         */
+        smp_wmb();
+        apic_sync_vapic(s, SYNC_FROM_VAPIC);
+    }
     apic_update_irq(s);
 }
 
@@ -334,6 +417,7 @@ static void apic_eoi(APICCommonState *s)
     if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) {
         ioapic_eoi_broadcast(isrv);
     }
+    apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC);
     apic_update_irq(s);
 }
 
@@ -471,15 +555,19 @@ int apic_get_interrupt(DeviceState *d)
     if (!(s->spurious_vec & APIC_SV_ENABLE))
         return -1;
 
+    apic_sync_vapic(s, SYNC_FROM_VAPIC);
     intno = apic_irq_pending(s);
 
     if (intno == 0) {
+        apic_sync_vapic(s, SYNC_TO_VAPIC);
         return -1;
     } else if (intno < 0) {
+        apic_sync_vapic(s, SYNC_TO_VAPIC);
         return s->spurious_vec & 0xff;
     }
     reset_bit(s->irr, intno);
     set_bit(s->isr, intno);
+    apic_sync_vapic(s, SYNC_TO_VAPIC);
     apic_update_irq(s);
     return intno;
 }
@@ -576,6 +664,10 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
         val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
         break;
     case 0x08:
+        apic_sync_vapic(s, SYNC_FROM_VAPIC);
+        if (apic_report_tpr_access) {
+            cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_READ);
+        }
         val = s->tpr;
         break;
     case 0x09:
@@ -675,7 +767,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
     case 0x03:
         break;
     case 0x08:
+        if (apic_report_tpr_access) {
+            cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_WRITE);
+        }
         s->tpr = val;
+        apic_sync_vapic(s, SYNC_TO_VAPIC);
         apic_update_irq(s);
         break;
     case 0x09:
@@ -737,6 +833,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
     }
 }
 
+static void apic_pre_save(APICCommonState *s)
+{
+    apic_sync_vapic(s, SYNC_FROM_VAPIC);
+}
+
 static void apic_post_load(APICCommonState *s)
 {
     if (s->timer_expiry != -1) {
@@ -770,7 +871,10 @@ static void apic_class_init(ObjectClass *klass, void *data)
     k->init = apic_init;
     k->set_base = apic_set_base;
     k->set_tpr = apic_set_tpr;
+    k->get_tpr = apic_get_tpr;
+    k->vapic_base_update = apic_vapic_base_update;
     k->external_nmi = apic_external_nmi;
+    k->pre_save = apic_pre_save;
     k->post_load = apic_post_load;
 }
 
diff --git a/hw/apic.h b/hw/apic.h
index a62d83ba9f..62179cebee 100644
--- a/hw/apic.h
+++ b/hw/apic.h
@@ -18,9 +18,11 @@ void cpu_set_apic_tpr(DeviceState *s, uint8_t val);
 uint8_t cpu_get_apic_tpr(DeviceState *s);
 void apic_init_reset(DeviceState *s);
 void apic_sipi(DeviceState *s);
+void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
+                                   TPRAccess access);
 
 /* pc.c */
-int cpu_is_bsp(CPUState *env);
+int cpu_is_bsp(CPUX86State *env);
 DeviceState *cpu_get_current_apic(void);
 
 #endif
diff --git a/hw/apic_common.c b/hw/apic_common.c
index c91f7d5391..60b82596e7 100644
--- a/hw/apic_common.c
+++ b/hw/apic_common.c
@@ -20,8 +20,10 @@
 #include "apic.h"
 #include "apic_internal.h"
 #include "trace.h"
+#include "kvm.h"
 
 static int apic_irq_delivered;
+bool apic_report_tpr_access;
 
 void cpu_set_apic_base(DeviceState *d, uint64_t val)
 {
@@ -63,9 +65,45 @@ void cpu_set_apic_tpr(DeviceState *d, uint8_t val)
 
 uint8_t cpu_get_apic_tpr(DeviceState *d)
 {
+    APICCommonState *s;
+    APICCommonClass *info;
+
+    if (!d) {
+        return 0;
+    }
+
+    s = APIC_COMMON(d);
+    info = APIC_COMMON_GET_CLASS(s);
+
+    return info->get_tpr(s);
+}
+
+void apic_enable_tpr_access_reporting(DeviceState *d, bool enable)
+{
     APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+    APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
 
-    return s ? s->tpr >> 4 : 0;
+    apic_report_tpr_access = enable;
+    if (info->enable_tpr_reporting) {
+        info->enable_tpr_reporting(s, enable);
+    }
+}
+
+void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr)
+{
+    APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+    APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+    s->vapic_paddr = paddr;
+    info->vapic_base_update(s);
+}
+
+void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
+                                   TPRAccess access)
+{
+    APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+
+    vapic_report_tpr_access(s->vapic, s->cpu_env, ip, access);
 }
 
 void apic_report_irq_delivered(int delivered)
@@ -166,12 +204,16 @@ void apic_init_reset(DeviceState *d)
 static void apic_reset_common(DeviceState *d)
 {
     APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+    APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
     bool bsp;
 
     bsp = cpu_is_bsp(s->cpu_env);
     s->apicbase = 0xfee00000 |
         (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
 
+    s->vapic_paddr = 0;
+    info->vapic_base_update(s);
+
     apic_init_reset(d);
 
     if (bsp) {
@@ -234,6 +276,7 @@ static int apic_init_common(SysBusDevice *dev)
 {
     APICCommonState *s = APIC_COMMON(dev);
     APICCommonClass *info;
+    static DeviceState *vapic;
     static int apic_no;
 
     if (apic_no >= MAX_APICS) {
@@ -244,10 +287,29 @@ static int apic_init_common(SysBusDevice *dev)
     info = APIC_COMMON_GET_CLASS(s);
     info->init(s);
 
-    sysbus_init_mmio(&s->busdev, &s->io_memory);
+    sysbus_init_mmio(dev, &s->io_memory);
+
+    if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) {
+        vapic = sysbus_create_simple("kvmvapic", -1, NULL);
+    }
+    s->vapic = vapic;
+    if (apic_report_tpr_access && info->enable_tpr_reporting) {
+        info->enable_tpr_reporting(s, true);
+    }
+
     return 0;
 }
 
+static void apic_dispatch_pre_save(void *opaque)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+    if (info->pre_save) {
+        info->pre_save(s);
+    }
+}
+
 static int apic_dispatch_post_load(void *opaque, int version_id)
 {
     APICCommonState *s = APIC_COMMON(opaque);
@@ -265,6 +327,7 @@ static const VMStateDescription vmstate_apic_common = {
     .minimum_version_id = 3,
     .minimum_version_id_old = 1,
     .load_state_old = apic_load_old,
+    .pre_save = apic_dispatch_pre_save,
     .post_load = apic_dispatch_post_load,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(apicbase, APICCommonState),
@@ -294,6 +357,8 @@ static const VMStateDescription vmstate_apic_common = {
 static Property apic_properties_common[] = {
     DEFINE_PROP_UINT8("id", APICCommonState, id, -1),
     DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env),
+    DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT,
+                    true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/apic_internal.h b/hw/apic_internal.h
index 0cab010717..60a6a8bdae 100644
--- a/hw/apic_internal.h
+++ b/hw/apic_internal.h
@@ -61,6 +61,9 @@
 #define APIC_SV_DIRECTED_IO             (1<<12)
 #define APIC_SV_ENABLE                  (1<<8)
 
+#define VAPIC_ENABLE_BIT                0
+#define VAPIC_ENABLE_MASK               (1 << VAPIC_ENABLE_BIT)
+
 #define MAX_APICS 255
 
 #define MSI_SPACE_SIZE                  0x100000
@@ -82,7 +85,11 @@ typedef struct APICCommonClass
     void (*init)(APICCommonState *s);
     void (*set_base)(APICCommonState *s, uint64_t val);
     void (*set_tpr)(APICCommonState *s, uint8_t val);
+    uint8_t (*get_tpr)(APICCommonState *s);
+    void (*enable_tpr_reporting)(APICCommonState *s, bool enable);
+    void (*vapic_base_update)(APICCommonState *s);
     void (*external_nmi)(APICCommonState *s);
+    void (*pre_save)(APICCommonState *s);
     void (*post_load)(APICCommonState *s);
 } APICCommonClass;
 
@@ -114,9 +121,29 @@ struct APICCommonState {
     int64_t timer_expiry;
     int sipi_vector;
     int wait_for_sipi;
+
+    uint32_t vapic_control;
+    DeviceState *vapic;
+    target_phys_addr_t vapic_paddr; /* note: persistence via kvmvapic */
 };
 
+typedef struct VAPICState {
+    uint8_t tpr;
+    uint8_t isr;
+    uint8_t zero;
+    uint8_t irr;
+    uint8_t enabled;
+} QEMU_PACKED VAPICState;
+
+extern bool apic_report_tpr_access;
+
 void apic_report_irq_delivered(int delivered);
 bool apic_next_timer(APICCommonState *s, int64_t current_time);
+void apic_enable_tpr_access_reporting(DeviceState *d, bool enable);
+void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr);
+void apic_poll_irq(DeviceState *d);
+
+void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip,
+                             TPRAccess access);
 
 #endif /* !QEMU_APIC_INTERNAL_H */
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
index 306013aeeb..2f46e214cf 100644
--- a/hw/arm-misc.h
+++ b/hw/arm-misc.h
@@ -16,7 +16,7 @@
 /* The CPU is also modeled as an interrupt controller.  */
 #define ARM_PIC_CPU_IRQ 0
 #define ARM_PIC_CPU_FIQ 1
-qemu_irq *arm_pic_init_cpu(CPUState *env);
+qemu_irq *arm_pic_init_cpu(CPUARMState *env);
 
 /* armv7m.c */
 qemu_irq *armv7m_init(MemoryRegion *address_space_mem,
@@ -29,6 +29,7 @@ struct arm_boot_info {
     const char *kernel_filename;
     const char *kernel_cmdline;
     const char *initrd_filename;
+    const char *dtb_filename;
     target_phys_addr_t loader_start;
     /* multicore boards that use the default secondary core boot functions
      * need to put the address of the secondary boot code, the boot reg,
@@ -49,16 +50,16 @@ struct arm_boot_info {
      * perform any necessary CPU reset handling and set the PC for thei
      * secondary CPUs to point at this boot blob.
      */
-    void (*write_secondary_boot)(CPUState *env,
+    void (*write_secondary_boot)(CPUARMState *env,
                                  const struct arm_boot_info *info);
-    void (*secondary_cpu_reset_hook)(CPUState *env,
+    void (*secondary_cpu_reset_hook)(CPUARMState *env,
                                      const struct arm_boot_info *info);
     /* Used internally by arm_boot.c */
     int is_linux;
     target_phys_addr_t initrd_size;
     target_phys_addr_t entry;
 };
-void arm_load_kernel(CPUState *env, struct arm_boot_info *info);
+void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info);
 
 /* Multiplication factor to convert from system clock ticks to qemu timer
    ticks.  */
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
index 102348bb70..ba6a89d3ed 100644
--- a/hw/arm11mpcore.c
+++ b/hw/arm11mpcore.c
@@ -42,7 +42,6 @@ static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset,
 {
     mpcore_priv_state *s = (mpcore_priv_state *)opaque;
     int id;
-    offset &= 0xff;
     /* SCU */
     switch (offset) {
     case 0x00: /* Control.  */
@@ -63,7 +62,6 @@ static void mpcore_scu_write(void *opaque, target_phys_addr_t offset,
                              uint64_t value, unsigned size)
 {
     mpcore_priv_state *s = (mpcore_priv_state *)opaque;
-    offset &= 0xff;
     /* SCU */
     switch (offset) {
     case 0: /* Control register.  */
@@ -202,16 +200,7 @@ static int realview_mpcore_init(SysBusDevice *dev)
 }
 
 static Property mpcore_rirq_properties[] = {
-    DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
-    /* The ARM11 MPCORE TRM says the on-chip controller may have
-     * anything from 0 to 224 external interrupt IRQ lines (with another
-     * 32 internal). We default to 32+32, which is the number provided by
-     * the ARM11 MPCore test chip in the Realview Versatile Express
-     * coretile. Other boards may differ and should set this property
-     * appropriately. Some Linux kernels may not boot if the hardware
-     * has more IRQ lines than the kernel expects.
-     */
-    DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
+    DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -233,6 +222,15 @@ static TypeInfo mpcore_rirq_info = {
 
 static Property mpcore_priv_properties[] = {
     DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
+    /* The ARM11 MPCORE TRM says the on-chip controller may have
+     * anything from 0 to 224 external interrupt IRQ lines (with another
+     * 32 internal). We default to 32+32, which is the number provided by
+     * the ARM11 MPCore test chip in the Realview Versatile Express
+     * coretile. Other boards may differ and should set this property
+     * appropriately. Some Linux kernels may not boot if the hardware
+     * has more IRQ lines than the kernel expects.
+     */
+    DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 2ef25ca9dd..7447f5c169 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -7,11 +7,14 @@
  * This code is licensed under the GPL.
  */
 
+#include "config.h"
 #include "hw.h"
 #include "arm-misc.h"
 #include "sysemu.h"
+#include "boards.h"
 #include "loader.h"
 #include "elf.h"
+#include "device_tree.h"
 
 #define KERNEL_ARGS_ADDR 0x100
 #define KERNEL_LOAD_ADDR 0x00010000
@@ -56,7 +59,7 @@ static uint32_t smpboot[] = {
   0           /* bootreg: Boot register address is held here */
 };
 
-static void default_write_secondary(CPUState *env,
+static void default_write_secondary(CPUARMState *env,
                                     const struct arm_boot_info *info)
 {
     int n;
@@ -69,7 +72,7 @@ static void default_write_secondary(CPUState *env,
                        info->smp_loader_start);
 }
 
-static void default_reset_secondary(CPUState *env,
+static void default_reset_secondary(CPUARMState *env,
                                     const struct arm_boot_info *info)
 {
     stl_phys_notdirty(info->smp_bootreg_addr, 0);
@@ -208,12 +211,73 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
     }
 }
 
+static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo)
+{
+#ifdef CONFIG_FDT
+    uint32_t mem_reg_property[] = { cpu_to_be32(binfo->loader_start),
+                                    cpu_to_be32(binfo->ram_size) };
+    void *fdt = NULL;
+    char *filename;
+    int size, rc;
+
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
+    if (!filename) {
+        fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
+        return -1;
+    }
+
+    fdt = load_device_tree(filename, &size);
+    if (!fdt) {
+        fprintf(stderr, "Couldn't open dtb file %s\n", filename);
+        g_free(filename);
+        return -1;
+    }
+    g_free(filename);
+
+    rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
+                               sizeof(mem_reg_property));
+    if (rc < 0) {
+        fprintf(stderr, "couldn't set /memory/reg\n");
+    }
+
+    rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+                                      binfo->kernel_cmdline);
+    if (rc < 0) {
+        fprintf(stderr, "couldn't set /chosen/bootargs\n");
+    }
+
+    if (binfo->initrd_size) {
+        rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+                binfo->loader_start + INITRD_LOAD_ADDR);
+        if (rc < 0) {
+            fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
+        }
+
+        rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                    binfo->loader_start + INITRD_LOAD_ADDR +
+                    binfo->initrd_size);
+        if (rc < 0) {
+            fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
+        }
+    }
+
+    cpu_physical_memory_write(addr, fdt, size);
+
+    return 0;
+
+#else
+    fprintf(stderr, "Device tree requested, "
+                "but qemu was compiled without fdt support\n");
+    return -1;
+#endif
+}
+
 static void do_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUARMState *env = opaque;
     const struct arm_boot_info *info = env->boot_info;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     if (info) {
         if (!info->is_linux) {
             /* Jump to the entry point.  */
@@ -222,10 +286,12 @@ static void do_cpu_reset(void *opaque)
         } else {
             if (env == first_cpu) {
                 env->regs[15] = info->loader_start;
-                if (old_param) {
-                    set_kernel_args_old(info);
-                } else {
-                    set_kernel_args(info);
+                if (!info->dtb_filename) {
+                    if (old_param) {
+                        set_kernel_args_old(info);
+                    } else {
+                        set_kernel_args(info);
+                    }
                 }
             } else {
                 info->secondary_cpu_reset_hook(env, info);
@@ -234,7 +300,7 @@ static void do_cpu_reset(void *opaque)
     }
 }
 
-void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
+void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info)
 {
     int kernel_size;
     int initrd_size;
@@ -243,6 +309,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
     uint64_t elf_entry;
     target_phys_addr_t entry;
     int big_endian;
+    QemuOpts *machine_opts;
 
     /* Load the kernel.  */
     if (!info->kernel_filename) {
@@ -250,6 +317,13 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
         exit(1);
     }
 
+    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+    if (machine_opts) {
+        info->dtb_filename = qemu_opt_get(machine_opts, "dtb");
+    } else {
+        info->dtb_filename = NULL;
+    }
+
     if (!info->secondary_cpu_reset_hook) {
         info->secondary_cpu_reset_hook = default_reset_secondary;
     }
@@ -300,8 +374,25 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
         } else {
             initrd_size = 0;
         }
+        info->initrd_size = initrd_size;
+
         bootloader[4] = info->board_id;
-        bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+
+        /* for device tree boot, we pass the DTB directly in r2. Otherwise
+         * we point to the kernel args.
+         */
+        if (info->dtb_filename) {
+            /* Place the DTB after the initrd in memory */
+            target_phys_addr_t dtb_start = TARGET_PAGE_ALIGN(info->loader_start
+                                                             + INITRD_LOAD_ADDR
+                                                             + initrd_size);
+            if (load_dtb(dtb_start, info)) {
+                exit(1);
+            }
+            bootloader[5] = dtb_start;
+        } else {
+            bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+        }
         bootloader[6] = entry;
         for (n = 0; n < sizeof(bootloader) / 4; n++) {
             bootloader[n] = tswap32(bootloader[n]);
@@ -311,7 +402,6 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
         if (info->nb_cpus > 1) {
             info->write_secondary_boot(env, info);
         }
-        info->initrd_size = initrd_size;
     }
     info->is_linux = is_linux;
 
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index cf582a5a14..6b34c06a8f 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -13,6 +13,8 @@
 
 /* Maximum number of possible interrupts, determined by the GIC architecture */
 #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
 //#define DEBUG_GIC
 
 #ifdef DEBUG_GIC
@@ -73,8 +75,9 @@ typedef struct gic_irq_state
 #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
 #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
 #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
-#define GIC_GET_PRIORITY(irq, cpu) \
-  (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ?            \
+                                    s->priority1[irq][cpu] :            \
+                                    s->priority2[(irq) - GIC_INTERNAL])
 #ifdef NVIC
 #define GIC_TARGET(irq) 1
 #else
@@ -92,8 +95,8 @@ typedef struct gic_state
 #ifndef NVIC
     int irq_target[GIC_MAXIRQ];
 #endif
-    int priority1[32][NCPU];
-    int priority2[GIC_MAXIRQ - 32];
+    int priority1[GIC_INTERNAL][NCPU];
+    int priority2[GIC_MAXIRQ - GIC_INTERNAL];
     int last_active[GIC_MAXIRQ][NCPU];
 
     int priority_mask[NCPU];
@@ -174,7 +177,7 @@ static void gic_set_irq(void *opaque, int irq, int level)
 {
     gic_state *s = (gic_state *)opaque;
     /* The first external input line is internal interrupt 32.  */
-    irq += 32;
+    irq += GIC_INTERNAL;
     if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
         return;
 
@@ -316,7 +319,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_PENDING(irq + i, mask)) {
                 res |= (1 << i);
@@ -328,7 +331,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
@@ -435,8 +438,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0xff;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq);
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (!GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Enabled IRQ %d\n", irq + i);
@@ -460,7 +463,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Disabled IRQ %d\n", irq + i);
@@ -502,10 +505,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32) {
+        if (irq < GIC_INTERNAL) {
             s->priority1[irq][cpu] = value;
         } else {
-            s->priority2[irq - 32] = value;
+            s->priority2[irq - GIC_INTERNAL] = value;
         }
 #ifndef NVIC
     } else if (offset < 0xc00) {
@@ -515,7 +518,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
             goto bad_reg;
         if (irq < 29)
             value = 0;
-        else if (irq < 32)
+        else if (irq < GIC_INTERNAL)
             value = ALL_CPU_MASK;
         s->irq_target[irq] = value & ALL_CPU_MASK;
     } else if (offset < 0xf00) {
@@ -523,7 +526,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32)
+        if (irq < GIC_INTERNAL)
             value |= 0xaa;
         for (i = 0; i < 4; i++) {
             if (value & (1 << (i * 2))) {
@@ -655,14 +658,14 @@ static uint64_t gic_thiscpu_read(void *opaque, target_phys_addr_t addr,
                                  unsigned size)
 {
     gic_state *s = (gic_state *)opaque;
-    return gic_cpu_read(s, gic_get_current_cpu(), addr & 0xff);
+    return gic_cpu_read(s, gic_get_current_cpu(), addr);
 }
 
 static void gic_thiscpu_write(void *opaque, target_phys_addr_t addr,
                               uint64_t value, unsigned size)
 {
     gic_state *s = (gic_state *)opaque;
-    gic_cpu_write(s, gic_get_current_cpu(), addr & 0xff, value);
+    gic_cpu_write(s, gic_get_current_cpu(), addr, value);
 }
 
 /* Wrappers to read/write the GIC CPU interface for a specific CPU.
@@ -674,7 +677,7 @@ static uint64_t gic_do_cpu_read(void *opaque, target_phys_addr_t addr,
     gic_state **backref = (gic_state **)opaque;
     gic_state *s = *backref;
     int id = (backref - s->backref);
-    return gic_cpu_read(s, id, addr & 0xff);
+    return gic_cpu_read(s, id, addr);
 }
 
 static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr,
@@ -683,7 +686,7 @@ static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr,
     gic_state **backref = (gic_state **)opaque;
     gic_state *s = *backref;
     int id = (backref - s->backref);
-    gic_cpu_write(s, id, addr & 0xff, value);
+    gic_cpu_write(s, id, addr, value);
 }
 
 static const MemoryRegionOps gic_thiscpu_ops = {
@@ -736,7 +739,7 @@ static void gic_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, s->enabled);
     for (i = 0; i < NUM_CPU(s); i++) {
         qemu_put_be32(f, s->cpu_enabled[i]);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             qemu_put_be32(f, s->priority1[j][i]);
         for (j = 0; j < s->num_irq; j++)
             qemu_put_be32(f, s->last_active[j][i]);
@@ -745,7 +748,7 @@ static void gic_save(QEMUFile *f, void *opaque)
         qemu_put_be32(f, s->running_priority[i]);
         qemu_put_be32(f, s->current_pending[i]);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         qemu_put_be32(f, s->priority2[i]);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -773,7 +776,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
     s->enabled = qemu_get_be32(f);
     for (i = 0; i < NUM_CPU(s); i++) {
         s->cpu_enabled[i] = qemu_get_be32(f);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             s->priority1[j][i] = qemu_get_be32(f);
         for (j = 0; j < s->num_irq; j++)
             s->last_active[j][i] = qemu_get_be32(f);
@@ -782,7 +785,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
         s->running_priority[i] = qemu_get_be32(f);
         s->current_pending[i] = qemu_get_be32(f);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         s->priority2[i] = qemu_get_be32(f);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -816,7 +819,16 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
-    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32);
+    /* ITLinesNumber is represented as (N / 32) - 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */
+    if (s->num_irq < 32 || (s->num_irq % 32)) {
+        hw_error("%d interrupt lines unsupported: not divisible by 32\n",
+                 num_irq);
+    }
+
+    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
     }
diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c
index 361e887dec..df7fb4c9bd 100644
--- a/hw/arm_mptimer.c
+++ b/hw/arm_mptimer.c
@@ -97,7 +97,6 @@ static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr,
 {
     timerblock *tb = (timerblock *)opaque;
     int64_t val;
-    addr &= 0x1f;
     switch (addr) {
     case 0: /* Load */
         return tb->load;
@@ -126,7 +125,6 @@ static void timerblock_write(void *opaque, target_phys_addr_t addr,
 {
     timerblock *tb = (timerblock *)opaque;
     int64_t old;
-    addr &= 0x1f;
     switch (addr) {
     case 0: /* Load */
         tb->load = value;
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
index a2e8a73301..109496528c 100644
--- a/hw/arm_pic.c
+++ b/hw/arm_pic.c
@@ -13,7 +13,7 @@
 /* Input 0 is IRQ and input 1 is FIQ.  */
 static void arm_pic_cpu_handler(void *opaque, int irq, int level)
 {
-    CPUState *env = (CPUState *)opaque;
+    CPUARMState *env = (CPUARMState *)opaque;
     switch (irq) {
     case ARM_PIC_CPU_IRQ:
         if (level)
@@ -32,7 +32,7 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level)
     }
 }
 
-qemu_irq *arm_pic_init_cpu(CPUState *env)
+qemu_irq *arm_pic_init_cpu(CPUARMState *env)
 {
     return qemu_allocate_irqs(arm_pic_cpu_handler, env, 2);
 }
diff --git a/hw/armv7m.c b/hw/armv7m.c
index 6b805798e6..4aac076e48 100644
--- a/hw/armv7m.c
+++ b/hw/armv7m.c
@@ -149,7 +149,7 @@ static void armv7m_bitband_init(void)
 
 static void armv7m_reset(void *opaque)
 {
-    cpu_reset((CPUState *)opaque);
+    cpu_state_reset((CPUARMState *)opaque);
 }
 
 /* Init CPU and memory for a v7-M based board.
@@ -160,7 +160,7 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem,
                       int flash_size, int sram_size,
                       const char *kernel_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUARMState *env;
     DeviceState *nvic;
     /* FIXME: make this local state.  */
     static qemu_irq pic[64];
diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c
index c9301fda1d..2304e3533a 100644
--- a/hw/axis_dev88.c
+++ b/hw/axis_dev88.c
@@ -247,7 +247,7 @@ void axisdev88_init (ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUCRISState *env;
     DeviceState *dev;
     SysBusDevice *s;
     DriveInfo *nand;
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
new file mode 100644
index 0000000000..e2140aea2b
--- /dev/null
+++ b/hw/cadence_gem.c
@@ -0,0 +1,1233 @@
+/*
+ * QEMU Xilinx GEM emulation
+ *
+ * Copyright (c) 2011 Xilinx, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <zlib.h> /* For crc32 */
+
+#include "sysbus.h"
+#include "net.h"
+#include "net/checksum.h"
+
+#ifdef CADENCE_GEM_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define GEM_NWCTRL        (0x00000000/4) /* Network Control reg */
+#define GEM_NWCFG         (0x00000004/4) /* Network Config reg */
+#define GEM_NWSTATUS      (0x00000008/4) /* Network Status reg */
+#define GEM_USERIO        (0x0000000C/4) /* User IO reg */
+#define GEM_DMACFG        (0x00000010/4) /* DMA Control reg */
+#define GEM_TXSTATUS      (0x00000014/4) /* TX Status reg */
+#define GEM_RXQBASE       (0x00000018/4) /* RX Q Base address reg */
+#define GEM_TXQBASE       (0x0000001C/4) /* TX Q Base address reg */
+#define GEM_RXSTATUS      (0x00000020/4) /* RX Status reg */
+#define GEM_ISR           (0x00000024/4) /* Interrupt Status reg */
+#define GEM_IER           (0x00000028/4) /* Interrupt Enable reg */
+#define GEM_IDR           (0x0000002C/4) /* Interrupt Disable reg */
+#define GEM_IMR           (0x00000030/4) /* Interrupt Mask reg */
+#define GEM_PHYMNTNC      (0x00000034/4) /* Phy Maintaince reg */
+#define GEM_RXPAUSE       (0x00000038/4) /* RX Pause Time reg */
+#define GEM_TXPAUSE       (0x0000003C/4) /* TX Pause Time reg */
+#define GEM_TXPARTIALSF   (0x00000040/4) /* TX Partial Store and Forward */
+#define GEM_RXPARTIALSF   (0x00000044/4) /* RX Partial Store and Forward */
+#define GEM_HASHLO        (0x00000080/4) /* Hash Low address reg */
+#define GEM_HASHHI        (0x00000084/4) /* Hash High address reg */
+#define GEM_SPADDR1LO     (0x00000088/4) /* Specific addr 1 low reg */
+#define GEM_SPADDR1HI     (0x0000008C/4) /* Specific addr 1 high reg */
+#define GEM_SPADDR2LO     (0x00000090/4) /* Specific addr 2 low reg */
+#define GEM_SPADDR2HI     (0x00000094/4) /* Specific addr 2 high reg */
+#define GEM_SPADDR3LO     (0x00000098/4) /* Specific addr 3 low reg */
+#define GEM_SPADDR3HI     (0x0000009C/4) /* Specific addr 3 high reg */
+#define GEM_SPADDR4LO     (0x000000A0/4) /* Specific addr 4 low reg */
+#define GEM_SPADDR4HI     (0x000000A4/4) /* Specific addr 4 high reg */
+#define GEM_TIDMATCH1     (0x000000A8/4) /* Type ID1 Match reg */
+#define GEM_TIDMATCH2     (0x000000AC/4) /* Type ID2 Match reg */
+#define GEM_TIDMATCH3     (0x000000B0/4) /* Type ID3 Match reg */
+#define GEM_TIDMATCH4     (0x000000B4/4) /* Type ID4 Match reg */
+#define GEM_WOLAN         (0x000000B8/4) /* Wake on LAN reg */
+#define GEM_IPGSTRETCH    (0x000000BC/4) /* IPG Stretch reg */
+#define GEM_SVLAN         (0x000000C0/4) /* Stacked VLAN reg */
+#define GEM_MODID         (0x000000FC/4) /* Module ID reg */
+#define GEM_OCTTXLO       (0x00000100/4) /* Octects transmitted Low reg */
+#define GEM_OCTTXHI       (0x00000104/4) /* Octects transmitted High reg */
+#define GEM_TXCNT         (0x00000108/4) /* Error-free Frames transmitted */
+#define GEM_TXBCNT        (0x0000010C/4) /* Error-free Broadcast Frames */
+#define GEM_TXMCNT        (0x00000110/4) /* Error-free Multicast Frame */
+#define GEM_TXPAUSECNT    (0x00000114/4) /* Pause Frames Transmitted */
+#define GEM_TX64CNT       (0x00000118/4) /* Error-free 64 TX */
+#define GEM_TX65CNT       (0x0000011C/4) /* Error-free 65-127 TX */
+#define GEM_TX128CNT      (0x00000120/4) /* Error-free 128-255 TX */
+#define GEM_TX256CNT      (0x00000124/4) /* Error-free 256-511 */
+#define GEM_TX512CNT      (0x00000128/4) /* Error-free 512-1023 TX */
+#define GEM_TX1024CNT     (0x0000012C/4) /* Error-free 1024-1518 TX */
+#define GEM_TX1519CNT     (0x00000130/4) /* Error-free larger than 1519 TX */
+#define GEM_TXURUNCNT     (0x00000134/4) /* TX under run error counter */
+#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */
+#define GEM_MULTCOLLCNT   (0x0000013C/4) /* Multiple Collision Frames */
+#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */
+#define GEM_LATECOLLCNT   (0x00000144/4) /* Late Collision Frames */
+#define GEM_DEFERTXCNT    (0x00000148/4) /* Deferred Transmission Frames */
+#define GEM_CSENSECNT     (0x0000014C/4) /* Carrier Sense Error Counter */
+#define GEM_OCTRXLO       (0x00000150/4) /* Octects Received register Low */
+#define GEM_OCTRXHI       (0x00000154/4) /* Octects Received register High */
+#define GEM_RXCNT         (0x00000158/4) /* Error-free Frames Received */
+#define GEM_RXBROADCNT    (0x0000015C/4) /* Error-free Broadcast Frames RX */
+#define GEM_RXMULTICNT    (0x00000160/4) /* Error-free Multicast Frames RX */
+#define GEM_RXPAUSECNT    (0x00000164/4) /* Pause Frames Received Counter */
+#define GEM_RX64CNT       (0x00000168/4) /* Error-free 64 byte Frames RX */
+#define GEM_RX65CNT       (0x0000016C/4) /* Error-free 65-127B Frames RX */
+#define GEM_RX128CNT      (0x00000170/4) /* Error-free 128-255B Frames RX */
+#define GEM_RX256CNT      (0x00000174/4) /* Error-free 256-512B Frames RX */
+#define GEM_RX512CNT      (0x00000178/4) /* Error-free 512-1023B Frames RX */
+#define GEM_RX1024CNT     (0x0000017C/4) /* Error-free 1024-1518B Frames RX */
+#define GEM_RX1519CNT     (0x00000180/4) /* Error-free 1519-max Frames RX */
+#define GEM_RXUNDERCNT    (0x00000184/4) /* Undersize Frames Received */
+#define GEM_RXOVERCNT     (0x00000188/4) /* Oversize Frames Received */
+#define GEM_RXJABCNT      (0x0000018C/4) /* Jabbers Received Counter */
+#define GEM_RXFCSCNT      (0x00000190/4) /* Frame Check seq. Error Counter */
+#define GEM_RXLENERRCNT   (0x00000194/4) /* Length Field Error Counter */
+#define GEM_RXSYMERRCNT   (0x00000198/4) /* Symbol Error Counter */
+#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */
+#define GEM_RXRSCERRCNT   (0x000001A0/4) /* Receive Resource Error Counter */
+#define GEM_RXORUNCNT     (0x000001A4/4) /* Receive Overrun Counter */
+#define GEM_RXIPCSERRCNT  (0x000001A8/4) /* IP header Checksum Error Counter */
+#define GEM_RXTCPCCNT     (0x000001AC/4) /* TCP Checksum Error Counter */
+#define GEM_RXUDPCCNT     (0x000001B0/4) /* UDP Checksum Error Counter */
+
+#define GEM_1588S         (0x000001D0/4) /* 1588 Timer Seconds */
+#define GEM_1588NS        (0x000001D4/4) /* 1588 Timer Nanoseconds */
+#define GEM_1588ADJ       (0x000001D8/4) /* 1588 Timer Adjust */
+#define GEM_1588INC       (0x000001DC/4) /* 1588 Timer Increment */
+#define GEM_PTPETXS       (0x000001E0/4) /* PTP Event Frame Transmitted (s) */
+#define GEM_PTPETXNS      (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */
+#define GEM_PTPERXS       (0x000001E8/4) /* PTP Event Frame Received (s) */
+#define GEM_PTPERXNS      (0x000001EC/4) /* PTP Event Frame Received (ns) */
+#define GEM_PTPPTXS       (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */
+#define GEM_PTPPTXNS      (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */
+#define GEM_PTPPRXS       (0x000001E8/4) /* PTP Peer Frame Received (s) */
+#define GEM_PTPPRXNS      (0x000001EC/4) /* PTP Peer Frame Received (ns) */
+
+/* Design Configuration Registers */
+#define GEM_DESCONF       (0x00000280/4)
+#define GEM_DESCONF2      (0x00000284/4)
+#define GEM_DESCONF3      (0x00000288/4)
+#define GEM_DESCONF4      (0x0000028C/4)
+#define GEM_DESCONF5      (0x00000290/4)
+#define GEM_DESCONF6      (0x00000294/4)
+#define GEM_DESCONF7      (0x00000298/4)
+
+#define GEM_MAXREG        (0x00000640/4) /* Last valid GEM address */
+
+/*****************************************/
+#define GEM_NWCTRL_TXSTART     0x00000200 /* Transmit Enable */
+#define GEM_NWCTRL_TXENA       0x00000008 /* Transmit Enable */
+#define GEM_NWCTRL_RXENA       0x00000004 /* Receive Enable */
+#define GEM_NWCTRL_LOCALLOOP   0x00000002 /* Local Loopback */
+
+#define GEM_NWCFG_STRIP_FCS    0x00020000 /* Strip FCS field */
+#define GEM_NWCFG_LERR_DISC    0x00010000 /* Discard RX frames with lenth err */
+#define GEM_NWCFG_BUFF_OFST_M  0x0000C000 /* Receive buffer offset mask */
+#define GEM_NWCFG_BUFF_OFST_S  14         /* Receive buffer offset shift */
+#define GEM_NWCFG_UCAST_HASH   0x00000080 /* accept unicast if hash match */
+#define GEM_NWCFG_MCAST_HASH   0x00000040 /* accept multicast if hash match */
+#define GEM_NWCFG_BCAST_REJ    0x00000020 /* Reject broadcast packets */
+#define GEM_NWCFG_PROMISC      0x00000010 /* Accept all packets */
+
+#define GEM_DMACFG_RBUFSZ_M    0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_S    16         /* DMA RX Buffer Size shift */
+#define GEM_DMACFG_RBUFSZ_MUL  64         /* DMA RX Buffer Size multiplier */
+#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
+
+#define GEM_TXSTATUS_TXCMPL    0x00000020 /* Transmit Complete */
+#define GEM_TXSTATUS_USED      0x00000001 /* sw owned descriptor encountered */
+
+#define GEM_RXSTATUS_FRMRCVD   0x00000002 /* Frame received */
+#define GEM_RXSTATUS_NOBUF     0x00000001 /* Buffer unavailable */
+
+/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */
+#define GEM_INT_TXCMPL        0x00000080 /* Transmit Complete */
+#define GEM_INT_TXUSED         0x00000008
+#define GEM_INT_RXUSED         0x00000004
+#define GEM_INT_RXCMPL        0x00000002
+
+#define GEM_PHYMNTNC_OP_R      0x20000000 /* read operation */
+#define GEM_PHYMNTNC_OP_W      0x10000000 /* write operation */
+#define GEM_PHYMNTNC_ADDR      0x0F800000 /* Address bits */
+#define GEM_PHYMNTNC_ADDR_SHFT 23
+#define GEM_PHYMNTNC_REG       0x007C0000 /* register bits */
+#define GEM_PHYMNTNC_REG_SHIFT 18
+
+/* Marvell PHY definitions */
+#define BOARD_PHY_ADDRESS    23 /* PHY address we will emulate a device at */
+
+#define PHY_REG_CONTROL      0
+#define PHY_REG_STATUS       1
+#define PHY_REG_PHYID1       2
+#define PHY_REG_PHYID2       3
+#define PHY_REG_ANEGADV      4
+#define PHY_REG_LINKPABIL    5
+#define PHY_REG_ANEGEXP      6
+#define PHY_REG_NEXTP        7
+#define PHY_REG_LINKPNEXTP   8
+#define PHY_REG_100BTCTRL    9
+#define PHY_REG_1000BTSTAT   10
+#define PHY_REG_EXTSTAT      15
+#define PHY_REG_PHYSPCFC_CTL 16
+#define PHY_REG_PHYSPCFC_ST  17
+#define PHY_REG_INT_EN       18
+#define PHY_REG_INT_ST       19
+#define PHY_REG_EXT_PHYSPCFC_CTL  20
+#define PHY_REG_RXERR        21
+#define PHY_REG_EACD         22
+#define PHY_REG_LED          24
+#define PHY_REG_LED_OVRD     25
+#define PHY_REG_EXT_PHYSPCFC_CTL2 26
+#define PHY_REG_EXT_PHYSPCFC_ST   27
+#define PHY_REG_CABLE_DIAG   28
+
+#define PHY_REG_CONTROL_RST  0x8000
+#define PHY_REG_CONTROL_LOOP 0x4000
+#define PHY_REG_CONTROL_ANEG 0x1000
+
+#define PHY_REG_STATUS_LINK     0x0004
+#define PHY_REG_STATUS_ANEGCMPL 0x0020
+
+#define PHY_REG_INT_ST_ANEGCMPL 0x0800
+#define PHY_REG_INT_ST_LINKC    0x0400
+#define PHY_REG_INT_ST_ENERGY   0x0010
+
+/***********************************************************************/
+#define GEM_RX_REJECT  1
+#define GEM_RX_ACCEPT  0
+
+/***********************************************************************/
+
+#define DESC_1_USED 0x80000000
+#define DESC_1_LENGTH 0x00001FFF
+
+#define DESC_1_TX_WRAP 0x40000000
+#define DESC_1_TX_LAST 0x00008000
+
+#define DESC_0_RX_WRAP 0x00000002
+#define DESC_0_RX_OWNERSHIP 0x00000001
+
+#define DESC_1_RX_SOF 0x00004000
+#define DESC_1_RX_EOF 0x00008000
+
+static inline unsigned tx_desc_get_buffer(unsigned *desc)
+{
+    return desc[0];
+}
+
+static inline unsigned tx_desc_get_used(unsigned *desc)
+{
+    return (desc[1] & DESC_1_USED) ? 1 : 0;
+}
+
+static inline void tx_desc_set_used(unsigned *desc)
+{
+    desc[1] |= DESC_1_USED;
+}
+
+static inline unsigned tx_desc_get_wrap(unsigned *desc)
+{
+    return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0;
+}
+
+static inline unsigned tx_desc_get_last(unsigned *desc)
+{
+    return (desc[1] & DESC_1_TX_LAST) ? 1 : 0;
+}
+
+static inline unsigned tx_desc_get_length(unsigned *desc)
+{
+    return desc[1] & DESC_1_LENGTH;
+}
+
+static inline void print_gem_tx_desc(unsigned *desc)
+{
+    DB_PRINT("TXDESC:\n");
+    DB_PRINT("bufaddr: 0x%08x\n", *desc);
+    DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc));
+    DB_PRINT("wrap:    %d\n", tx_desc_get_wrap(desc));
+    DB_PRINT("last:    %d\n", tx_desc_get_last(desc));
+    DB_PRINT("length:  %d\n", tx_desc_get_length(desc));
+}
+
+static inline unsigned rx_desc_get_buffer(unsigned *desc)
+{
+    return desc[0] & ~0x3UL;
+}
+
+static inline unsigned rx_desc_get_wrap(unsigned *desc)
+{
+    return desc[0] & DESC_0_RX_WRAP ? 1 : 0;
+}
+
+static inline unsigned rx_desc_get_ownership(unsigned *desc)
+{
+    return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0;
+}
+
+static inline void rx_desc_set_ownership(unsigned *desc)
+{
+    desc[0] |= DESC_0_RX_OWNERSHIP;
+}
+
+static inline void rx_desc_set_sof(unsigned *desc)
+{
+    desc[1] |= DESC_1_RX_SOF;
+}
+
+static inline void rx_desc_set_eof(unsigned *desc)
+{
+    desc[1] |= DESC_1_RX_EOF;
+}
+
+static inline void rx_desc_set_length(unsigned *desc, unsigned len)
+{
+    desc[1] &= ~DESC_1_LENGTH;
+    desc[1] |= len;
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+
+    /* GEM registers backing store */
+    uint32_t regs[GEM_MAXREG];
+    /* Mask of register bits which are write only */
+    uint32_t regs_wo[GEM_MAXREG];
+    /* Mask of register bits which are read only */
+    uint32_t regs_ro[GEM_MAXREG];
+    /* Mask of register bits which are clear on read */
+    uint32_t regs_rtc[GEM_MAXREG];
+    /* Mask of register bits which are write 1 to clear */
+    uint32_t regs_w1c[GEM_MAXREG];
+
+    /* PHY registers backing store */
+    uint16_t phy_regs[32];
+
+    uint8_t phy_loop; /* Are we in phy loopback? */
+
+    /* The current DMA descriptor pointers */
+    target_phys_addr_t rx_desc_addr;
+    target_phys_addr_t tx_desc_addr;
+
+} GemState;
+
+/* The broadcast MAC address: 0xFFFFFFFFFFFF */
+const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ * gem_init_register_masks:
+ * One time initialization.
+ * Set masks to identify which register bits have magical clear properties
+ */
+static void gem_init_register_masks(GemState *s)
+{
+    /* Mask of register bits which are read only*/
+    memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
+    s->regs_ro[GEM_NWCTRL]   = 0xFFF80000;
+    s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF;
+    s->regs_ro[GEM_DMACFG]   = 0xFE00F000;
+    s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08;
+    s->regs_ro[GEM_RXQBASE]  = 0x00000003;
+    s->regs_ro[GEM_TXQBASE]  = 0x00000003;
+    s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0;
+    s->regs_ro[GEM_ISR]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_IMR]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_MODID]    = 0xFFFFFFFF;
+
+    /* Mask of register bits which are clear on read */
+    memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc));
+    s->regs_rtc[GEM_ISR]      = 0xFFFFFFFF;
+
+    /* Mask of register bits which are write 1 to clear */
+    memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c));
+    s->regs_w1c[GEM_TXSTATUS] = 0x000001F7;
+    s->regs_w1c[GEM_RXSTATUS] = 0x0000000F;
+
+    /* Mask of register bits which are write only */
+    memset(&s->regs_wo[0], 0, sizeof(s->regs_wo));
+    s->regs_wo[GEM_NWCTRL]   = 0x00073E60;
+    s->regs_wo[GEM_IER]      = 0x07FFFFFF;
+    s->regs_wo[GEM_IDR]      = 0x07FFFFFF;
+}
+
+/*
+ * phy_update_link:
+ * Make the emulated PHY link state match the QEMU "interface" state.
+ */
+static void phy_update_link(GemState *s)
+{
+    DB_PRINT("down %d\n", s->nic->nc.link_down);
+
+    /* Autonegotiation status mirrors link status.  */
+    if (s->nic->nc.link_down) {
+        s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
+    } else {
+        s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC |
+                                        PHY_REG_INT_ST_ANEGCMPL |
+                                        PHY_REG_INT_ST_ENERGY);
+    }
+}
+
+static int gem_can_receive(VLANClientState *nc)
+{
+    GemState *s;
+
+    s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    DB_PRINT("\n");
+
+    /* Do nothing if receive is not enabled. */
+    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * gem_update_int_status:
+ * Raise or lower interrupt based on current status.
+ */
+static void gem_update_int_status(GemState *s)
+{
+    uint32_t new_interrupts = 0;
+    /* Packet transmitted ? */
+    if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_TXCMPL) {
+        new_interrupts |= GEM_INT_TXCMPL;
+    }
+    /* End of TX ring ? */
+    if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_USED) {
+        new_interrupts |= GEM_INT_TXUSED;
+    }
+
+    /* Frame received ? */
+    if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_FRMRCVD) {
+        new_interrupts |= GEM_INT_RXCMPL;
+    }
+    /* RX ring full ? */
+    if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_NOBUF) {
+        new_interrupts |= GEM_INT_RXUSED;
+    }
+
+    s->regs[GEM_ISR] |= new_interrupts & ~(s->regs[GEM_IMR]);
+
+    if (s->regs[GEM_ISR]) {
+        DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
+        qemu_set_irq(s->irq, 1);
+    } else {
+        qemu_set_irq(s->irq, 0);
+    }
+}
+
+/*
+ * gem_receive_updatestats:
+ * Increment receive statistics.
+ */
+static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
+                                    unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) received */
+    octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) |
+             s->regs[GEM_OCTRXHI];
+    octets += bytes;
+    s->regs[GEM_OCTRXLO] = octets >> 32;
+    s->regs[GEM_OCTRXHI] = octets;
+
+    /* Error-free Frames received */
+    s->regs[GEM_RXCNT]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        s->regs[GEM_RXBROADCNT]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_RXMULTICNT]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_RX64CNT]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_RX65CNT]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_RX128CNT]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_RX256CNT]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_RX512CNT]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_RX1024CNT]++;
+    } else {
+        s->regs[GEM_RX1519CNT]++;
+    }
+}
+
+/*
+ * Get the MAC Address bit from the specified position
+ */
+static unsigned get_bit(const uint8_t *mac, unsigned bit)
+{
+    unsigned byte;
+
+    byte = mac[bit / 8];
+    byte >>= (bit & 0x7);
+    byte &= 1;
+
+    return byte;
+}
+
+/*
+ * Calculate a GEM MAC Address hash index
+ */
+static unsigned calc_mac_hash(const uint8_t *mac)
+{
+    int index_bit, mac_bit;
+    unsigned hash_index;
+
+    hash_index = 0;
+    mac_bit = 5;
+    for (index_bit = 5; index_bit >= 0; index_bit--) {
+        hash_index |= (get_bit(mac,  mac_bit) ^
+                               get_bit(mac, mac_bit + 6) ^
+                               get_bit(mac, mac_bit + 12) ^
+                               get_bit(mac, mac_bit + 18) ^
+                               get_bit(mac, mac_bit + 24) ^
+                               get_bit(mac, mac_bit + 30) ^
+                               get_bit(mac, mac_bit + 36) ^
+                               get_bit(mac, mac_bit + 42)) << index_bit;
+        mac_bit--;
+    }
+
+    return hash_index;
+}
+
+/*
+ * gem_mac_address_filter:
+ * Accept or reject this destination address?
+ * Returns:
+ * GEM_RX_REJECT: reject
+ * GEM_RX_ACCEPT: accept
+ */
+static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
+{
+    uint8_t *gem_spaddr;
+    int i;
+
+    /* Promiscuous mode? */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) {
+        return GEM_RX_ACCEPT;
+    }
+
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        /* Reject broadcast packets? */
+        if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) {
+            return GEM_RX_REJECT;
+        }
+        return GEM_RX_ACCEPT;
+    }
+
+    /* Accept packets -w- hash match? */
+    if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) ||
+        (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) {
+        unsigned hash_index;
+
+        hash_index = calc_mac_hash(packet);
+        if (hash_index < 32) {
+            if (s->regs[GEM_HASHLO] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        } else {
+            hash_index -= 32;
+            if (s->regs[GEM_HASHHI] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        }
+    }
+
+    /* Check all 4 specific addresses */
+    gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]);
+    for (i = 0; i < 4; i++) {
+        if (!memcmp(packet, gem_spaddr, 6)) {
+            return GEM_RX_ACCEPT;
+        }
+
+        gem_spaddr += 8;
+    }
+
+    /* No address match; reject the packet */
+    return GEM_RX_REJECT;
+}
+
+/*
+ * gem_receive:
+ * Fit a packet handed to us by QEMU into the receive descriptor ring.
+ */
+static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    unsigned    desc[2];
+    target_phys_addr_t packet_desc_addr, last_desc_addr;
+    GemState *s;
+    unsigned   rxbufsize, bytes_to_copy;
+    unsigned   rxbuf_offset;
+    uint8_t    rxbuf[2048];
+    uint8_t   *rxbuf_ptr;
+
+    s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    /* Do nothing if receive is not enabled. */
+    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
+        return -1;
+    }
+
+    /* Is this destination MAC address "for us" ? */
+    if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) {
+        return -1;
+    }
+
+    /* Discard packets with receive length error enabled ? */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) {
+        unsigned type_len;
+
+        /* Fish the ethertype / length field out of the RX packet */
+        type_len = buf[12] << 8 | buf[13];
+        /* It is a length field, not an ethertype */
+        if (type_len < 0x600) {
+            if (size < type_len) {
+                /* discard */
+                return -1;
+            }
+        }
+    }
+
+    /*
+     * Determine configured receive buffer offset (probably 0)
+     */
+    rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >>
+                   GEM_NWCFG_BUFF_OFST_S;
+
+    /* The configure size of each receive buffer.  Determines how many
+     * buffers needed to hold this packet.
+     */
+    rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >>
+                 GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
+    bytes_to_copy = size;
+
+    /* Strip of FCS field ? (usually yes) */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) {
+        rxbuf_ptr = (void *)buf;
+    } else {
+        unsigned crc_val;
+        int      crc_offset;
+
+        /* The application wants the FCS field, which QEMU does not provide.
+         * We must try and caclculate one.
+         */
+
+        memcpy(rxbuf, buf, size);
+        memset(rxbuf + size, 0, sizeof(rxbuf - size));
+        rxbuf_ptr = rxbuf;
+        crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60)));
+        if (size < 60) {
+            crc_offset = 60;
+        } else {
+            crc_offset = size;
+        }
+        memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val));
+
+        bytes_to_copy += 4;
+        size += 4;
+    }
+
+    /* Pad to minimum length */
+    if (size < 64) {
+        size = 64;
+    }
+
+    DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size);
+
+    packet_desc_addr = s->rx_desc_addr;
+    while (1) {
+        DB_PRINT("read descriptor 0x%x\n", packet_desc_addr);
+        /* read current descriptor */
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+
+        /* Descriptor owned by software ? */
+        if (rx_desc_get_ownership(desc) == 1) {
+            DB_PRINT("descriptor 0x%x owned by sw.\n", packet_desc_addr);
+            s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF;
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+            return -1;
+        }
+
+        DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize),
+                rx_desc_get_buffer(desc));
+
+        /*
+         * Let's have QEMU lend a helping hand.
+         */
+        if (rx_desc_get_buffer(desc) == 0) {
+            DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n",
+                       packet_desc_addr);
+            break;
+        }
+
+        /* Copy packet data to emulated DMA buffer */
+        cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset,
+                                  rxbuf_ptr, MIN(bytes_to_copy, rxbufsize));
+        bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
+        rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
+        if (bytes_to_copy == 0) {
+            break;
+        }
+
+        /* Next descriptor */
+        if (rx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_RXQBASE];
+        } else {
+            packet_desc_addr += 8;
+        }
+    }
+
+    DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size,
+            (unsigned)packet_desc_addr);
+
+    /* Update last descriptor with EOF and total length */
+    rx_desc_set_eof(desc);
+    rx_desc_set_length(desc, size);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    /* Advance RX packet descriptor Q */
+    last_desc_addr = packet_desc_addr;
+    packet_desc_addr = s->rx_desc_addr;
+    s->rx_desc_addr = last_desc_addr;
+    if (rx_desc_get_wrap(desc)) {
+        s->rx_desc_addr = s->regs[GEM_RXQBASE];
+    } else {
+        s->rx_desc_addr += 8;
+    }
+
+    DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", packet_desc_addr);
+
+    /* Count it */
+    gem_receive_updatestats(s, buf, size);
+
+    /* Update first descriptor (which could also be the last) */
+    /* read descriptor */
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    rx_desc_set_sof(desc);
+    rx_desc_set_ownership(desc);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD;
+
+    /* Handle interrupt consequences */
+    gem_update_int_status(s);
+
+    return size;
+}
+
+/*
+ * gem_transmit_updatestats:
+ * Increment transmit statistics.
+ */
+static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
+                                     unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) transmitted */
+    octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) |
+             s->regs[GEM_OCTTXHI];
+    octets += bytes;
+    s->regs[GEM_OCTTXLO] = octets >> 32;
+    s->regs[GEM_OCTTXHI] = octets;
+
+    /* Error-free Frames transmitted */
+    s->regs[GEM_TXCNT]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        s->regs[GEM_TXBCNT]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_TXMCNT]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_TX64CNT]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_TX65CNT]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_TX128CNT]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_TX256CNT]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_TX512CNT]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_TX1024CNT]++;
+    } else {
+        s->regs[GEM_TX1519CNT]++;
+    }
+}
+
+/*
+ * gem_transmit:
+ * Fish packets out of the descriptor ring and feed them to QEMU
+ */
+static void gem_transmit(GemState *s)
+{
+    unsigned    desc[2];
+    target_phys_addr_t packet_desc_addr;
+    uint8_t     tx_packet[2048];
+    uint8_t     *p;
+    unsigned    total_bytes;
+
+    /* Do nothing if transmit is not enabled. */
+    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
+        return;
+    }
+
+    DB_PRINT("\n");
+
+    /* The packet we will hand off to qemu.
+     * Packets scattered across multiple descriptors are gathered to this
+     * one contiguous buffer first.
+     */
+    p = tx_packet;
+    total_bytes = 0;
+
+    /* read current descriptor */
+    packet_desc_addr = s->tx_desc_addr;
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    /* Handle all descriptors owned by hardware */
+    while (tx_desc_get_used(desc) == 0) {
+
+        /* Do nothing if transmit is not enabled. */
+        if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
+            return;
+        }
+        print_gem_tx_desc(desc);
+
+        /* The real hardware would eat this (and possibly crash).
+         * For QEMU let's lend a helping hand.
+         */
+        if ((tx_desc_get_buffer(desc) == 0) ||
+            (tx_desc_get_length(desc) == 0)) {
+            DB_PRINT("Invalid TX descriptor @ 0x%x\n", packet_desc_addr);
+            break;
+        }
+
+        /* Gather this fragment of the packet from "dma memory" to our contig.
+         * buffer.
+         */
+        cpu_physical_memory_read(tx_desc_get_buffer(desc), p,
+                                 tx_desc_get_length(desc));
+        p += tx_desc_get_length(desc);
+        total_bytes += tx_desc_get_length(desc);
+
+        /* Last descriptor for this packet; hand the whole thing off */
+        if (tx_desc_get_last(desc)) {
+            /* Modify the 1st descriptor of this packet to be owned by
+             * the processor.
+             */
+            cpu_physical_memory_read(s->tx_desc_addr,
+                                     (uint8_t *)&desc[0], sizeof(desc));
+            tx_desc_set_used(desc);
+            cpu_physical_memory_write(s->tx_desc_addr,
+                                      (uint8_t *)&desc[0], sizeof(desc));
+            /* Advance the hardare current descriptor past this packet */
+            if (tx_desc_get_wrap(desc)) {
+                s->tx_desc_addr = s->regs[GEM_TXQBASE];
+            } else {
+                s->tx_desc_addr = packet_desc_addr + 8;
+            }
+            DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr);
+
+            s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL;
+
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+
+            /* Is checksum offload enabled? */
+            if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) {
+                net_checksum_calculate(tx_packet, total_bytes);
+            }
+
+            /* Update MAC statistics */
+            gem_transmit_updatestats(s, tx_packet, total_bytes);
+
+            /* Send the packet somewhere */
+            if (s->phy_loop) {
+                gem_receive(&s->nic->nc, tx_packet, total_bytes);
+            } else {
+                qemu_send_packet(&s->nic->nc, tx_packet, total_bytes);
+            }
+
+            /* Prepare for next packet */
+            p = tx_packet;
+            total_bytes = 0;
+        }
+
+        /* read next descriptor */
+        if (tx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_TXQBASE];
+        } else {
+            packet_desc_addr += 8;
+        }
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+    }
+
+    if (tx_desc_get_used(desc)) {
+        s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED;
+        gem_update_int_status(s);
+    }
+}
+
+static void gem_phy_reset(GemState *s)
+{
+    memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
+    s->phy_regs[PHY_REG_CONTROL] = 0x1140;
+    s->phy_regs[PHY_REG_STATUS] = 0x7969;
+    s->phy_regs[PHY_REG_PHYID1] = 0x0141;
+    s->phy_regs[PHY_REG_PHYID2] = 0x0CC2;
+    s->phy_regs[PHY_REG_ANEGADV] = 0x01E1;
+    s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1;
+    s->phy_regs[PHY_REG_ANEGEXP] = 0x000F;
+    s->phy_regs[PHY_REG_NEXTP] = 0x2001;
+    s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6;
+    s->phy_regs[PHY_REG_100BTCTRL] = 0x0300;
+    s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
+    s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
+    s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
+    s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
+    s->phy_regs[PHY_REG_LED] = 0x4100;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B;
+
+    phy_update_link(s);
+}
+
+static void gem_reset(DeviceState *d)
+{
+    GemState *s = FROM_SYSBUS(GemState, sysbus_from_qdev(d));
+
+    DB_PRINT("\n");
+
+    /* Set post reset register values */
+    memset(&s->regs[0], 0, sizeof(s->regs));
+    s->regs[GEM_NWCFG] = 0x00080000;
+    s->regs[GEM_NWSTATUS] = 0x00000006;
+    s->regs[GEM_DMACFG] = 0x00020784;
+    s->regs[GEM_IMR] = 0x07ffffff;
+    s->regs[GEM_TXPAUSE] = 0x0000ffff;
+    s->regs[GEM_TXPARTIALSF] = 0x000003ff;
+    s->regs[GEM_RXPARTIALSF] = 0x000003ff;
+    s->regs[GEM_MODID] = 0x00020118;
+    s->regs[GEM_DESCONF] = 0x02500111;
+    s->regs[GEM_DESCONF2] = 0x2ab13fff;
+    s->regs[GEM_DESCONF5] = 0x002f2145;
+    s->regs[GEM_DESCONF6] = 0x00000200;
+
+    gem_phy_reset(s);
+
+    gem_update_int_status(s);
+}
+
+static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
+{
+    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
+    return s->phy_regs[reg_num];
+}
+
+static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
+{
+    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
+
+    switch (reg_num) {
+    case PHY_REG_CONTROL:
+        if (val & PHY_REG_CONTROL_RST) {
+            /* Phy reset */
+            gem_phy_reset(s);
+            val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP);
+            s->phy_loop = 0;
+        }
+        if (val & PHY_REG_CONTROL_ANEG) {
+            /* Complete autonegotiation immediately */
+            val &= ~PHY_REG_CONTROL_ANEG;
+            s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL;
+        }
+        if (val & PHY_REG_CONTROL_LOOP) {
+            DB_PRINT("PHY placed in loopback\n");
+            s->phy_loop = 1;
+        } else {
+            s->phy_loop = 0;
+        }
+        break;
+    }
+    s->phy_regs[reg_num] = val;
+}
+
+/*
+ * gem_read32:
+ * Read a GEM register.
+ */
+static uint64_t gem_read(void *opaque, target_phys_addr_t offset, unsigned size)
+{
+    GemState *s;
+    uint32_t retval;
+
+    s = (GemState *)opaque;
+
+    offset >>= 2;
+    retval = s->regs[offset];
+
+    DB_PRINT("offset: 0x%04x read: 0x%08x\n", offset*4, retval);
+
+    switch (offset) {
+    case GEM_ISR:
+        qemu_set_irq(s->irq, 0);
+        break;
+    case GEM_PHYMNTNC:
+        if (retval & GEM_PHYMNTNC_OP_R) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                retval &= 0xFFFF0000;
+                retval |= gem_phy_read(s, reg_num);
+            } else {
+                retval |= 0xFFFF; /* No device at this address */
+            }
+        }
+        break;
+    }
+
+    /* Squash read to clear bits */
+    s->regs[offset] &= ~(s->regs_rtc[offset]);
+
+    /* Do not provide write only bits */
+    retval &= ~(s->regs_wo[offset]);
+
+    DB_PRINT("0x%08x\n", retval);
+    return retval;
+}
+
+/*
+ * gem_write32:
+ * Write a GEM register.
+ */
+static void gem_write(void *opaque, target_phys_addr_t offset, uint64_t val,
+        unsigned size)
+{
+    GemState *s = (GemState *)opaque;
+    uint32_t readonly;
+
+    DB_PRINT("offset: 0x%04x write: 0x%08x ", offset, (unsigned)val);
+    offset >>= 2;
+
+    /* Squash bits which are read only in write value */
+    val &= ~(s->regs_ro[offset]);
+    /* Preserve (only) bits which are read only in register */
+    readonly = s->regs[offset];
+    readonly &= s->regs_ro[offset];
+
+    /* Squash bits which are write 1 to clear */
+    val &= ~(s->regs_w1c[offset] & val);
+
+    /* Copy register write to backing store */
+    s->regs[offset] = val | readonly;
+
+    /* Handle register write side effects */
+    switch (offset) {
+    case GEM_NWCTRL:
+        if (val & GEM_NWCTRL_TXSTART) {
+            gem_transmit(s);
+        }
+        if (!(val & GEM_NWCTRL_TXENA)) {
+            /* Reset to start of Q when transmit disabled. */
+            s->tx_desc_addr = s->regs[GEM_TXQBASE];
+        }
+        if (!(val & GEM_NWCTRL_RXENA)) {
+            /* Reset to start of Q when receive disabled. */
+            s->rx_desc_addr = s->regs[GEM_RXQBASE];
+        }
+        break;
+
+    case GEM_TXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_RXQBASE:
+        s->rx_desc_addr = val;
+        break;
+    case GEM_TXQBASE:
+        s->tx_desc_addr = val;
+        break;
+    case GEM_RXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_IER:
+        s->regs[GEM_IMR] &= ~val;
+        gem_update_int_status(s);
+        break;
+    case GEM_IDR:
+        s->regs[GEM_IMR] |= val;
+        gem_update_int_status(s);
+        break;
+    case GEM_PHYMNTNC:
+        if (val & GEM_PHYMNTNC_OP_W) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                gem_phy_write(s, reg_num, val);
+            }
+        }
+        break;
+    }
+
+    DB_PRINT("newval: 0x%08x\n", s->regs[offset]);
+}
+
+static const MemoryRegionOps gem_ops = {
+    .read = gem_read,
+    .write = gem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void gem_cleanup(VLANClientState *nc)
+{
+    GemState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    DB_PRINT("\n");
+    s->nic = NULL;
+}
+
+static void gem_set_link(VLANClientState *nc)
+{
+    DB_PRINT("\n");
+    phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
+}
+
+static NetClientInfo net_gem_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = gem_can_receive,
+    .receive = gem_receive,
+    .cleanup = gem_cleanup,
+    .link_status_changed = gem_set_link,
+};
+
+static int gem_init(SysBusDevice *dev)
+{
+    GemState *s;
+
+    DB_PRINT("\n");
+
+    s = FROM_SYSBUS(GemState, dev);
+    gem_init_register_masks(s);
+    memory_region_init_io(&s->iomem, &gem_ops, s, "enet", sizeof(s->regs));
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->nic = qemu_new_nic(&net_gem_info, &s->conf,
+            object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_gem = {
+    .name = "cadence_gem",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
+        VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
+        VMSTATE_UINT8(phy_loop, GemState),
+        VMSTATE_UINT32(rx_desc_addr, GemState),
+        VMSTATE_UINT32(tx_desc_addr, GemState),
+    }
+};
+
+static Property gem_properties[] = {
+    DEFINE_NIC_PROPERTIES(GemState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gem_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = gem_init;
+    dc->props = gem_properties;
+    dc->vmsd = &vmstate_cadence_gem;
+    dc->reset = gem_reset;
+}
+
+static TypeInfo gem_info = {
+    .class_init = gem_class_init,
+    .name  = "cadence_gem",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(GemState),
+};
+
+static void gem_register_types(void)
+{
+    type_register_static(&gem_info);
+}
+
+type_init(gem_register_types)
diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c
new file mode 100644
index 0000000000..2b5477b688
--- /dev/null
+++ b/hw/cadence_ttc.c
@@ -0,0 +1,489 @@
+/*
+ * Xilinx Zynq cadence TTC model
+ *
+ * Copyright (c) 2011 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written By Haibing Ma
+ *            M. Habib
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+#ifdef CADENCE_TTC_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define COUNTER_INTR_IV     0x00000001
+#define COUNTER_INTR_M1     0x00000002
+#define COUNTER_INTR_M2     0x00000004
+#define COUNTER_INTR_M3     0x00000008
+#define COUNTER_INTR_OV     0x00000010
+#define COUNTER_INTR_EV     0x00000020
+
+#define COUNTER_CTRL_DIS    0x00000001
+#define COUNTER_CTRL_INT    0x00000002
+#define COUNTER_CTRL_DEC    0x00000004
+#define COUNTER_CTRL_MATCH  0x00000008
+#define COUNTER_CTRL_RST    0x00000010
+
+#define CLOCK_CTRL_PS_EN    0x00000001
+#define CLOCK_CTRL_PS_V     0x0000001e
+
+typedef struct {
+    QEMUTimer *timer;
+    int freq;
+
+    uint32_t reg_clock;
+    uint32_t reg_count;
+    uint32_t reg_value;
+    uint16_t reg_interval;
+    uint16_t reg_match[3];
+    uint32_t reg_intr;
+    uint32_t reg_intr_en;
+    uint32_t reg_event_ctrl;
+    uint32_t reg_event;
+
+    uint64_t cpu_time;
+    unsigned int cpu_time_valid;
+
+    qemu_irq irq;
+} CadenceTimerState;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    CadenceTimerState timer[3];
+} CadenceTTCState;
+
+static void cadence_timer_update(CadenceTimerState *s)
+{
+    qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
+}
+
+static CadenceTimerState *cadence_timer_from_addr(void *opaque,
+                                        target_phys_addr_t offset)
+{
+    unsigned int index;
+    CadenceTTCState *s = (CadenceTTCState *)opaque;
+
+    index = (offset >> 2) % 3;
+
+    return &s->timer[index];
+}
+
+static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
+{
+    /* timer_steps has max value of 0x100000000. double check it
+     * (or overflow can happen below) */
+    assert(timer_steps <= 1ULL << 32);
+
+    uint64_t r = timer_steps * 1000000000ULL;
+    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
+        r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
+    } else {
+        r >>= 16;
+    }
+    r /= (uint64_t)s->freq;
+    return r;
+}
+
+static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
+{
+    uint64_t to_divide = 1000000000ULL;
+
+    uint64_t r = ns;
+     /* for very large intervals (> 8s) do some division first to stop
+      * overflow (costs some prescision) */
+    while (r >= 8ULL << 30 && to_divide > 1) {
+        r /= 1000;
+        to_divide /= 1000;
+    }
+    r <<= 16;
+    /* keep early-dividing as needed */
+    while (r >= 8ULL << 30 && to_divide > 1) {
+        r /= 1000;
+        to_divide /= 1000;
+    }
+    r *= (uint64_t)s->freq;
+    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
+        r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
+    }
+
+    r /= to_divide;
+    return r;
+}
+
+/* determine if x is in between a and b, exclusive of a, inclusive of b */
+
+static inline int64_t is_between(int64_t x, int64_t a, int64_t b)
+{
+    if (a < b) {
+        return x > a && x <= b;
+    }
+    return x < a && x >= b;
+}
+
+static void cadence_timer_run(CadenceTimerState *s)
+{
+    int i;
+    int64_t event_interval, next_value;
+
+    assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
+
+    if (s->reg_count & COUNTER_CTRL_DIS) {
+        s->cpu_time_valid = 0;
+        return;
+    }
+
+    { /* figure out what's going to happen next (rollover or match) */
+        int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ?
+                (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
+        next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
+        for (i = 0; i < 3; ++i) {
+            int64_t cand = (uint64_t)s->reg_match[i] << 16;
+            if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
+                next_value = cand;
+            }
+        }
+    }
+    DB_PRINT("next timer event value: %09llx\n",
+            (unsigned long long)next_value);
+
+    event_interval = next_value - (int64_t)s->reg_value;
+    event_interval = (event_interval < 0) ? -event_interval : event_interval;
+
+    qemu_mod_timer(s->timer, s->cpu_time +
+                cadence_timer_get_ns(s, event_interval));
+}
+
+static void cadence_timer_sync(CadenceTimerState *s)
+{
+    int i;
+    int64_t r, x;
+    int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ?
+            (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
+    uint64_t old_time = s->cpu_time;
+
+    s->cpu_time = qemu_get_clock_ns(vm_clock);
+    DB_PRINT("cpu time: %lld ns\n", (long long)old_time);
+
+    if (!s->cpu_time_valid || old_time == s->cpu_time) {
+        s->cpu_time_valid = 1;
+        return;
+    }
+
+    r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time);
+    x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
+
+    for (i = 0; i < 3; ++i) {
+        int64_t m = (int64_t)s->reg_match[i] << 16;
+        if (m > interval) {
+            continue;
+        }
+        /* check to see if match event has occurred. check m +/- interval
+         * to account for match events in wrap around cases */
+        if (is_between(m, s->reg_value, x) ||
+            is_between(m + interval, s->reg_value, x) ||
+            is_between(m - interval, s->reg_value, x)) {
+            s->reg_intr |= (2 << i);
+        }
+    }
+    while (x < 0) {
+        x += interval;
+    }
+    s->reg_value = (uint32_t)(x % interval);
+
+    if (s->reg_value != x) {
+        s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
+            COUNTER_INTR_IV : COUNTER_INTR_OV;
+    }
+    cadence_timer_update(s);
+}
+
+static void cadence_timer_tick(void *opaque)
+{
+    CadenceTimerState *s = opaque;
+
+    DB_PRINT("\n");
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+}
+
+static uint32_t cadence_ttc_read_imp(void *opaque, target_phys_addr_t offset)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+    uint32_t value;
+
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        return s->reg_clock;
+
+    case 0x0c: /* counter control */
+    case 0x10:
+    case 0x14:
+        return s->reg_count;
+
+    case 0x18: /* counter value */
+    case 0x1c:
+    case 0x20:
+        return (uint16_t)(s->reg_value >> 16);
+
+    case 0x24: /* reg_interval counter */
+    case 0x28:
+    case 0x2c:
+        return s->reg_interval;
+
+    case 0x30: /* match 1 counter */
+    case 0x34:
+    case 0x38:
+        return s->reg_match[0];
+
+    case 0x3c: /* match 2 counter */
+    case 0x40:
+    case 0x44:
+        return s->reg_match[1];
+
+    case 0x48: /* match 3 counter */
+    case 0x4c:
+    case 0x50:
+        return s->reg_match[2];
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        /* cleared after read */
+        value = s->reg_intr;
+        s->reg_intr = 0;
+        return value;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        return s->reg_intr_en;
+
+    case 0x6c:
+    case 0x70:
+    case 0x74:
+        return s->reg_event_ctrl;
+
+    case 0x78:
+    case 0x7c:
+    case 0x80:
+        return s->reg_event;
+
+    default:
+        return 0;
+    }
+}
+
+static uint64_t cadence_ttc_read(void *opaque, target_phys_addr_t offset,
+    unsigned size)
+{
+    uint32_t ret = cadence_ttc_read_imp(opaque, offset);
+
+    DB_PRINT("addr: %08x data: %08x\n", offset, ret);
+    return ret;
+}
+
+static void cadence_ttc_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+
+    DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value);
+
+    cadence_timer_sync(s);
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        s->reg_clock = value & 0x3F;
+        break;
+
+    case 0x0c: /* counter control */
+    case 0x10:
+    case 0x14:
+        if (value & COUNTER_CTRL_RST) {
+            s->reg_value = 0;
+        }
+        s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
+        break;
+
+    case 0x24: /* interval register */
+    case 0x28:
+    case 0x2c:
+        s->reg_interval = value & 0xffff;
+        break;
+
+    case 0x30: /* match register */
+    case 0x34:
+    case 0x38:
+        s->reg_match[0] = value & 0xffff;
+
+    case 0x3c: /* match register */
+    case 0x40:
+    case 0x44:
+        s->reg_match[1] = value & 0xffff;
+
+    case 0x48: /* match register */
+    case 0x4c:
+    case 0x50:
+        s->reg_match[2] = value & 0xffff;
+        break;
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        s->reg_intr &= (~value & 0xfff);
+        break;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        s->reg_intr_en = value & 0x3f;
+        break;
+
+    case 0x6c: /* event control */
+    case 0x70:
+    case 0x74:
+        s->reg_event_ctrl = value & 0x07;
+        break;
+
+    default:
+        return;
+    }
+
+    cadence_timer_run(s);
+    cadence_timer_update(s);
+}
+
+static const MemoryRegionOps cadence_ttc_ops = {
+    .read = cadence_ttc_read,
+    .write = cadence_ttc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void cadence_timer_reset(CadenceTimerState *s)
+{
+   s->reg_count = 0x21;
+}
+
+static void cadence_timer_init(uint32_t freq, CadenceTimerState *s)
+{
+    memset(s, 0, sizeof(CadenceTimerState));
+    s->freq = freq;
+
+    cadence_timer_reset(s);
+
+    s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s);
+}
+
+static int cadence_ttc_init(SysBusDevice *dev)
+{
+    CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev);
+    int i;
+
+    for (i = 0; i < 3; ++i) {
+        cadence_timer_init(2500000, &s->timer[i]);
+        sysbus_init_irq(dev, &s->timer[i].irq);
+    }
+
+    memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void cadence_timer_pre_save(void *opaque)
+{
+    cadence_timer_sync((CadenceTimerState *)opaque);
+}
+
+static int cadence_timer_post_load(void *opaque, int version_id)
+{
+    CadenceTimerState *s = opaque;
+
+    s->cpu_time_valid = 0;
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+    cadence_timer_update(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_timer = {
+    .name = "cadence_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = cadence_timer_pre_save,
+    .post_load = cadence_timer_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(reg_clock, CadenceTimerState),
+        VMSTATE_UINT32(reg_count, CadenceTimerState),
+        VMSTATE_UINT32(reg_value, CadenceTimerState),
+        VMSTATE_UINT16(reg_interval, CadenceTimerState),
+        VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
+        VMSTATE_UINT32(reg_intr, CadenceTimerState),
+        VMSTATE_UINT32(reg_intr_en, CadenceTimerState),
+        VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState),
+        VMSTATE_UINT32(reg_event, CadenceTimerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_cadence_ttc = {
+    .name = "cadence_TTC",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0,
+                            vmstate_cadence_timer,
+                            CadenceTimerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void cadence_ttc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = cadence_ttc_init;
+    dc->vmsd = &vmstate_cadence_ttc;
+}
+
+static TypeInfo cadence_ttc_info = {
+    .name  = "cadence_ttc",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(CadenceTTCState),
+    .class_init = cadence_ttc_class_init,
+};
+
+static void cadence_ttc_register_types(void)
+{
+    type_register_static(&cadence_ttc_info);
+}
+
+type_init(cadence_ttc_register_types)
diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c
new file mode 100644
index 0000000000..d98e531372
--- /dev/null
+++ b/hw/cadence_uart.c
@@ -0,0 +1,513 @@
+/*
+ * Device model for Cadence UART
+ *
+ * Copyright (c) 2010 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written by Haibing Ma
+ *            M.Habib
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "qemu-timer.h"
+
+#ifdef CADENCE_UART_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define UART_SR_INTR_RTRIG     0x00000001
+#define UART_SR_INTR_REMPTY    0x00000002
+#define UART_SR_INTR_RFUL      0x00000004
+#define UART_SR_INTR_TEMPTY    0x00000008
+#define UART_SR_INTR_TFUL      0x00000010
+/* bits fields in CSR that correlate to CISR. If any of these bits are set in
+ * SR, then the same bit in CISR is set high too */
+#define UART_SR_TO_CISR_MASK   0x0000001F
+
+#define UART_INTR_ROVR         0x00000020
+#define UART_INTR_FRAME        0x00000040
+#define UART_INTR_PARE         0x00000080
+#define UART_INTR_TIMEOUT      0x00000100
+#define UART_INTR_DMSI         0x00000200
+
+#define UART_SR_RACTIVE    0x00000400
+#define UART_SR_TACTIVE    0x00000800
+#define UART_SR_FDELT      0x00001000
+
+#define UART_CR_RXRST       0x00000001
+#define UART_CR_TXRST       0x00000002
+#define UART_CR_RX_EN       0x00000004
+#define UART_CR_RX_DIS      0x00000008
+#define UART_CR_TX_EN       0x00000010
+#define UART_CR_TX_DIS      0x00000020
+#define UART_CR_RST_TO      0x00000040
+#define UART_CR_STARTBRK    0x00000080
+#define UART_CR_STOPBRK     0x00000100
+
+#define UART_MR_CLKS            0x00000001
+#define UART_MR_CHRL            0x00000006
+#define UART_MR_CHRL_SH         1
+#define UART_MR_PAR             0x00000038
+#define UART_MR_PAR_SH          3
+#define UART_MR_NBSTOP          0x000000C0
+#define UART_MR_NBSTOP_SH       6
+#define UART_MR_CHMODE          0x00000300
+#define UART_MR_CHMODE_SH       8
+#define UART_MR_UCLKEN          0x00000400
+#define UART_MR_IRMODE          0x00000800
+
+#define UART_DATA_BITS_6       (0x3 << UART_MR_CHRL_SH)
+#define UART_DATA_BITS_7       (0x2 << UART_MR_CHRL_SH)
+#define UART_PARITY_ODD        (0x1 << UART_MR_PAR_SH)
+#define UART_PARITY_EVEN       (0x0 << UART_MR_PAR_SH)
+#define UART_STOP_BITS_1       (0x3 << UART_MR_NBSTOP_SH)
+#define UART_STOP_BITS_2       (0x2 << UART_MR_NBSTOP_SH)
+#define NORMAL_MODE            (0x0 << UART_MR_CHMODE_SH)
+#define ECHO_MODE              (0x1 << UART_MR_CHMODE_SH)
+#define LOCAL_LOOPBACK         (0x2 << UART_MR_CHMODE_SH)
+#define REMOTE_LOOPBACK        (0x3 << UART_MR_CHMODE_SH)
+
+#define RX_FIFO_SIZE           16
+#define TX_FIFO_SIZE           16
+#define UART_INPUT_CLK         50000000
+
+#define R_CR       (0x00/4)
+#define R_MR       (0x04/4)
+#define R_IER      (0x08/4)
+#define R_IDR      (0x0C/4)
+#define R_IMR      (0x10/4)
+#define R_CISR     (0x14/4)
+#define R_BRGR     (0x18/4)
+#define R_RTOR     (0x1C/4)
+#define R_RTRIG    (0x20/4)
+#define R_MCR      (0x24/4)
+#define R_MSR      (0x28/4)
+#define R_SR       (0x2C/4)
+#define R_TX_RX    (0x30/4)
+#define R_BDIV     (0x34/4)
+#define R_FDEL     (0x38/4)
+#define R_PMIN     (0x3C/4)
+#define R_PWID     (0x40/4)
+#define R_TTRIG    (0x44/4)
+
+#define R_MAX (R_TTRIG + 1)
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t r[R_MAX];
+    uint8_t r_fifo[RX_FIFO_SIZE];
+    uint32_t rx_wpos;
+    uint32_t rx_count;
+    uint64_t char_tx_time;
+    CharDriverState *chr;
+    qemu_irq irq;
+    struct QEMUTimer *fifo_trigger_handle;
+    struct QEMUTimer *tx_time_handle;
+} UartState;
+
+static void uart_update_status(UartState *s)
+{
+    s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK;
+    qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR]));
+}
+
+static void fifo_trigger_update(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    s->r[R_CISR] |= UART_INTR_TIMEOUT;
+
+    uart_update_status(s);
+}
+
+static void uart_tx_redo(UartState *s)
+{
+    uint64_t new_tx_time = qemu_get_clock_ns(vm_clock);
+
+    qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time);
+
+    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+
+    uart_update_status(s);
+}
+
+static void uart_tx_write(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    uart_tx_redo(s);
+}
+
+static void uart_rx_reset(UartState *s)
+{
+    s->rx_wpos = 0;
+    s->rx_count = 0;
+
+    s->r[R_SR] |= UART_SR_INTR_REMPTY;
+    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+}
+
+static void uart_tx_reset(UartState *s)
+{
+    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+    s->r[R_SR] &= ~UART_SR_INTR_TFUL;
+}
+
+static void uart_send_breaks(UartState *s)
+{
+    int break_enabled = 1;
+
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                               &break_enabled);
+}
+
+static void uart_parameters_setup(UartState *s)
+{
+    QEMUSerialSetParams ssp;
+    unsigned int baud_rate, packet_size;
+
+    baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
+            UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+
+    ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+    packet_size = 1;
+
+    switch (s->r[R_MR] & UART_MR_PAR) {
+    case UART_PARITY_EVEN:
+        ssp.parity = 'E';
+        packet_size++;
+        break;
+    case UART_PARITY_ODD:
+        ssp.parity = 'O';
+        packet_size++;
+        break;
+    default:
+        ssp.parity = 'N';
+        break;
+    }
+
+    switch (s->r[R_MR] & UART_MR_CHRL) {
+    case UART_DATA_BITS_6:
+        ssp.data_bits = 6;
+        break;
+    case UART_DATA_BITS_7:
+        ssp.data_bits = 7;
+        break;
+    default:
+        ssp.data_bits = 8;
+        break;
+    }
+
+    switch (s->r[R_MR] & UART_MR_NBSTOP) {
+    case UART_STOP_BITS_1:
+        ssp.stop_bits = 1;
+        break;
+    default:
+        ssp.stop_bits = 2;
+        break;
+    }
+
+    packet_size += ssp.data_bits + ssp.stop_bits;
+    s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static int uart_can_receive(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    return RX_FIFO_SIZE - s->rx_count;
+}
+
+static void uart_ctrl_update(UartState *s)
+{
+    if (s->r[R_CR] & UART_CR_TXRST) {
+        uart_tx_reset(s);
+    }
+
+    if (s->r[R_CR] & UART_CR_RXRST) {
+        uart_rx_reset(s);
+    }
+
+    s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST);
+
+    if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
+            uart_tx_redo(s);
+    }
+
+    if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
+        uart_send_breaks(s);
+    }
+}
+
+static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
+{
+    UartState *s = (UartState *)opaque;
+    uint64_t new_rx_time = qemu_get_clock_ns(vm_clock);
+    int i;
+
+    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+        return;
+    }
+
+    s->r[R_SR] &= ~UART_SR_INTR_REMPTY;
+
+    if (s->rx_count == RX_FIFO_SIZE) {
+        s->r[R_CISR] |= UART_INTR_ROVR;
+    } else {
+        for (i = 0; i < size; i++) {
+            s->r_fifo[s->rx_wpos] = buf[i];
+            s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
+            s->rx_count++;
+
+            if (s->rx_count == RX_FIFO_SIZE) {
+                s->r[R_SR] |= UART_SR_INTR_RFUL;
+                break;
+            }
+
+            if (s->rx_count >= s->r[R_RTRIG]) {
+                s->r[R_SR] |= UART_SR_INTR_RTRIG;
+            }
+        }
+        qemu_mod_timer(s->fifo_trigger_handle, new_rx_time +
+                                                (s->char_tx_time * 4));
+    }
+    uart_update_status(s);
+}
+
+static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
+{
+    if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
+        return;
+    }
+
+    while (size) {
+        size -= qemu_chr_fe_write(s->chr, buf, size);
+    }
+}
+
+static void uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    UartState *s = (UartState *)opaque;
+    uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
+
+    if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
+        uart_write_rx_fifo(opaque, buf, size);
+    }
+    if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
+        uart_write_tx_fifo(s, buf, size);
+    }
+}
+
+static void uart_event(void *opaque, int event)
+{
+    UartState *s = (UartState *)opaque;
+    uint8_t buf = '\0';
+
+    if (event == CHR_EVENT_BREAK) {
+        uart_write_rx_fifo(opaque, &buf, 1);
+    }
+
+    uart_update_status(s);
+}
+
+static void uart_read_rx_fifo(UartState *s, uint32_t *c)
+{
+    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+        return;
+    }
+
+    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+
+    if (s->rx_count) {
+        uint32_t rx_rpos =
+                (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
+        *c = s->r_fifo[rx_rpos];
+        s->rx_count--;
+
+        if (!s->rx_count) {
+            s->r[R_SR] |= UART_SR_INTR_REMPTY;
+        }
+    } else {
+        *c = 0;
+        s->r[R_SR] |= UART_SR_INTR_REMPTY;
+    }
+
+    if (s->rx_count < s->r[R_RTRIG]) {
+        s->r[R_SR] &= ~UART_SR_INTR_RTRIG;
+    }
+    uart_update_status(s);
+}
+
+static void uart_write(void *opaque, target_phys_addr_t offset,
+                          uint64_t value, unsigned size)
+{
+    UartState *s = (UartState *)opaque;
+
+    DB_PRINT(" offset:%x data:%08x\n", offset, (unsigned)value);
+    offset >>= 2;
+    switch (offset) {
+    case R_IER: /* ier (wts imr) */
+        s->r[R_IMR] |= value;
+        break;
+    case R_IDR: /* idr (wtc imr) */
+        s->r[R_IMR] &= ~value;
+        break;
+    case R_IMR: /* imr (read only) */
+        break;
+    case R_CISR: /* cisr (wtc) */
+        s->r[R_CISR] &= ~value;
+        break;
+    case R_TX_RX: /* UARTDR */
+        switch (s->r[R_MR] & UART_MR_CHMODE) {
+        case NORMAL_MODE:
+            uart_write_tx_fifo(s, (uint8_t *) &value, 1);
+            break;
+        case LOCAL_LOOPBACK:
+            uart_write_rx_fifo(opaque, (uint8_t *) &value, 1);
+            break;
+        }
+        break;
+    default:
+        s->r[offset] = value;
+    }
+
+    switch (offset) {
+    case R_CR:
+        uart_ctrl_update(s);
+        break;
+    case R_MR:
+        uart_parameters_setup(s);
+        break;
+    }
+}
+
+static uint64_t uart_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    UartState *s = (UartState *)opaque;
+    uint32_t c = 0;
+
+    offset >>= 2;
+    if (offset > R_MAX) {
+        return 0;
+    } else if (offset == R_TX_RX) {
+        uart_read_rx_fifo(s, &c);
+        return c;
+    }
+    return s->r[offset];
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void cadence_uart_reset(UartState *s)
+{
+    s->r[R_CR] = 0x00000128;
+    s->r[R_IMR] = 0;
+    s->r[R_CISR] = 0;
+    s->r[R_RTRIG] = 0x00000020;
+    s->r[R_BRGR] = 0x0000000F;
+    s->r[R_TTRIG] = 0x00000020;
+
+    uart_rx_reset(s);
+    uart_tx_reset(s);
+
+    s->rx_count = 0;
+    s->rx_wpos = 0;
+}
+
+static int cadence_uart_init(SysBusDevice *dev)
+{
+    UartState *s = FROM_SYSBUS(UartState, dev);
+
+    memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+
+    s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock,
+            (QEMUTimerCB *)fifo_trigger_update, s);
+
+    s->tx_time_handle = qemu_new_timer_ns(vm_clock,
+            (QEMUTimerCB *)uart_tx_write, s);
+
+    s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
+
+    s->chr = qemu_char_get_next_serial();
+
+    cadence_uart_reset(s);
+
+    if (s->chr) {
+        qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
+                              uart_event, s);
+    }
+
+    return 0;
+}
+
+static int cadence_uart_post_load(void *opaque, int version_id)
+{
+    UartState *s = opaque;
+
+    uart_parameters_setup(s);
+    uart_update_status(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_uart = {
+    .name = "cadence_uart",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = cadence_uart_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
+        VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE),
+        VMSTATE_UINT32(rx_count, UartState),
+        VMSTATE_UINT32(rx_wpos, UartState),
+        VMSTATE_TIMER(fifo_trigger_handle, UartState),
+        VMSTATE_TIMER(tx_time_handle, UartState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void cadence_uart_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = cadence_uart_init;
+    dc->vmsd = &vmstate_cadence_uart;
+}
+
+static TypeInfo cadence_uart_info = {
+    .name          = "cadence_uart",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(UartState),
+    .class_init    = cadence_uart_class_init,
+};
+
+static void cadence_uart_register_types(void)
+{
+    type_register_static(&cadence_uart_info);
+}
+
+type_init(cadence_uart_register_types)
diff --git a/hw/cris-boot.c b/hw/cris-boot.c
index 37894f8b53..ca6c52fa8e 100644
--- a/hw/cris-boot.c
+++ b/hw/cris-boot.c
@@ -29,12 +29,12 @@
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUCRISState *env = opaque;
     struct cris_load_info *li;
 
     li = env->load_info;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     if (!li) {
         /* nothing more to do.  */
@@ -60,7 +60,7 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
     return addr - 0x80000000LL;
 }
 
-void cris_load_image(CPUState *env, struct cris_load_info *li)
+void cris_load_image(CPUCRISState *env, struct cris_load_info *li)
 {
     uint64_t entry, high;
     int kcmdline_len;
diff --git a/hw/cris-boot.h b/hw/cris-boot.h
index e9caf8dee8..ecb9779e49 100644
--- a/hw/cris-boot.h
+++ b/hw/cris-boot.h
@@ -8,4 +8,4 @@ struct cris_load_info
     target_phys_addr_t entry;
 };
 
-void cris_load_image(CPUState *env, struct cris_load_info *li);
+void cris_load_image(CPUCRISState *env, struct cris_load_info *li);
diff --git a/hw/cris_pic_cpu.c b/hw/cris_pic_cpu.c
index 06ae484950..3da0e86536 100644
--- a/hw/cris_pic_cpu.c
+++ b/hw/cris_pic_cpu.c
@@ -30,7 +30,7 @@
 
 static void cris_pic_cpu_handler(void *opaque, int irq, int level)
 {
-    CPUState *env = (CPUState *)opaque;
+    CPUCRISState *env = (CPUCRISState *)opaque;
     int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
 
     if (level)
@@ -39,7 +39,7 @@ static void cris_pic_cpu_handler(void *opaque, int irq, int level)
         cpu_reset_interrupt(env, type);
 }
 
-qemu_irq *cris_pic_init_cpu(CPUState *env)
+qemu_irq *cris_pic_init_cpu(CPUCRISState *env)
 {
     return qemu_allocate_irqs(cris_pic_cpu_handler, env, 2);
 }
diff --git a/hw/cuda.c b/hw/cuda.c
index 40774360df..233ab666da 100644
--- a/hw/cuda.c
+++ b/hw/cuda.c
@@ -634,7 +634,7 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
     return 0;
 }
 
-static MemoryRegionOps cuda_ops = {
+static const MemoryRegionOps cuda_ops = {
     .old_mmio = {
         .write = {
             cuda_writeb,
diff --git a/hw/ds1338.c b/hw/ds1338.c
index 6397f0aa6f..d590d9c007 100644
--- a/hw/ds1338.c
+++ b/hw/ds1338.c
@@ -100,6 +100,7 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data)
             break;
         case 5:
             s->now.tm_mon = from_bcd(data & 0x1f) - 1;
+            break;
         case 6:
             s->now.tm_year = from_bcd(data) + 100;
             break;
diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c
index e3c574008f..7cc7a99bfb 100644
--- a/hw/dummy_m68k.c
+++ b/hw/dummy_m68k.c
@@ -21,7 +21,7 @@ static void dummy_m68k_init(ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUM68KState *env;
     MemoryRegion *address_space_mem =  get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     int kernel_size;
diff --git a/hw/etraxfs.h b/hw/etraxfs.h
index 24e8fd880b..c62f94b7b7 100644
--- a/hw/etraxfs.h
+++ b/hw/etraxfs.h
@@ -25,7 +25,7 @@
 #include "net.h"
 #include "etraxfs_dma.h"
 
-qemu_irq *cris_pic_init_cpu(CPUState *env);
+qemu_irq *cris_pic_init_cpu(CPUCRISState *env);
 
 /* Instantiate an ETRAXFS Ethernet MAC.  */
 static inline DeviceState *
diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c
index cecd819584..5f16b17835 100644
--- a/hw/etraxfs_ser.c
+++ b/hw/etraxfs_ser.c
@@ -78,7 +78,7 @@ static uint64_t
 ser_read(void *opaque, target_phys_addr_t addr, unsigned int size)
 {
     struct etrax_serial *s = opaque;
-    D(CPUState *env = s->env);
+    D(CPUCRISState *env = s->env);
     uint32_t r = 0;
 
     addr >>= 2;
@@ -116,7 +116,7 @@ ser_write(void *opaque, target_phys_addr_t addr,
     struct etrax_serial *s = opaque;
     uint32_t value = val64;
     unsigned char ch = val64;
-    D(CPUState *env = s->env);
+    D(CPUCRISState *env = s->env);
 
     D(qemu_log("%s " TARGET_FMT_plx "=%x\n",  __func__, addr, value));
     addr >>= 2;
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index e7522f851a..c112e03bfb 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -83,7 +83,7 @@ typedef struct Exynos4210Irq {
 } Exynos4210Irq;
 
 typedef struct Exynos4210State {
-    CPUState * env[EXYNOS4210_NCPUS];
+    CPUARMState * env[EXYNOS4210_NCPUS];
     Exynos4210Irq irqs;
     qemu_irq *irq_table;
 
diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c
index 01e3fb8a3b..7474fcf802 100644
--- a/hw/exynos4210_mct.c
+++ b/hw/exynos4210_mct.c
@@ -888,7 +888,7 @@ static void exynos4210_ltick_event(void *opaque)
     static uint64_t time2[2] = {0};
 #endif
 
-    /* Call tick_timer event handler, it will update it's tcntb and icntb */
+    /* Call tick_timer event handler, it will update its tcntb and icntb. */
     exynos4210_ltick_timer_event(&s->tick_timer);
 
     /* get tick_timer cnt */
diff --git a/hw/fdc.c b/hw/fdc.c
index 38fad587cb..a0236b7295 100644
--- a/hw/fdc.c
+++ b/hw/fdc.c
@@ -62,12 +62,15 @@
 #define FD_SECTOR_SC           2   /* Sector size code */
 #define FD_RESET_SENSEI_COUNT  4   /* Number of sense interrupts on RESET */
 
+typedef struct FDCtrl FDCtrl;
+
 /* Floppy disk drive emulation */
 typedef enum FDiskFlags {
     FDISK_DBL_SIDES  = 0x01,
 } FDiskFlags;
 
 typedef struct FDrive {
+    FDCtrl *fdctrl;
     BlockDriverState *bs;
     /* Drive status */
     FDriveType drive;
@@ -83,6 +86,7 @@ typedef struct FDrive {
     uint16_t bps;             /* Bytes per sector       */
     uint8_t ro;               /* Is read-only           */
     uint8_t media_changed;    /* Is media changed       */
+    uint8_t media_rate;       /* Data rate of medium    */
 } FDrive;
 
 static void fd_init(FDrive *drv)
@@ -95,16 +99,19 @@ static void fd_init(FDrive *drv)
     drv->max_track = 0;
 }
 
+#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
+
 static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
-                          uint8_t last_sect)
+                          uint8_t last_sect, uint8_t num_sides)
 {
-    return (((track * 2) + head) * last_sect) + sect - 1;
+    return (((track * num_sides) + head) * last_sect) + sect - 1;
 }
 
 /* Returns current position, in sectors, for given drive */
 static int fd_sector(FDrive *drv)
 {
-    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect);
+    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
+                          NUM_SIDES(drv));
 }
 
 /* Seek to a new position:
@@ -135,7 +142,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
                        drv->max_track, drv->last_sect);
         return 3;
     }
-    sector = fd_sector_calc(head, track, sect, drv->last_sect);
+    sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
     ret = 0;
     if (sector != fd_sector(drv)) {
 #if 0
@@ -169,12 +176,13 @@ static void fd_revalidate(FDrive *drv)
 {
     int nb_heads, max_track, last_sect, ro;
     FDriveType drive;
+    FDriveRate rate;
 
     FLOPPY_DPRINTF("revalidate\n");
     if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
         ro = bdrv_is_read_only(drv->bs);
         bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track,
-                                      &last_sect, drv->drive, &drive);
+                                      &last_sect, drv->drive, &drive, &rate);
         if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
             FLOPPY_DPRINTF("User defined disk (%d %d %d)",
                            nb_heads - 1, max_track, last_sect);
@@ -191,6 +199,7 @@ static void fd_revalidate(FDrive *drv)
         drv->last_sect = last_sect;
         drv->ro = ro;
         drv->drive = drive;
+        drv->media_rate = rate;
     } else {
         FLOPPY_DPRINTF("No disk in drive\n");
         drv->last_sect = 0;
@@ -202,13 +211,12 @@ static void fd_revalidate(FDrive *drv)
 /********************************************************/
 /* Intel 82078 floppy disk controller emulation          */
 
-typedef struct FDCtrl FDCtrl;
-
 static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
 static void fdctrl_reset_fifo(FDCtrl *fdctrl);
 static int fdctrl_transfer_handler (void *opaque, int nchan,
                                     int dma_pos, int dma_len);
 static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0);
+static FDrive *get_cur_drv(FDCtrl *fdctrl);
 
 static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
 static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
@@ -221,6 +229,7 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
 static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
 static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
 static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
 
 enum {
     FD_DIR_WRITE   = 0,
@@ -245,6 +254,7 @@ enum {
     FD_REG_DSR = 0x04,
     FD_REG_FIFO = 0x05,
     FD_REG_DIR = 0x07,
+    FD_REG_CCR = 0x07,
 };
 
 enum {
@@ -297,6 +307,8 @@ enum {
 };
 
 enum {
+    FD_SR1_MA       = 0x01, /* Missing address mark */
+    FD_SR1_NW       = 0x02, /* Not writable */
     FD_SR1_EC       = 0x80, /* End of cylinder */
 };
 
@@ -413,6 +425,7 @@ struct FDCtrl {
     int sun4m;
     FDrive drives[MAX_FD];
     int reset_sensei;
+    uint32_t check_media_rate;
     /* Timers state */
     uint8_t timer0;
     uint8_t timer1;
@@ -487,6 +500,9 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
     case FD_REG_FIFO:
         fdctrl_write_data(fdctrl, value);
         break;
+    case FD_REG_CCR:
+        fdctrl_write_ccr(fdctrl, value);
+        break;
     default:
         break;
     }
@@ -538,6 +554,24 @@ static const VMStateDescription vmstate_fdrive_media_changed = {
     }
 };
 
+static bool fdrive_media_rate_needed(void *opaque)
+{
+    FDrive *drive = opaque;
+
+    return drive->fdctrl->check_media_rate;
+}
+
+static const VMStateDescription vmstate_fdrive_media_rate = {
+    .name = "fdrive/media_rate",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(media_rate, FDrive),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_fdrive = {
     .name = "fdrive",
     .version_id = 1,
@@ -554,6 +588,9 @@ static const VMStateDescription vmstate_fdrive = {
             .vmsd = &vmstate_fdrive_media_changed,
             .needed = &fdrive_media_changed_needed,
         } , {
+            .vmsd = &vmstate_fdrive_media_rate,
+            .needed = &fdrive_media_rate_needed,
+        } , {
             /* empty */
         }
     }
@@ -877,6 +914,23 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
     fdctrl->dsr = value;
 }
 
+/* Configuration control register: 0x07 (write) */
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value)
+{
+    /* Reset mode */
+    if (!(fdctrl->dor & FD_DOR_nRESET)) {
+        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+        return;
+    }
+    FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);
+
+    /* Only the rate selection bits used in AT mode, and we
+     * store those in the DSR.
+     */
+    fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) |
+                  (value & FD_DSR_DRATEMASK);
+}
+
 static int fdctrl_media_changed(FDrive *drv)
 {
     int ret;
@@ -903,14 +957,9 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
 {
     uint32_t retval = 0;
 
-    if (fdctrl_media_changed(drv0(fdctrl))
-     || fdctrl_media_changed(drv1(fdctrl))
-#if MAX_FD == 4
-     || fdctrl_media_changed(drv2(fdctrl))
-     || fdctrl_media_changed(drv3(fdctrl))
-#endif
-        )
+    if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
         retval |= FD_DIR_DSKCHG;
+    }
     if (retval != 0) {
         FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
     }
@@ -1019,7 +1068,8 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
     ks = fdctrl->fifo[4];
     FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
                    GET_CUR_DRV(fdctrl), kh, kt, ks,
-                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+                                  NUM_SIDES(cur_drv)));
     switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
     case 2:
         /* sect too big */
@@ -1049,6 +1099,19 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
         break;
     }
 
+    /* Check the data rate. If the programmed data rate does not match
+     * the currently inserted medium, the operation has to fail. */
+    if (fdctrl->check_media_rate &&
+        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+        FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
+                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    }
+
     /* Set the FIFO state */
     fdctrl->data_dir = direction;
     fdctrl->data_pos = 0;
@@ -1175,6 +1238,16 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
             break;
         case FD_DIR_WRITE:
             /* WRITE commands */
+            if (cur_drv->ro) {
+                /* Handle readonly medium early, no need to do DMA, touch the
+                 * LED or attempt any writes. A real floppy doesn't attempt
+                 * to write to readonly media either. */
+                fdctrl_stop_transfer(fdctrl,
+                                     FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
+                                     0x00);
+                goto transfer_error;
+            }
+
             DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
                              fdctrl->data_pos, len);
             if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
@@ -1289,7 +1362,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl)
     ks = fdctrl->fifo[8];
     FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
                    GET_CUR_DRV(fdctrl), kh, kt, ks,
-                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+                                  NUM_SIDES(cur_drv)));
     switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
     case 2:
         /* sect too big */
@@ -1343,7 +1417,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
 {
     fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
     fdctrl->fifo[0] = fdctrl->lock << 4;
-    fdctrl_set_fifo(fdctrl, 1, fdctrl->lock);
+    fdctrl_set_fifo(fdctrl, 1, 0);
 }
 
 static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
@@ -1375,7 +1449,7 @@ static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
 {
     /* Controller's version */
     fdctrl->fifo[0] = fdctrl->version;
-    fdctrl_set_fifo(fdctrl, 1, 1);
+    fdctrl_set_fifo(fdctrl, 1, 0);
 }
 
 static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
@@ -1434,14 +1508,13 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
     fdctrl->fifo[12] = fdctrl->pwrd;
     fdctrl->fifo[13] = 0;
     fdctrl->fifo[14] = 0;
-    fdctrl_set_fifo(fdctrl, 15, 1);
+    fdctrl_set_fifo(fdctrl, 15, 0);
 }
 
 static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
 {
     FDrive *cur_drv = get_cur_drv(fdctrl);
 
-    /* XXX: should set main status register to busy */
     cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
     qemu_mod_timer(fdctrl->result_timer,
                    qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50));
@@ -1545,13 +1618,16 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
     SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
     cur_drv = get_cur_drv(fdctrl);
     fdctrl_reset_fifo(fdctrl);
+    /* The seek command just sends step pulses to the drive and doesn't care if
+     * there is a medium inserted of if it's banging the head against the drive.
+     */
     if (fdctrl->fifo[2] > cur_drv->max_track) {
-        fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
+        cur_drv->track = cur_drv->max_track;
     } else {
         cur_drv->track = fdctrl->fifo[2];
-        /* Raise Interrupt */
-        fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
     }
+    /* Raise Interrupt */
+    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
 }
 
 static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
@@ -1576,7 +1652,7 @@ static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
 {
     fdctrl->pwrd = fdctrl->fifo[1];
     fdctrl->fifo[0] = fdctrl->fifo[1];
-    fdctrl_set_fifo(fdctrl, 1, 1);
+    fdctrl_set_fifo(fdctrl, 1, 0);
 }
 
 static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
@@ -1595,7 +1671,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
             fdctrl->fifo[0] = fdctrl->fifo[1];
             fdctrl->fifo[2] = 0;
             fdctrl->fifo[3] = 0;
-            fdctrl_set_fifo(fdctrl, 4, 1);
+            fdctrl_set_fifo(fdctrl, 4, 0);
         } else {
             fdctrl_reset_fifo(fdctrl);
         }
@@ -1603,7 +1679,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
         /* ERROR */
         fdctrl->fifo[0] = 0x80 |
             (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
-        fdctrl_set_fifo(fdctrl, 1, 1);
+        fdctrl_set_fifo(fdctrl, 1, 0);
     }
 }
 
@@ -1729,6 +1805,7 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
         pos = command_to_handler[value & 0xff];
         FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
         fdctrl->data_len = handlers[pos].parameters + 1;
+        fdctrl->msr |= FD_MSR_CMDBUSY;
     }
 
     FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
@@ -1760,7 +1837,15 @@ static void fdctrl_result_timer(void *opaque)
     if (cur_drv->last_sect != 0) {
         cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
     }
-    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+    /* READ_ID can't automatically succeed! */
+    if (fdctrl->check_media_rate &&
+        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+        FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
+                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+    } else {
+        fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+    }
 }
 
 static void fdctrl_change_cb(void *opaque, bool load)
@@ -1782,6 +1867,7 @@ static int fdctrl_connect_drives(FDCtrl *fdctrl)
 
     for (i = 0; i < MAX_FD; i++) {
         drive = &fdctrl->drives[i];
+        drive->fdctrl = fdctrl;
 
         if (drive->bs) {
             if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
@@ -1964,6 +2050,8 @@ static Property isa_fdc_properties[] = {
     DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
     DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
     DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
+    DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
+                    0, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/grlib.h b/hw/grlib.h
index fdf4b1190a..e1c41378d4 100644
--- a/hw/grlib.h
+++ b/hw/grlib.h
@@ -42,7 +42,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno);
 
 static inline
 DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
-                                CPUState            *env,
+                                CPUSPARCState            *env,
                                 qemu_irq           **cpu_irqs,
                                 uint32_t             nr_irqs,
                                 set_pil_in_fn        set_pil_in)
diff --git a/hw/highbank.c b/hw/highbank.c
index 489c00e5b9..906eed5a47 100644
--- a/hw/highbank.c
+++ b/hw/highbank.c
@@ -37,12 +37,12 @@
 /* Board init.  */
 static void highbank_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUARMState *env = opaque;
 
     env->cp15.c15_config_base_address = GIC_BASE_ADDR;
 }
 
-static void hb_write_secondary(CPUState *env, const struct arm_boot_info *info)
+static void hb_write_secondary(CPUARMState *env, const struct arm_boot_info *info)
 {
     int n;
     uint32_t smpboot[] = {
@@ -66,7 +66,7 @@ static void hb_write_secondary(CPUState *env, const struct arm_boot_info *info)
     rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR);
 }
 
-static void hb_reset_secondary(CPUState *env, const struct arm_boot_info *info)
+static void hb_reset_secondary(CPUARMState *env, const struct arm_boot_info *info)
 {
     switch (info->nb_cpus) {
     case 4:
@@ -196,7 +196,7 @@ static void highbank_init(ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env = NULL;
+    CPUARMState *env = NULL;
     DeviceState *dev;
     SysBusDevice *busdev;
     qemu_irq *irqp;
diff --git a/hw/i8254.c b/hw/i8254.c
index f30396af88..77bd5e8222 100644
--- a/hw/i8254.c
+++ b/hw/i8254.c
@@ -26,6 +26,7 @@
 #include "isa.h"
 #include "qemu-timer.h"
 #include "i8254.h"
+#include "i8254_internal.h"
 
 //#define DEBUG_PIT
 
@@ -34,34 +35,6 @@
 #define RW_STATE_WORD0 3
 #define RW_STATE_WORD1 4
 
-typedef struct PITChannelState {
-    int count; /* can be 65536 */
-    uint16_t latched_count;
-    uint8_t count_latched;
-    uint8_t status_latched;
-    uint8_t status;
-    uint8_t read_state;
-    uint8_t write_state;
-    uint8_t write_latch;
-    uint8_t rw_mode;
-    uint8_t mode;
-    uint8_t bcd; /* not supported */
-    uint8_t gate; /* timer start */
-    int64_t count_load_time;
-    /* irq handling */
-    int64_t next_transition_time;
-    QEMUTimer *irq_timer;
-    qemu_irq irq;
-    uint32_t irq_disabled;
-} PITChannelState;
-
-typedef struct PITState {
-    ISADevice dev;
-    MemoryRegion ioports;
-    uint32_t iobase;
-    PITChannelState channels[3];
-} PITState;
-
 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
 
 static int pit_get_count(PITChannelState *s)
@@ -89,99 +62,11 @@ static int pit_get_count(PITChannelState *s)
     return counter;
 }
 
-/* get pit output bit */
-static int pit_get_out(PITChannelState *s, int64_t current_time)
-{
-    uint64_t d;
-    int out;
-
-    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
-                 get_ticks_per_sec());
-    switch(s->mode) {
-    default:
-    case 0:
-        out = (d >= s->count);
-        break;
-    case 1:
-        out = (d < s->count);
-        break;
-    case 2:
-        if ((d % s->count) == 0 && d != 0)
-            out = 1;
-        else
-            out = 0;
-        break;
-    case 3:
-        out = (d % s->count) < ((s->count + 1) >> 1);
-        break;
-    case 4:
-    case 5:
-        out = (d == s->count);
-        break;
-    }
-    return out;
-}
-
-/* return -1 if no transition will occur.  */
-static int64_t pit_get_next_transition_time(PITChannelState *s,
-                                            int64_t current_time)
-{
-    uint64_t d, next_time, base;
-    int period2;
-
-    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
-                 get_ticks_per_sec());
-    switch(s->mode) {
-    default:
-    case 0:
-    case 1:
-        if (d < s->count)
-            next_time = s->count;
-        else
-            return -1;
-        break;
-    case 2:
-        base = (d / s->count) * s->count;
-        if ((d - base) == 0 && d != 0)
-            next_time = base + s->count;
-        else
-            next_time = base + s->count + 1;
-        break;
-    case 3:
-        base = (d / s->count) * s->count;
-        period2 = ((s->count + 1) >> 1);
-        if ((d - base) < period2)
-            next_time = base + period2;
-        else
-            next_time = base + s->count;
-        break;
-    case 4:
-    case 5:
-        if (d < s->count)
-            next_time = s->count;
-        else if (d == s->count)
-            next_time = s->count + 1;
-        else
-            return -1;
-        break;
-    }
-    /* convert to timer units */
-    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
-                                              PIT_FREQ);
-    /* fix potential rounding problems */
-    /* XXX: better solution: use a clock at PIT_FREQ Hz */
-    if (next_time <= current_time)
-        next_time = current_time + 1;
-    return next_time;
-}
-
 /* val must be 0 or 1 */
-void pit_set_gate(ISADevice *dev, int channel, int val)
+static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc,
+                                 int val)
 {
-    PITState *pit = DO_UPCAST(PITState, dev, dev);
-    PITChannelState *s = &pit->channels[channel];
-
-    switch(s->mode) {
+    switch (sc->mode) {
     default:
     case 0:
     case 4:
@@ -189,34 +74,23 @@ void pit_set_gate(ISADevice *dev, int channel, int val)
         break;
     case 1:
     case 5:
-        if (s->gate < val) {
+        if (sc->gate < val) {
             /* restart counting on rising edge */
-            s->count_load_time = qemu_get_clock_ns(vm_clock);
-            pit_irq_timer_update(s, s->count_load_time);
+            sc->count_load_time = qemu_get_clock_ns(vm_clock);
+            pit_irq_timer_update(sc, sc->count_load_time);
         }
         break;
     case 2:
     case 3:
-        if (s->gate < val) {
+        if (sc->gate < val) {
             /* restart counting on rising edge */
-            s->count_load_time = qemu_get_clock_ns(vm_clock);
-            pit_irq_timer_update(s, s->count_load_time);
+            sc->count_load_time = qemu_get_clock_ns(vm_clock);
+            pit_irq_timer_update(sc, sc->count_load_time);
         }
         /* XXX: disable/enable counting */
         break;
     }
-    s->gate = val;
-}
-
-void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
-{
-    PITState *pit = DO_UPCAST(PITState, dev, dev);
-    PITChannelState *s = &pit->channels[channel];
-
-    info->gate = s->gate;
-    info->mode = s->mode;
-    info->initial_count = s->count;
-    info->out = pit_get_out(s, qemu_get_clock_ns(vm_clock));
+    sc->gate = val;
 }
 
 static inline void pit_load_count(PITChannelState *s, int val)
@@ -239,7 +113,7 @@ static void pit_latch_count(PITChannelState *s)
 
 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
-    PITState *pit = opaque;
+    PITCommonState *pit = opaque;
     int channel, access;
     PITChannelState *s;
 
@@ -306,7 +180,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 
 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
 {
-    PITState *pit = opaque;
+    PITCommonState *pit = opaque;
     int ret, count;
     PITChannelState *s;
 
@@ -387,94 +261,16 @@ static void pit_irq_timer(void *opaque)
     pit_irq_timer_update(s, s->next_transition_time);
 }
 
-static const VMStateDescription vmstate_pit_channel = {
-    .name = "pit channel",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(count, PITChannelState),
-        VMSTATE_UINT16(latched_count, PITChannelState),
-        VMSTATE_UINT8(count_latched, PITChannelState),
-        VMSTATE_UINT8(status_latched, PITChannelState),
-        VMSTATE_UINT8(status, PITChannelState),
-        VMSTATE_UINT8(read_state, PITChannelState),
-        VMSTATE_UINT8(write_state, PITChannelState),
-        VMSTATE_UINT8(write_latch, PITChannelState),
-        VMSTATE_UINT8(rw_mode, PITChannelState),
-        VMSTATE_UINT8(mode, PITChannelState),
-        VMSTATE_UINT8(bcd, PITChannelState),
-        VMSTATE_UINT8(gate, PITChannelState),
-        VMSTATE_INT64(count_load_time, PITChannelState),
-        VMSTATE_INT64(next_transition_time, PITChannelState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
+static void pit_reset(DeviceState *dev)
 {
-    PITState *pit = opaque;
+    PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev);
     PITChannelState *s;
-    int i;
-
-    if (version_id != 1)
-        return -EINVAL;
-
-    for(i = 0; i < 3; i++) {
-        s = &pit->channels[i];
-        s->count=qemu_get_be32(f);
-        qemu_get_be16s(f, &s->latched_count);
-        qemu_get_8s(f, &s->count_latched);
-        qemu_get_8s(f, &s->status_latched);
-        qemu_get_8s(f, &s->status);
-        qemu_get_8s(f, &s->read_state);
-        qemu_get_8s(f, &s->write_state);
-        qemu_get_8s(f, &s->write_latch);
-        qemu_get_8s(f, &s->rw_mode);
-        qemu_get_8s(f, &s->mode);
-        qemu_get_8s(f, &s->bcd);
-        qemu_get_8s(f, &s->gate);
-        s->count_load_time=qemu_get_be64(f);
-        s->irq_disabled = 0;
-        if (s->irq_timer) {
-            s->next_transition_time=qemu_get_be64(f);
-            qemu_get_timer(f, s->irq_timer);
-        }
-    }
-    return 0;
-}
 
-static const VMStateDescription vmstate_pit = {
-    .name = "i8254",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 1,
-    .load_state_old = pit_load_old,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3),
-        VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
-        VMSTATE_TIMER(channels[0].irq_timer, PITState),
-        VMSTATE_END_OF_LIST()
-    }
-};
+    pit_reset_common(pit);
 
-static void pit_reset(DeviceState *dev)
-{
-    PITState *pit = container_of(dev, PITState, dev.qdev);
-    PITChannelState *s;
-    int i;
-
-    for(i = 0;i < 3; i++) {
-        s = &pit->channels[i];
-        s->mode = 3;
-        s->gate = (i != 2);
-        s->count_load_time = qemu_get_clock_ns(vm_clock);
-        s->count = 0x10000;
-        if (i == 0 && !s->irq_disabled) {
-            s->next_transition_time =
-                pit_get_next_transition_time(s, s->count_load_time);
-            qemu_mod_timer(s->irq_timer, s->next_transition_time);
-        }
+    s = &pit->channels[0];
+    if (!s->irq_disabled) {
+        qemu_mod_timer(s->irq_timer, s->next_transition_time);
     }
 }
 
@@ -482,7 +278,7 @@ static void pit_reset(DeviceState *dev)
  * reenable it when legacy mode is left again. */
 static void pit_irq_control(void *opaque, int n, int enable)
 {
-    PITState *pit = opaque;
+    PITCommonState *pit = opaque;
     PITChannelState *s = &pit->channels[0];
 
     if (enable) {
@@ -504,46 +300,55 @@ static const MemoryRegionOps pit_ioport_ops = {
     .old_portio = pit_portio
 };
 
-static int pit_initfn(ISADevice *dev)
+static void pit_post_load(PITCommonState *s)
+{
+    PITChannelState *sc = &s->channels[0];
+
+    if (sc->next_transition_time != -1) {
+        qemu_mod_timer(sc->irq_timer, sc->next_transition_time);
+    } else {
+        qemu_del_timer(sc->irq_timer);
+    }
+}
+
+static int pit_initfn(PITCommonState *pit)
 {
-    PITState *pit = DO_UPCAST(PITState, dev, dev);
     PITChannelState *s;
 
     s = &pit->channels[0];
     /* the timer 0 is connected to an IRQ */
     s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
-    qdev_init_gpio_out(&dev->qdev, &s->irq, 1);
+    qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1);
 
     memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
-    isa_register_ioport(dev, &pit->ioports, pit->iobase);
 
-    qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1);
-
-    qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
+    qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1);
 
     return 0;
 }
 
 static Property pit_properties[] = {
-    DEFINE_PROP_HEX32("iobase", PITState, iobase,  -1),
+    DEFINE_PROP_HEX32("iobase", PITCommonState, iobase,  -1),
     DEFINE_PROP_END_OF_LIST(),
 };
 
 static void pit_class_initfn(ObjectClass *klass, void *data)
 {
+    PITCommonClass *k = PIT_COMMON_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = pit_initfn;
-    dc->no_user = 1;
+
+    k->init = pit_initfn;
+    k->set_channel_gate = pit_set_channel_gate;
+    k->get_channel_info = pit_get_channel_info_common;
+    k->post_load = pit_post_load;
     dc->reset = pit_reset;
-    dc->vmsd = &vmstate_pit;
     dc->props = pit_properties;
 }
 
 static TypeInfo pit_info = {
     .name          = "isa-pit",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(PITState),
+    .parent        = TYPE_PIT_COMMON,
+    .instance_size = sizeof(PITCommonState),
     .class_init    = pit_class_initfn,
 };
 
diff --git a/hw/i8254.h b/hw/i8254.h
index a1d2e9835b..ba6b598a99 100644
--- a/hw/i8254.h
+++ b/hw/i8254.h
@@ -51,6 +51,17 @@ static inline ISADevice *pit_init(ISABus *bus, int base, int isa_irq,
     return dev;
 }
 
+static inline ISADevice *kvm_pit_init(ISABus *bus, int base)
+{
+    ISADevice *dev;
+
+    dev = isa_create(bus, "kvm-pit");
+    qdev_prop_set_uint32(&dev->qdev, "iobase", base);
+    qdev_init_nofail(&dev->qdev);
+
+    return dev;
+}
+
 void pit_set_gate(ISADevice *dev, int channel, int val);
 void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info);
 
diff --git a/hw/i8254_common.c b/hw/i8254_common.c
new file mode 100644
index 0000000000..a03d7cd458
--- /dev/null
+++ b/hw/i8254_common.c
@@ -0,0 +1,311 @@
+/*
+ * QEMU 8253/8254 - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2012      Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qemu-timer.h"
+#include "i8254.h"
+#include "i8254_internal.h"
+
+/* val must be 0 or 1 */
+void pit_set_gate(ISADevice *dev, int channel, int val)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITChannelState *s = &pit->channels[channel];
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+
+    c->set_channel_gate(pit, s, val);
+}
+
+/* get pit output bit */
+int pit_get_out(PITChannelState *s, int64_t current_time)
+{
+    uint64_t d;
+    int out;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+                 get_ticks_per_sec());
+    switch (s->mode) {
+    default:
+    case 0:
+        out = (d >= s->count);
+        break;
+    case 1:
+        out = (d < s->count);
+        break;
+    case 2:
+        if ((d % s->count) == 0 && d != 0) {
+            out = 1;
+        } else {
+            out = 0;
+        }
+        break;
+    case 3:
+        out = (d % s->count) < ((s->count + 1) >> 1);
+        break;
+    case 4:
+    case 5:
+        out = (d == s->count);
+        break;
+    }
+    return out;
+}
+
+/* return -1 if no transition will occur.  */
+int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
+{
+    uint64_t d, next_time, base;
+    int period2;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+                 get_ticks_per_sec());
+    switch (s->mode) {
+    default:
+    case 0:
+    case 1:
+        if (d < s->count) {
+            next_time = s->count;
+        } else {
+            return -1;
+        }
+        break;
+    case 2:
+        base = (d / s->count) * s->count;
+        if ((d - base) == 0 && d != 0) {
+            next_time = base + s->count;
+        } else {
+            next_time = base + s->count + 1;
+        }
+        break;
+    case 3:
+        base = (d / s->count) * s->count;
+        period2 = ((s->count + 1) >> 1);
+        if ((d - base) < period2) {
+            next_time = base + period2;
+        } else {
+            next_time = base + s->count;
+        }
+        break;
+    case 4:
+    case 5:
+        if (d < s->count) {
+            next_time = s->count;
+        } else if (d == s->count) {
+            next_time = s->count + 1;
+        } else {
+            return -1;
+        }
+        break;
+    }
+    /* convert to timer units */
+    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
+                                              PIT_FREQ);
+    /* fix potential rounding problems */
+    /* XXX: better solution: use a clock at PIT_FREQ Hz */
+    if (next_time <= current_time) {
+        next_time = current_time + 1;
+    }
+    return next_time;
+}
+
+void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc,
+                                 PITChannelInfo *info)
+{
+    info->gate = sc->gate;
+    info->mode = sc->mode;
+    info->initial_count = sc->count;
+    info->out = pit_get_out(sc, qemu_get_clock_ns(vm_clock));
+}
+
+void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITChannelState *s = &pit->channels[channel];
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+
+    c->get_channel_info(pit, s, info);
+}
+
+void pit_reset_common(PITCommonState *pit)
+{
+    PITChannelState *s;
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        s->mode = 3;
+        s->gate = (i != 2);
+        s->count_load_time = qemu_get_clock_ns(vm_clock);
+        s->count = 0x10000;
+        if (i == 0 && !s->irq_disabled) {
+            s->next_transition_time =
+                pit_get_next_transition_time(s, s->count_load_time);
+        }
+    }
+}
+
+static int pit_init_common(ISADevice *dev)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+    int ret;
+
+    ret = c->init(pit);
+    if (ret < 0) {
+        return ret;
+    }
+
+    isa_register_ioport(dev, &pit->ioports, pit->iobase);
+
+    qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_pit_channel = {
+    .name = "pit channel",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(count, PITChannelState),
+        VMSTATE_UINT16(latched_count, PITChannelState),
+        VMSTATE_UINT8(count_latched, PITChannelState),
+        VMSTATE_UINT8(status_latched, PITChannelState),
+        VMSTATE_UINT8(status, PITChannelState),
+        VMSTATE_UINT8(read_state, PITChannelState),
+        VMSTATE_UINT8(write_state, PITChannelState),
+        VMSTATE_UINT8(write_latch, PITChannelState),
+        VMSTATE_UINT8(rw_mode, PITChannelState),
+        VMSTATE_UINT8(mode, PITChannelState),
+        VMSTATE_UINT8(bcd, PITChannelState),
+        VMSTATE_UINT8(gate, PITChannelState),
+        VMSTATE_INT64(count_load_time, PITChannelState),
+        VMSTATE_INT64(next_transition_time, PITChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+    PITCommonState *pit = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+    PITChannelState *s;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        s->count = qemu_get_be32(f);
+        qemu_get_be16s(f, &s->latched_count);
+        qemu_get_8s(f, &s->count_latched);
+        qemu_get_8s(f, &s->status_latched);
+        qemu_get_8s(f, &s->status);
+        qemu_get_8s(f, &s->read_state);
+        qemu_get_8s(f, &s->write_state);
+        qemu_get_8s(f, &s->write_latch);
+        qemu_get_8s(f, &s->rw_mode);
+        qemu_get_8s(f, &s->mode);
+        qemu_get_8s(f, &s->bcd);
+        qemu_get_8s(f, &s->gate);
+        s->count_load_time = qemu_get_be64(f);
+        s->irq_disabled = 0;
+        if (i == 0) {
+            s->next_transition_time = qemu_get_be64(f);
+        }
+    }
+    if (c->post_load) {
+        c->post_load(pit);
+    }
+    return 0;
+}
+
+static void pit_dispatch_pre_save(void *opaque)
+{
+    PITCommonState *s = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
+
+    if (c->pre_save) {
+        c->pre_save(s);
+    }
+}
+
+static int pit_dispatch_post_load(void *opaque, int version_id)
+{
+    PITCommonState *s = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
+
+    if (c->post_load) {
+        c->post_load(s);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_pit_common = {
+    .name = "i8254",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 1,
+    .load_state_old = pit_load_old,
+    .pre_save = pit_dispatch_pre_save,
+    .post_load = pit_dispatch_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
+        VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
+                             vmstate_pit_channel, PITChannelState),
+        VMSTATE_INT64(channels[0].next_transition_time,
+                      PITCommonState), /* formerly irq_timer */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pit_common_class_init(ObjectClass *klass, void *data)
+{
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    ic->init = pit_init_common;
+    dc->vmsd = &vmstate_pit_common;
+    dc->no_user = 1;
+}
+
+static TypeInfo pit_common_type = {
+    .name          = TYPE_PIT_COMMON,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(PITCommonState),
+    .class_size    = sizeof(PITCommonClass),
+    .class_init    = pit_common_class_init,
+    .abstract      = true,
+};
+
+static void register_devices(void)
+{
+    type_register_static(&pit_common_type);
+}
+
+type_init(register_devices);
diff --git a/hw/i8254_internal.h b/hw/i8254_internal.h
new file mode 100644
index 0000000000..686f0c2ba9
--- /dev/null
+++ b/hw/i8254_internal.h
@@ -0,0 +1,85 @@
+/*
+ * QEMU 8253/8254 - internal interfaces
+ *
+ * Copyright (c) 2011 Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_I8254_INTERNAL_H
+#define QEMU_I8254_INTERNAL_H
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+
+typedef struct PITChannelState {
+    int count; /* can be 65536 */
+    uint16_t latched_count;
+    uint8_t count_latched;
+    uint8_t status_latched;
+    uint8_t status;
+    uint8_t read_state;
+    uint8_t write_state;
+    uint8_t write_latch;
+    uint8_t rw_mode;
+    uint8_t mode;
+    uint8_t bcd; /* not supported */
+    uint8_t gate; /* timer start */
+    int64_t count_load_time;
+    /* irq handling */
+    int64_t next_transition_time;
+    QEMUTimer *irq_timer;
+    qemu_irq irq;
+    uint32_t irq_disabled;
+} PITChannelState;
+
+typedef struct PITCommonState {
+    ISADevice dev;
+    MemoryRegion ioports;
+    uint32_t iobase;
+    PITChannelState channels[3];
+} PITCommonState;
+
+#define TYPE_PIT_COMMON "pit-common"
+#define PIT_COMMON(obj) \
+     OBJECT_CHECK(PITCommonState, (obj), TYPE_PIT_COMMON)
+#define PIT_COMMON_CLASS(klass) \
+     OBJECT_CLASS_CHECK(PITCommonClass, (klass), TYPE_PIT_COMMON)
+#define PIT_COMMON_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PITCommonClass, (obj), TYPE_PIT_COMMON)
+
+typedef struct PITCommonClass {
+    ISADeviceClass parent_class;
+
+    int (*init)(PITCommonState *s);
+    void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val);
+    void (*get_channel_info)(PITCommonState *s, PITChannelState *sc,
+                             PITChannelInfo *info);
+    void (*pre_save)(PITCommonState *s);
+    void (*post_load)(PITCommonState *s);
+} PITCommonClass;
+
+int pit_get_out(PITChannelState *s, int64_t current_time);
+int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time);
+void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc,
+                                 PITChannelInfo *info);
+void pit_reset_common(PITCommonState *s);
+
+#endif /* !QEMU_I8254_INTERNAL_H */
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 041ce1e89f..a883a920be 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -367,7 +367,7 @@ static void ahci_mem_write(void *opaque, target_phys_addr_t addr,
 
 }
 
-static MemoryRegionOps ahci_mem_ops = {
+static const MemoryRegionOps ahci_mem_ops = {
     .read = ahci_mem_read,
     .write = ahci_mem_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
@@ -403,7 +403,7 @@ static void ahci_idp_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static MemoryRegionOps ahci_idp_ops = {
+static const MemoryRegionOps ahci_idp_ops = {
     .read = ahci_idp_read,
     .write = ahci_idp_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index 743ec02406..bf8ece4708 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -65,7 +65,7 @@ static void cmd646_cmd_write(void *opaque, target_phys_addr_t addr,
     ide_cmd_write(cmd646bar->bus, addr + 2, data);
 }
 
-static MemoryRegionOps cmd646_cmd_ops = {
+static const MemoryRegionOps cmd646_cmd_ops = {
     .read = cmd646_cmd_read,
     .write = cmd646_cmd_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
@@ -104,7 +104,7 @@ static void cmd646_data_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static MemoryRegionOps cmd646_data_ops = {
+static const MemoryRegionOps cmd646_data_ops = {
     .read = cmd646_data_read,
     .write = cmd646_data_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
@@ -193,7 +193,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static MemoryRegionOps cmd646_bmdma_ops = {
+static const MemoryRegionOps cmd646_bmdma_ops = {
     .read = bmdma_read,
     .write = bmdma_write,
 };
diff --git a/hw/ide/core.c b/hw/ide/core.c
index ce570a7ce5..4d568acc9c 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1068,6 +1068,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
             ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */
             goto abort_cmd;
         }
+        if (!s->bs) {
+            goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         s->req_nb_sectors = 1;
         ide_sector_read(s);
@@ -1078,6 +1081,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
     case WIN_WRITE_ONCE:
     case CFA_WRITE_SECT_WO_ERASE:
     case WIN_WRITE_VERIFY:
+        if (!s->bs) {
+            goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         s->error = 0;
         s->status = SEEK_STAT | READY_STAT;
@@ -1088,8 +1094,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
     case WIN_MULTREAD_EXT:
 	lba48 = 1;
     case WIN_MULTREAD:
-        if (!s->mult_sectors)
+        if (!s->bs) {
             goto abort_cmd;
+        }
+        if (!s->mult_sectors) {
+            goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         s->req_nb_sectors = s->mult_sectors;
         ide_sector_read(s);
@@ -1098,8 +1108,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
 	lba48 = 1;
     case WIN_MULTWRITE:
     case CFA_WRITE_MULTI_WO_ERASE:
-        if (!s->mult_sectors)
+        if (!s->bs) {
             goto abort_cmd;
+        }
+        if (!s->mult_sectors) {
+            goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         s->error = 0;
         s->status = SEEK_STAT | READY_STAT;
@@ -1114,8 +1128,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
 	lba48 = 1;
     case WIN_READDMA:
     case WIN_READDMA_ONCE:
-        if (!s->bs)
+        if (!s->bs) {
             goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         ide_sector_start_dma(s, IDE_DMA_READ);
         break;
@@ -1123,8 +1138,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
 	lba48 = 1;
     case WIN_WRITEDMA:
     case WIN_WRITEDMA_ONCE:
-        if (!s->bs)
+        if (!s->bs) {
             goto abort_cmd;
+        }
 	ide_cmd_lba48_transform(s, lba48);
         ide_sector_start_dma(s, IDE_DMA_WRITE);
         s->media_changed = 1;
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index abbc41b59e..a4df24406a 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -291,7 +291,7 @@ static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
     return retval;
 }
 
-static MemoryRegionOps pmac_ide_ops = {
+static const MemoryRegionOps pmac_ide_ops = {
     .old_mmio = {
         .write = {
             pmac_ide_writeb,
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 1030fcc31c..bcaa400e2d 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -79,7 +79,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static MemoryRegionOps piix_bmdma_ops = {
+static const MemoryRegionOps piix_bmdma_ops = {
     .read = bmdma_read,
     .write = bmdma_write,
 };
diff --git a/hw/ide/via.c b/hw/ide/via.c
index 2886bc6dfb..eec5136019 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -82,7 +82,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static MemoryRegionOps via_bmdma_ops = {
+static const MemoryRegionOps via_bmdma_ops = {
     .read = bmdma_read,
     .write = bmdma_write,
 };
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 5b06c81c9b..9bdb9e62d6 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -443,7 +443,7 @@ static void integratorcp_init(ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUARMState *env;
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
diff --git a/hw/ioapic.c b/hw/ioapic.c
index 3fee0114d9..e2e4796bb5 100644
--- a/hw/ioapic.c
+++ b/hw/ioapic.c
@@ -195,7 +195,7 @@ ioapic_mem_write(void *opaque, target_phys_addr_t addr, uint64_t val,
         if (size != 4) {
             break;
         }
-        DPRINTF("write: %08x = %08x\n", s->ioregsel, val);
+        DPRINTF("write: %08x = %08" PRIx64 "\n", s->ioregsel, val);
         switch (s->ioregsel) {
         case IOAPIC_REG_ID:
             s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK;
diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c
index 5bb0a4b9fd..ffe7a521b7 100644
--- a/hw/kvm/apic.c
+++ b/hw/kvm/apic.c
@@ -92,10 +92,39 @@ static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val)
     s->tpr = (val & 0x0f) << 4;
 }
 
+static uint8_t kvm_apic_get_tpr(APICCommonState *s)
+{
+    return s->tpr >> 4;
+}
+
+static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable)
+{
+    struct kvm_tpr_access_ctl ctl = {
+        .enabled = enable
+    };
+
+    kvm_vcpu_ioctl(s->cpu_env, KVM_TPR_ACCESS_REPORTING, &ctl);
+}
+
+static void kvm_apic_vapic_base_update(APICCommonState *s)
+{
+    struct kvm_vapic_addr vapid_addr = {
+        .vapic_addr = s->vapic_paddr,
+    };
+    int ret;
+
+    ret = kvm_vcpu_ioctl(s->cpu_env, KVM_SET_VAPIC_ADDR, &vapid_addr);
+    if (ret < 0) {
+        fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n",
+                strerror(-ret));
+        abort();
+    }
+}
+
 static void do_inject_external_nmi(void *data)
 {
     APICCommonState *s = data;
-    CPUState *env = s->cpu_env;
+    CPUX86State *env = s->cpu_env;
     uint32_t lvt;
     int ret;
 
@@ -129,6 +158,9 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
     k->init = kvm_apic_init;
     k->set_base = kvm_apic_set_base;
     k->set_tpr = kvm_apic_set_tpr;
+    k->get_tpr = kvm_apic_get_tpr;
+    k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
+    k->vapic_base_update = kvm_apic_vapic_base_update;
     k->external_nmi = kvm_apic_external_nmi;
 }
 
diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c
index 2157340326..446bd62176 100644
--- a/hw/kvm/clock.c
+++ b/hw/kvm/clock.c
@@ -121,9 +121,7 @@ void kvmclock_create(void)
 
 static void kvmclock_register_types(void)
 {
-    if (kvm_enabled()) {
     type_register_static(&kvmclock_info);
-    }
 }
 
 type_init(kvmclock_register_types)
diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c
new file mode 100644
index 0000000000..bb5fe07d1e
--- /dev/null
+++ b/hw/kvm/i8254.c
@@ -0,0 +1,254 @@
+/*
+ * KVM in-kernel PIT (i8254) support
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2012      Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-timer.h"
+#include "hw/i8254.h"
+#include "hw/i8254_internal.h"
+#include "kvm.h"
+
+#define KVM_PIT_REINJECT_BIT 0
+
+typedef struct KVMPITState {
+    PITCommonState pit;
+    LostTickPolicy lost_tick_policy;
+} KVMPITState;
+
+static void kvm_pit_get(PITCommonState *s)
+{
+    struct kvm_pit_state2 kpit;
+    struct kvm_pit_channel_state *kchan;
+    struct PITChannelState *sc;
+    int i, ret;
+
+    if (kvm_has_pit_state2()) {
+        ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit);
+        if (ret < 0) {
+            fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret));
+            abort();
+        }
+        s->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
+    } else {
+        /*
+         * kvm_pit_state2 is superset of kvm_pit_state struct,
+         * so we can use it for KVM_GET_PIT as well.
+         */
+        ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit);
+        if (ret < 0) {
+            fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret));
+            abort();
+        }
+    }
+    for (i = 0; i < 3; i++) {
+        kchan = &kpit.channels[i];
+        sc = &s->channels[i];
+        sc->count = kchan->count;
+        sc->latched_count = kchan->latched_count;
+        sc->count_latched = kchan->count_latched;
+        sc->status_latched = kchan->status_latched;
+        sc->status = kchan->status;
+        sc->read_state = kchan->read_state;
+        sc->write_state = kchan->write_state;
+        sc->write_latch = kchan->write_latch;
+        sc->rw_mode = kchan->rw_mode;
+        sc->mode = kchan->mode;
+        sc->bcd = kchan->bcd;
+        sc->gate = kchan->gate;
+        sc->count_load_time = kchan->count_load_time;
+    }
+
+    sc = &s->channels[0];
+    sc->next_transition_time =
+        pit_get_next_transition_time(sc, sc->count_load_time);
+}
+
+static void kvm_pit_put(PITCommonState *s)
+{
+    struct kvm_pit_state2 kpit;
+    struct kvm_pit_channel_state *kchan;
+    struct PITChannelState *sc;
+    int i, ret;
+
+    kpit.flags = s->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0;
+    for (i = 0; i < 3; i++) {
+        kchan = &kpit.channels[i];
+        sc = &s->channels[i];
+        kchan->count = sc->count;
+        kchan->latched_count = sc->latched_count;
+        kchan->count_latched = sc->count_latched;
+        kchan->status_latched = sc->status_latched;
+        kchan->status = sc->status;
+        kchan->read_state = sc->read_state;
+        kchan->write_state = sc->write_state;
+        kchan->write_latch = sc->write_latch;
+        kchan->rw_mode = sc->rw_mode;
+        kchan->mode = sc->mode;
+        kchan->bcd = sc->bcd;
+        kchan->gate = sc->gate;
+        kchan->count_load_time = sc->count_load_time;
+    }
+
+    ret = kvm_vm_ioctl(kvm_state,
+                       kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT,
+                       &kpit);
+    if (ret < 0) {
+        fprintf(stderr, "%s failed: %s\n",
+                kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT",
+                strerror(ret));
+        abort();
+    }
+}
+
+static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val)
+{
+    kvm_pit_get(s);
+
+    switch (sc->mode) {
+    default:
+    case 0:
+    case 4:
+        /* XXX: just disable/enable counting */
+        break;
+    case 1:
+    case 2:
+    case 3:
+    case 5:
+        if (sc->gate < val) {
+            /* restart counting on rising edge */
+            sc->count_load_time = qemu_get_clock_ns(vm_clock);
+        }
+        break;
+    }
+    sc->gate = val;
+
+    kvm_pit_put(s);
+}
+
+static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc,
+                                     PITChannelInfo *info)
+{
+    kvm_pit_get(s);
+
+    pit_get_channel_info_common(s, sc, info);
+}
+
+static void kvm_pit_reset(DeviceState *dev)
+{
+    PITCommonState *s = DO_UPCAST(PITCommonState, dev.qdev, dev);
+
+    pit_reset_common(s);
+
+    kvm_pit_put(s);
+}
+
+static void kvm_pit_irq_control(void *opaque, int n, int enable)
+{
+    PITCommonState *pit = opaque;
+    PITChannelState *s = &pit->channels[0];
+
+    kvm_pit_get(pit);
+
+    s->irq_disabled = !enable;
+
+    kvm_pit_put(pit);
+}
+
+static int kvm_pit_initfn(PITCommonState *pit)
+{
+    KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit);
+    struct kvm_pit_config config = {
+        .flags = 0,
+    };
+    int ret;
+
+    if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) {
+        ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config);
+    } else {
+        ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT);
+    }
+    if (ret < 0) {
+        fprintf(stderr, "Create kernel PIC irqchip failed: %s\n",
+                strerror(ret));
+        return ret;
+    }
+    switch (s->lost_tick_policy) {
+    case LOST_TICK_DELAY:
+        break; /* enabled by default */
+    case LOST_TICK_DISCARD:
+        if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) {
+            struct kvm_reinject_control control = { .pit_reinject = 0 };
+
+            ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control);
+            if (ret < 0) {
+                fprintf(stderr,
+                        "Can't disable in-kernel PIT reinjection: %s\n",
+                        strerror(ret));
+                return ret;
+            }
+        }
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    memory_region_init_reservation(&pit->ioports, "kvm-pit", 4);
+
+    qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1);
+
+    return 0;
+}
+
+static Property kvm_pit_properties[] = {
+    DEFINE_PROP_HEX32("iobase", KVMPITState, pit.iobase,  -1),
+    DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState,
+                               lost_tick_policy, LOST_TICK_DELAY),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void kvm_pit_class_init(ObjectClass *klass, void *data)
+{
+    PITCommonClass *k = PIT_COMMON_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = kvm_pit_initfn;
+    k->set_channel_gate = kvm_pit_set_gate;
+    k->get_channel_info = kvm_pit_get_channel_info;
+    k->pre_save = kvm_pit_get;
+    k->post_load = kvm_pit_put;
+    dc->reset = kvm_pit_reset;
+    dc->props = kvm_pit_properties;
+}
+
+static TypeInfo kvm_pit_info = {
+    .name          = "kvm-pit",
+    .parent        = TYPE_PIT_COMMON,
+    .instance_size = sizeof(KVMPITState),
+    .class_init = kvm_pit_class_init,
+};
+
+static void kvm_pit_register(void)
+{
+    type_register_static(&kvm_pit_info);
+}
+
+type_init(kvm_pit_register)
diff --git a/hw/kvmvapic.c b/hw/kvmvapic.c
new file mode 100644
index 0000000000..5d83625f4a
--- /dev/null
+++ b/hw/kvmvapic.c
@@ -0,0 +1,807 @@
+/*
+ * TPR optimization for 32-bit Windows guests (XP and Server 2003)
+ *
+ * Copyright (C) 2007-2008 Qumranet Technologies
+ * Copyright (C) 2012      Jan Kiszka, Siemens AG
+ *
+ * This work is licensed under the terms of the GNU GPL version 2, or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+#include "sysemu.h"
+#include "cpus.h"
+#include "kvm.h"
+#include "apic_internal.h"
+
+#define APIC_DEFAULT_ADDRESS    0xfee00000
+
+#define VAPIC_IO_PORT           0x7e
+
+#define VAPIC_CPU_SHIFT         7
+
+#define ROM_BLOCK_SIZE          512
+#define ROM_BLOCK_MASK          (~(ROM_BLOCK_SIZE - 1))
+
+typedef enum VAPICMode {
+    VAPIC_INACTIVE = 0,
+    VAPIC_ACTIVE   = 1,
+    VAPIC_STANDBY  = 2,
+} VAPICMode;
+
+typedef struct VAPICHandlers {
+    uint32_t set_tpr;
+    uint32_t set_tpr_eax;
+    uint32_t get_tpr[8];
+    uint32_t get_tpr_stack;
+} QEMU_PACKED VAPICHandlers;
+
+typedef struct GuestROMState {
+    char signature[8];
+    uint32_t vaddr;
+    uint32_t fixup_start;
+    uint32_t fixup_end;
+    uint32_t vapic_vaddr;
+    uint32_t vapic_size;
+    uint32_t vcpu_shift;
+    uint32_t real_tpr_addr;
+    VAPICHandlers up;
+    VAPICHandlers mp;
+} QEMU_PACKED GuestROMState;
+
+typedef struct VAPICROMState {
+    SysBusDevice busdev;
+    MemoryRegion io;
+    MemoryRegion rom;
+    uint32_t state;
+    uint32_t rom_state_paddr;
+    uint32_t rom_state_vaddr;
+    uint32_t vapic_paddr;
+    uint32_t real_tpr_addr;
+    GuestROMState rom_state;
+    size_t rom_size;
+    bool rom_mapped_writable;
+} VAPICROMState;
+
+#define TPR_INSTR_ABS_MODRM             0x1
+#define TPR_INSTR_MATCH_MODRM_REG       0x2
+
+typedef struct TPRInstruction {
+    uint8_t opcode;
+    uint8_t modrm_reg;
+    unsigned int flags;
+    TPRAccess access;
+    size_t length;
+    off_t addr_offset;
+} TPRInstruction;
+
+/* must be sorted by length, shortest first */
+static const TPRInstruction tpr_instr[] = {
+    { /* mov abs to eax */
+        .opcode = 0xa1,
+        .access = TPR_ACCESS_READ,
+        .length = 5,
+        .addr_offset = 1,
+    },
+    { /* mov eax to abs */
+        .opcode = 0xa3,
+        .access = TPR_ACCESS_WRITE,
+        .length = 5,
+        .addr_offset = 1,
+    },
+    { /* mov r32 to r/m32 */
+        .opcode = 0x89,
+        .flags = TPR_INSTR_ABS_MODRM,
+        .access = TPR_ACCESS_WRITE,
+        .length = 6,
+        .addr_offset = 2,
+    },
+    { /* mov r/m32 to r32 */
+        .opcode = 0x8b,
+        .flags = TPR_INSTR_ABS_MODRM,
+        .access = TPR_ACCESS_READ,
+        .length = 6,
+        .addr_offset = 2,
+    },
+    { /* push r/m32 */
+        .opcode = 0xff,
+        .modrm_reg = 6,
+        .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
+        .access = TPR_ACCESS_READ,
+        .length = 6,
+        .addr_offset = 2,
+    },
+    { /* mov imm32, r/m32 (c7/0) */
+        .opcode = 0xc7,
+        .modrm_reg = 0,
+        .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
+        .access = TPR_ACCESS_WRITE,
+        .length = 10,
+        .addr_offset = 2,
+    },
+};
+
+static void read_guest_rom_state(VAPICROMState *s)
+{
+    cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
+                           sizeof(GuestROMState), 0);
+}
+
+static void write_guest_rom_state(VAPICROMState *s)
+{
+    cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
+                           sizeof(GuestROMState), 1);
+}
+
+static void update_guest_rom_state(VAPICROMState *s)
+{
+    read_guest_rom_state(s);
+
+    s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr);
+    s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT);
+
+    write_guest_rom_state(s);
+}
+
+static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env)
+{
+    target_phys_addr_t paddr;
+    target_ulong addr;
+
+    if (s->state == VAPIC_ACTIVE) {
+        return 0;
+    }
+    /*
+     * If there is no prior TPR access instruction we could analyze (which is
+     * the case after resume from hibernation), we need to scan the possible
+     * virtual address space for the APIC mapping.
+     */
+    for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) {
+        paddr = cpu_get_phys_page_debug(env, addr);
+        if (paddr != APIC_DEFAULT_ADDRESS) {
+            continue;
+        }
+        s->real_tpr_addr = addr + 0x80;
+        update_guest_rom_state(s);
+        return 0;
+    }
+    return -1;
+}
+
+static uint8_t modrm_reg(uint8_t modrm)
+{
+    return (modrm >> 3) & 7;
+}
+
+static bool is_abs_modrm(uint8_t modrm)
+{
+    return (modrm & 0xc7) == 0x05;
+}
+
+static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr)
+{
+    return opcode[0] == instr->opcode &&
+        (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) &&
+        (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) ||
+         modrm_reg(opcode[1]) == instr->modrm_reg);
+}
+
+static int evaluate_tpr_instruction(VAPICROMState *s, CPUX86State *env,
+                                    target_ulong *pip, TPRAccess access)
+{
+    const TPRInstruction *instr;
+    target_ulong ip = *pip;
+    uint8_t opcode[2];
+    uint32_t real_tpr_addr;
+    int i;
+
+    if ((ip & 0xf0000000ULL) != 0x80000000ULL &&
+        (ip & 0xf0000000ULL) != 0xe0000000ULL) {
+        return -1;
+    }
+
+    /*
+     * Early Windows 2003 SMP initialization contains a
+     *
+     *   mov imm32, r/m32
+     *
+     * instruction that is patched by TPR optimization. The problem is that
+     * RSP, used by the patched instruction, is zero, so the guest gets a
+     * double fault and dies.
+     */
+    if (env->regs[R_ESP] == 0) {
+        return -1;
+    }
+
+    if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
+        /*
+         * KVM without kernel-based TPR access reporting will pass an IP that
+         * points after the accessing instruction. So we need to look backward
+         * to find the reason.
+         */
+        for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
+            instr = &tpr_instr[i];
+            if (instr->access != access) {
+                continue;
+            }
+            if (cpu_memory_rw_debug(env, ip - instr->length, opcode,
+                                    sizeof(opcode), 0) < 0) {
+                return -1;
+            }
+            if (opcode_matches(opcode, instr)) {
+                ip -= instr->length;
+                goto instruction_ok;
+            }
+        }
+        return -1;
+    } else {
+        if (cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0) < 0) {
+            return -1;
+        }
+        for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
+            instr = &tpr_instr[i];
+            if (opcode_matches(opcode, instr)) {
+                goto instruction_ok;
+            }
+        }
+        return -1;
+    }
+
+instruction_ok:
+    /*
+     * Grab the virtual TPR address from the instruction
+     * and update the cached values.
+     */
+    if (cpu_memory_rw_debug(env, ip + instr->addr_offset,
+                            (void *)&real_tpr_addr,
+                            sizeof(real_tpr_addr), 0) < 0) {
+        return -1;
+    }
+    real_tpr_addr = le32_to_cpu(real_tpr_addr);
+    if ((real_tpr_addr & 0xfff) != 0x80) {
+        return -1;
+    }
+    s->real_tpr_addr = real_tpr_addr;
+    update_guest_rom_state(s);
+
+    *pip = ip;
+    return 0;
+}
+
+static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip)
+{
+    target_phys_addr_t paddr;
+    uint32_t rom_state_vaddr;
+    uint32_t pos, patch, offset;
+
+    /* nothing to do if already activated */
+    if (s->state == VAPIC_ACTIVE) {
+        return 0;
+    }
+
+    /* bail out if ROM init code was not executed (missing ROM?) */
+    if (s->state == VAPIC_INACTIVE) {
+        return -1;
+    }
+
+    /* find out virtual address of the ROM */
+    rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000);
+    paddr = cpu_get_phys_page_debug(env, rom_state_vaddr);
+    if (paddr == -1) {
+        return -1;
+    }
+    paddr += rom_state_vaddr & ~TARGET_PAGE_MASK;
+    if (paddr != s->rom_state_paddr) {
+        return -1;
+    }
+    read_guest_rom_state(s);
+    if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) {
+        return -1;
+    }
+    s->rom_state_vaddr = rom_state_vaddr;
+
+    /* fixup addresses in ROM if needed */
+    if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) {
+        return 0;
+    }
+    for (pos = le32_to_cpu(s->rom_state.fixup_start);
+         pos < le32_to_cpu(s->rom_state.fixup_end);
+         pos += 4) {
+        cpu_physical_memory_rw(paddr + pos - s->rom_state.vaddr,
+                               (void *)&offset, sizeof(offset), 0);
+        offset = le32_to_cpu(offset);
+        cpu_physical_memory_rw(paddr + offset, (void *)&patch,
+                               sizeof(patch), 0);
+        patch = le32_to_cpu(patch);
+        patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr);
+        patch = cpu_to_le32(patch);
+        cpu_physical_memory_rw(paddr + offset, (void *)&patch,
+                               sizeof(patch), 1);
+    }
+    read_guest_rom_state(s);
+    s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) -
+        le32_to_cpu(s->rom_state.vaddr);
+
+    return 0;
+}
+
+/*
+ * Tries to read the unique processor number from the Kernel Processor Control
+ * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR
+ * cannot be accessed or is considered invalid. This also ensures that we are
+ * not patching the wrong guest.
+ */
+static int get_kpcr_number(CPUX86State *env)
+{
+    struct kpcr {
+        uint8_t  fill1[0x1c];
+        uint32_t self;
+        uint8_t  fill2[0x31];
+        uint8_t  number;
+    } QEMU_PACKED kpcr;
+
+    if (cpu_memory_rw_debug(env, env->segs[R_FS].base,
+                            (void *)&kpcr, sizeof(kpcr), 0) < 0 ||
+        kpcr.self != env->segs[R_FS].base) {
+        return -1;
+    }
+    return kpcr.number;
+}
+
+static int vapic_enable(VAPICROMState *s, CPUX86State *env)
+{
+    int cpu_number = get_kpcr_number(env);
+    target_phys_addr_t vapic_paddr;
+    static const uint8_t enabled = 1;
+
+    if (cpu_number < 0) {
+        return -1;
+    }
+    vapic_paddr = s->vapic_paddr +
+        (((target_phys_addr_t)cpu_number) << VAPIC_CPU_SHIFT);
+    cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled),
+                           (void *)&enabled, sizeof(enabled), 1);
+    apic_enable_vapic(env->apic_state, vapic_paddr);
+
+    s->state = VAPIC_ACTIVE;
+
+    return 0;
+}
+
+static void patch_byte(CPUX86State *env, target_ulong addr, uint8_t byte)
+{
+    cpu_memory_rw_debug(env, addr, &byte, 1, 1);
+}
+
+static void patch_call(VAPICROMState *s, CPUX86State *env, target_ulong ip,
+                       uint32_t target)
+{
+    uint32_t offset;
+
+    offset = cpu_to_le32(target - ip - 5);
+    patch_byte(env, ip, 0xe8); /* call near */
+    cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1);
+}
+
+static void patch_instruction(VAPICROMState *s, CPUX86State *env, target_ulong ip)
+{
+    target_phys_addr_t paddr;
+    VAPICHandlers *handlers;
+    uint8_t opcode[2];
+    uint32_t imm32;
+
+    if (smp_cpus == 1) {
+        handlers = &s->rom_state.up;
+    } else {
+        handlers = &s->rom_state.mp;
+    }
+
+    pause_all_vcpus();
+
+    cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0);
+
+    switch (opcode[0]) {
+    case 0x89: /* mov r32 to r/m32 */
+        patch_byte(env, ip, 0x50 + modrm_reg(opcode[1]));  /* push reg */
+        patch_call(s, env, ip + 1, handlers->set_tpr);
+        break;
+    case 0x8b: /* mov r/m32 to r32 */
+        patch_byte(env, ip, 0x90);
+        patch_call(s, env, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]);
+        break;
+    case 0xa1: /* mov abs to eax */
+        patch_call(s, env, ip, handlers->get_tpr[0]);
+        break;
+    case 0xa3: /* mov eax to abs */
+        patch_call(s, env, ip, handlers->set_tpr_eax);
+        break;
+    case 0xc7: /* mov imm32, r/m32 (c7/0) */
+        patch_byte(env, ip, 0x68);  /* push imm32 */
+        cpu_memory_rw_debug(env, ip + 6, (void *)&imm32, sizeof(imm32), 0);
+        cpu_memory_rw_debug(env, ip + 1, (void *)&imm32, sizeof(imm32), 1);
+        patch_call(s, env, ip + 5, handlers->set_tpr);
+        break;
+    case 0xff: /* push r/m32 */
+        patch_byte(env, ip, 0x50); /* push eax */
+        patch_call(s, env, ip + 1, handlers->get_tpr_stack);
+        break;
+    default:
+        abort();
+    }
+
+    resume_all_vcpus();
+
+    paddr = cpu_get_phys_page_debug(env, ip);
+    paddr += ip & ~TARGET_PAGE_MASK;
+    tb_invalidate_phys_page_range(paddr, paddr + 1, 1);
+}
+
+void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip,
+                             TPRAccess access)
+{
+    VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev);
+    CPUX86State *env = cpu;
+
+    cpu_synchronize_state(env);
+
+    if (evaluate_tpr_instruction(s, env, &ip, access) < 0) {
+        if (s->state == VAPIC_ACTIVE) {
+            vapic_enable(s, env);
+        }
+        return;
+    }
+    if (update_rom_mapping(s, env, ip) < 0) {
+        return;
+    }
+    if (vapic_enable(s, env) < 0) {
+        return;
+    }
+    patch_instruction(s, env, ip);
+}
+
+typedef struct VAPICEnableTPRReporting {
+    DeviceState *apic;
+    bool enable;
+} VAPICEnableTPRReporting;
+
+static void vapic_do_enable_tpr_reporting(void *data)
+{
+    VAPICEnableTPRReporting *info = data;
+
+    apic_enable_tpr_access_reporting(info->apic, info->enable);
+}
+
+static void vapic_enable_tpr_reporting(bool enable)
+{
+    VAPICEnableTPRReporting info = {
+        .enable = enable,
+    };
+    CPUX86State *env;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        info.apic = env->apic_state;
+        run_on_cpu(env, vapic_do_enable_tpr_reporting, &info);
+    }
+}
+
+static void vapic_reset(DeviceState *dev)
+{
+    VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev);
+
+    if (s->state == VAPIC_ACTIVE) {
+        s->state = VAPIC_STANDBY;
+    }
+    vapic_enable_tpr_reporting(false);
+}
+
+/*
+ * Set the IRQ polling hypercalls to the supported variant:
+ *  - vmcall if using KVM in-kernel irqchip
+ *  - 32-bit VAPIC port write otherwise
+ */
+static int patch_hypercalls(VAPICROMState *s)
+{
+    target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
+    static const uint8_t vmcall_pattern[] = { /* vmcall */
+        0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1
+    };
+    static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */
+        0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e
+    };
+    uint8_t alternates[2];
+    const uint8_t *pattern;
+    const uint8_t *patch;
+    int patches = 0;
+    off_t pos;
+    uint8_t *rom;
+
+    rom = g_malloc(s->rom_size);
+    cpu_physical_memory_rw(rom_paddr, rom, s->rom_size, 0);
+
+    for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) {
+        if (kvm_irqchip_in_kernel()) {
+            pattern = outl_pattern;
+            alternates[0] = outl_pattern[7];
+            alternates[1] = outl_pattern[7];
+            patch = &vmcall_pattern[5];
+        } else {
+            pattern = vmcall_pattern;
+            alternates[0] = vmcall_pattern[7];
+            alternates[1] = 0xd9; /* AMD's VMMCALL */
+            patch = &outl_pattern[5];
+        }
+        if (memcmp(rom + pos, pattern, 7) == 0 &&
+            (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) {
+            cpu_physical_memory_rw(rom_paddr + pos + 5, (uint8_t *)patch,
+                                   3, 1);
+            /*
+             * Don't flush the tb here. Under ordinary conditions, the patched
+             * calls are miles away from the current IP. Under malicious
+             * conditions, the guest could trick us to crash.
+             */
+        }
+    }
+
+    g_free(rom);
+
+    if (patches != 0 && patches != 2) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * For TCG mode or the time KVM honors read-only memory regions, we need to
+ * enable write access to the option ROM so that variables can be updated by
+ * the guest.
+ */
+static void vapic_map_rom_writable(VAPICROMState *s)
+{
+    target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
+    MemoryRegionSection section;
+    MemoryRegion *as;
+    size_t rom_size;
+    uint8_t *ram;
+
+    as = sysbus_address_space(&s->busdev);
+
+    if (s->rom_mapped_writable) {
+        memory_region_del_subregion(as, &s->rom);
+        memory_region_destroy(&s->rom);
+    }
+
+    /* grab RAM memory region (region @rom_paddr may still be pc.rom) */
+    section = memory_region_find(as, 0, 1);
+
+    /* read ROM size from RAM region */
+    ram = memory_region_get_ram_ptr(section.mr);
+    rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
+    s->rom_size = rom_size;
+
+    /* We need to round to avoid creating subpages
+     * from which we cannot run code. */
+    rom_size += rom_paddr & ~TARGET_PAGE_MASK;
+    rom_paddr &= TARGET_PAGE_MASK;
+    rom_size = TARGET_PAGE_ALIGN(rom_size);
+
+    memory_region_init_alias(&s->rom, "kvmvapic-rom", section.mr, rom_paddr,
+                             rom_size);
+    memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000);
+    s->rom_mapped_writable = true;
+}
+
+static int vapic_prepare(VAPICROMState *s)
+{
+    vapic_map_rom_writable(s);
+
+    if (patch_hypercalls(s) < 0) {
+        return -1;
+    }
+
+    vapic_enable_tpr_reporting(true);
+
+    return 0;
+}
+
+static void vapic_write(void *opaque, target_phys_addr_t addr, uint64_t data,
+                        unsigned int size)
+{
+    CPUX86State *env = cpu_single_env;
+    target_phys_addr_t rom_paddr;
+    VAPICROMState *s = opaque;
+
+    cpu_synchronize_state(env);
+
+    /*
+     * The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
+     *  o 16-bit write access:
+     *    Reports the option ROM initialization to the hypervisor. Written
+     *    value is the offset of the state structure in the ROM.
+     *  o 8-bit write access:
+     *    Reactivates the VAPIC after a guest hibernation, i.e. after the
+     *    option ROM content has been re-initialized by a guest power cycle.
+     *  o 32-bit write access:
+     *    Poll for pending IRQs, considering the current VAPIC state.
+     */
+    switch (size) {
+    case 2:
+        if (s->state == VAPIC_INACTIVE) {
+            rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK;
+            s->rom_state_paddr = rom_paddr + data;
+
+            s->state = VAPIC_STANDBY;
+        }
+        if (vapic_prepare(s) < 0) {
+            s->state = VAPIC_INACTIVE;
+            break;
+        }
+        break;
+    case 1:
+        if (kvm_enabled()) {
+            /*
+             * Disable triggering instruction in ROM by writing a NOP.
+             *
+             * We cannot do this in TCG mode as the reported IP is not
+             * accurate.
+             */
+            pause_all_vcpus();
+            patch_byte(env, env->eip - 2, 0x66);
+            patch_byte(env, env->eip - 1, 0x90);
+            resume_all_vcpus();
+        }
+
+        if (s->state == VAPIC_ACTIVE) {
+            break;
+        }
+        if (update_rom_mapping(s, env, env->eip) < 0) {
+            break;
+        }
+        if (find_real_tpr_addr(s, env) < 0) {
+            break;
+        }
+        vapic_enable(s, env);
+        break;
+    default:
+    case 4:
+        if (!kvm_irqchip_in_kernel()) {
+            apic_poll_irq(env->apic_state);
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps vapic_ops = {
+    .write = vapic_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int vapic_init(SysBusDevice *dev)
+{
+    VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev);
+
+    memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2);
+    sysbus_add_io(dev, VAPIC_IO_PORT, &s->io);
+    sysbus_init_ioports(dev, VAPIC_IO_PORT, 2);
+
+    option_rom[nb_option_roms].name = "kvmvapic.bin";
+    option_rom[nb_option_roms].bootindex = -1;
+    nb_option_roms++;
+
+    return 0;
+}
+
+static void do_vapic_enable(void *data)
+{
+    VAPICROMState *s = data;
+
+    vapic_enable(s, first_cpu);
+}
+
+static int vapic_post_load(void *opaque, int version_id)
+{
+    VAPICROMState *s = opaque;
+    uint8_t *zero;
+
+    /*
+     * The old implementation of qemu-kvm did not provide the state
+     * VAPIC_STANDBY. Reconstruct it.
+     */
+    if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) {
+        s->state = VAPIC_STANDBY;
+    }
+
+    if (s->state != VAPIC_INACTIVE) {
+        if (vapic_prepare(s) < 0) {
+            return -1;
+        }
+    }
+    if (s->state == VAPIC_ACTIVE) {
+        if (smp_cpus == 1) {
+            run_on_cpu(first_cpu, do_vapic_enable, s);
+        } else {
+            zero = g_malloc0(s->rom_state.vapic_size);
+            cpu_physical_memory_rw(s->vapic_paddr, zero,
+                                   s->rom_state.vapic_size, 1);
+            g_free(zero);
+        }
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_handlers = {
+    .name = "kvmvapic-handlers",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(set_tpr, VAPICHandlers),
+        VMSTATE_UINT32(set_tpr_eax, VAPICHandlers),
+        VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8),
+        VMSTATE_UINT32(get_tpr_stack, VAPICHandlers),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_guest_rom = {
+    .name = "kvmvapic-guest-rom",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UNUSED(8),     /* signature */
+        VMSTATE_UINT32(vaddr, GuestROMState),
+        VMSTATE_UINT32(fixup_start, GuestROMState),
+        VMSTATE_UINT32(fixup_end, GuestROMState),
+        VMSTATE_UINT32(vapic_vaddr, GuestROMState),
+        VMSTATE_UINT32(vapic_size, GuestROMState),
+        VMSTATE_UINT32(vcpu_shift, GuestROMState),
+        VMSTATE_UINT32(real_tpr_addr, GuestROMState),
+        VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
+        VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_vapic = {
+    .name = "kvm-tpr-opt",      /* compatible with qemu-kvm VAPIC */
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = vapic_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom,
+                       GuestROMState),
+        VMSTATE_UINT32(state, VAPICROMState),
+        VMSTATE_UINT32(real_tpr_addr, VAPICROMState),
+        VMSTATE_UINT32(rom_state_vaddr, VAPICROMState),
+        VMSTATE_UINT32(vapic_paddr, VAPICROMState),
+        VMSTATE_UINT32(rom_state_paddr, VAPICROMState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vapic_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->no_user = 1;
+    dc->reset   = vapic_reset;
+    dc->vmsd    = &vmstate_vapic;
+    sc->init    = vapic_init;
+}
+
+static TypeInfo vapic_type = {
+    .name          = "kvmvapic",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(VAPICROMState),
+    .class_init    = vapic_class_init,
+};
+
+static void vapic_register(void)
+{
+    type_register_static(&vapic_type);
+}
+
+type_init(vapic_register);
diff --git a/hw/leon3.c b/hw/leon3.c
index 71d79a65a7..0a5ff165a1 100644
--- a/hw/leon3.c
+++ b/hw/leon3.c
@@ -42,16 +42,16 @@
 #define MAX_PILS 16
 
 typedef struct ResetData {
-    CPUState *env;
+    CPUSPARCState *env;
     uint32_t  entry;            /* save kernel entry in case of reset */
 } ResetData;
 
 static void main_cpu_reset(void *opaque)
 {
     ResetData *s   = (ResetData *)opaque;
-    CPUState  *env = s->env;
+    CPUSPARCState  *env = s->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     env->halted = 0;
     env->pc     = s->entry;
@@ -65,7 +65,7 @@ void leon3_irq_ack(void *irq_manager, int intno)
 
 static void leon3_set_pil_in(void *opaque, uint32_t pil_in)
 {
-    CPUState *env = (CPUState *)opaque;
+    CPUSPARCState *env = (CPUSPARCState *)opaque;
 
     assert(env != NULL);
 
@@ -101,7 +101,7 @@ static void leon3_generic_hw_init(ram_addr_t  ram_size,
                                   const char *initrd_filename,
                                   const char *cpu_model)
 {
-    CPUState   *env;
+    CPUSPARCState   *env;
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *prom = g_new(MemoryRegion, 1);
diff --git a/hw/lm32_boards.c b/hw/lm32_boards.c
index 3cdf120a14..4dd4f0ab90 100644
--- a/hw/lm32_boards.c
+++ b/hw/lm32_boards.c
@@ -31,7 +31,7 @@
 #include "exec-memory.h"
 
 typedef struct {
-    CPUState *env;
+    CPULM32State *env;
     target_phys_addr_t bootstrap_pc;
     target_phys_addr_t flash_base;
     target_phys_addr_t hwsetup_base;
@@ -42,7 +42,7 @@ typedef struct {
 
 static void cpu_irq_handler(void *opaque, int irq, int level)
 {
-    CPUState *env = opaque;
+    CPULM32State *env = opaque;
 
     if (level) {
         cpu_interrupt(env, CPU_INTERRUPT_HARD);
@@ -54,9 +54,9 @@ static void cpu_irq_handler(void *opaque, int irq, int level)
 static void main_cpu_reset(void *opaque)
 {
     ResetInfo *reset_info = opaque;
-    CPUState *env = reset_info->env;
+    CPULM32State *env = reset_info->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     /* init defaults */
     env->pc = (uint32_t)reset_info->bootstrap_pc;
@@ -75,7 +75,7 @@ static void lm32_evr_init(ram_addr_t ram_size_not_used,
                           const char *kernel_cmdline,
                           const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPULM32State *env;
     DriveInfo *dinfo;
     MemoryRegion *address_space_mem =  get_system_memory();
     MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
@@ -163,7 +163,7 @@ static void lm32_uclinux_init(ram_addr_t ram_size_not_used,
                           const char *kernel_cmdline,
                           const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPULM32State *env;
     DriveInfo *dinfo;
     MemoryRegion *address_space_mem =  get_system_memory();
     MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index a46fdfc487..2b59c36ee6 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -24,11 +24,12 @@
 #include "hw.h"
 #include "qemu-timer.h"
 #include "sysemu.h"
-#include "pc.h"
-#include "apic.h"
-#include "isa.h"
 #include "mc146818rtc.h"
 
+#ifdef TARGET_I386
+#include "apic.h"
+#endif
+
 //#define DEBUG_CMOS
 //#define DEBUG_COALESCED
 
diff --git a/hw/mcf.h b/hw/mcf.h
index baa790b0c5..19a8b54778 100644
--- a/hw/mcf.h
+++ b/hw/mcf.h
@@ -17,7 +17,7 @@ void mcf_uart_mm_init(struct MemoryRegion *sysmem,
 /* mcf_intc.c */
 qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem,
                         target_phys_addr_t base,
-                        CPUState *env);
+                        CPUM68KState *env);
 
 /* mcf_fec.c */
 void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd,
@@ -25,6 +25,6 @@ void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd,
 
 /* mcf5206.c */
 qemu_irq *mcf5206_init(struct MemoryRegion *sysmem,
-                       uint32_t base, CPUState *env);
+                       uint32_t base, CPUM68KState *env);
 
 #endif
diff --git a/hw/mcf5206.c b/hw/mcf5206.c
index 5110d833af..539b391338 100644
--- a/hw/mcf5206.c
+++ b/hw/mcf5206.c
@@ -145,7 +145,7 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq)
 /* System Integration Module.  */
 
 typedef struct {
-    CPUState *env;
+    CPUM68KState *env;
     MemoryRegion iomem;
     m5206_timer_state *timer[2];
     void *uart[2];
@@ -525,7 +525,7 @@ static const MemoryRegionOps m5206_mbar_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUState *env)
+qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUM68KState *env)
 {
     m5206_mbar_state *s;
     qemu_irq *pic;
diff --git a/hw/mcf5208.c b/hw/mcf5208.c
index aa11a755cc..d3ebe8d9ad 100644
--- a/hw/mcf5208.c
+++ b/hw/mcf5208.c
@@ -192,7 +192,7 @@ static void mcf5208evb_init(ram_addr_t ram_size,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUM68KState *env;
     int kernel_size;
     uint64_t elf_entry;
     target_phys_addr_t entry;
diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c
index 0b498dd3ac..cc1a5f3763 100644
--- a/hw/mcf_intc.c
+++ b/hw/mcf_intc.c
@@ -16,7 +16,7 @@ typedef struct {
     uint64_t ifr;
     uint64_t enabled;
     uint8_t icr[64];
-    CPUState *env;
+    CPUM68KState *env;
     int active_vector;
 } mcf_intc_state;
 
@@ -139,7 +139,7 @@ static const MemoryRegionOps mcf_intc_ops = {
 
 qemu_irq *mcf_intc_init(MemoryRegion *sysmem,
                         target_phys_addr_t base,
-                        CPUState *env)
+                        CPUM68KState *env)
 {
     mcf_intc_state *s;
 
diff --git a/hw/microblaze_boot.c b/hw/microblaze_boot.c
new file mode 100644
index 0000000000..b4fbb10dd0
--- /dev/null
+++ b/hw/microblaze_boot.c
@@ -0,0 +1,177 @@
+/*
+ * Microblaze kernel loader
+ *
+ * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (c) 2012 PetaLogix
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "qemu-common.h"
+#include "device_tree.h"
+#include "loader.h"
+#include "elf.h"
+
+#include "microblaze_boot.h"
+
+static struct
+{
+    void (*machine_cpu_reset)(CPUMBState *);
+    uint32_t bootstrap_pc;
+    uint32_t cmdline;
+    uint32_t fdt;
+} boot_info;
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUMBState *env = opaque;
+
+    cpu_state_reset(env);
+    env->regs[5] = boot_info.cmdline;
+    env->regs[7] = boot_info.fdt;
+    env->sregs[SR_PC] = boot_info.bootstrap_pc;
+    if (boot_info.machine_cpu_reset) {
+        boot_info.machine_cpu_reset(env);
+    }
+}
+
+static int microblaze_load_dtb(target_phys_addr_t addr,
+                                      uint32_t ramsize,
+                                      const char *kernel_cmdline,
+                                      const char *dtb_filename)
+{
+    int fdt_size;
+#ifdef CONFIG_FDT
+    void *fdt = NULL;
+    int r;
+
+    if (dtb_filename) {
+        fdt = load_device_tree(dtb_filename, &fdt_size);
+    }
+    if (!fdt) {
+        return 0;
+    }
+
+    if (kernel_cmdline) {
+        r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+                                                        kernel_cmdline);
+        if (r < 0) {
+            fprintf(stderr, "couldn't set /chosen/bootargs\n");
+        }
+    }
+
+    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
+#else
+    /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
+       to the kernel.  */
+    if (dtb_filename) {
+        fdt_size = load_image_targphys(dtb_filename, addr, 0x10000);
+    }
+    if (kernel_cmdline) {
+        fprintf(stderr,
+                "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
+    }
+#endif
+    return fdt_size;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0x30000000LL;
+}
+
+void microblaze_load_kernel(CPUMBState *env, target_phys_addr_t ddr_base,
+                            uint32_t ramsize, const char *dtb_filename,
+                                  void (*machine_cpu_reset)(CPUMBState *))
+{
+
+    QemuOpts *machine_opts;
+    const char *kernel_filename = NULL;
+    const char *kernel_cmdline = NULL;
+
+    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+    if (machine_opts) {
+        const char *dtb_arg;
+        kernel_filename = qemu_opt_get(machine_opts, "kernel");
+        kernel_cmdline = qemu_opt_get(machine_opts, "append");
+        dtb_arg = qemu_opt_get(machine_opts, "dtb");
+        if (dtb_arg) { /* Preference a -dtb argument */
+            dtb_filename = dtb_arg;
+        } else { /* default to pcbios dtb as passed by machine_init */
+            dtb_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename);
+        }
+    }
+
+    boot_info.machine_cpu_reset = machine_cpu_reset;
+    qemu_register_reset(main_cpu_reset, env);
+
+    if (kernel_filename) {
+        int kernel_size;
+        uint64_t entry, low, high;
+        uint32_t base32;
+        int big_endian = 0;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+        big_endian = 1;
+#endif
+
+        /* Boots a kernel elf binary.  */
+        kernel_size = load_elf(kernel_filename, NULL, NULL,
+                               &entry, &low, &high,
+                               big_endian, ELF_MACHINE, 0);
+        base32 = entry;
+        if (base32 == 0xc0000000) {
+            kernel_size = load_elf(kernel_filename, translate_kernel_address,
+                                   NULL, &entry, NULL, NULL,
+                                   big_endian, ELF_MACHINE, 0);
+        }
+        /* Always boot into physical ram.  */
+        boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff);
+
+        /* If it wasn't an ELF image, try an u-boot image.  */
+        if (kernel_size < 0) {
+            target_phys_addr_t uentry, loadaddr;
+
+            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+            boot_info.bootstrap_pc = uentry;
+            high = (loadaddr + kernel_size + 3) & ~3;
+        }
+
+        /* Not an ELF image nor an u-boot image, try a RAW image.  */
+        if (kernel_size < 0) {
+            kernel_size = load_image_targphys(kernel_filename, ddr_base,
+                                              ram_size);
+            boot_info.bootstrap_pc = ddr_base;
+            high = (ddr_base + kernel_size + 3) & ~3;
+        }
+
+        boot_info.cmdline = high + 4096;
+        if (kernel_cmdline && strlen(kernel_cmdline)) {
+            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+        }
+        /* Provide a device-tree.  */
+        boot_info.fdt = boot_info.cmdline + 4096;
+        microblaze_load_dtb(boot_info.fdt, ram_size, kernel_cmdline,
+                                                     dtb_filename);
+    }
+
+}
diff --git a/hw/microblaze_boot.h b/hw/microblaze_boot.h
new file mode 100644
index 0000000000..bf9d136f12
--- /dev/null
+++ b/hw/microblaze_boot.h
@@ -0,0 +1,10 @@
+#ifndef __MICROBLAZE_BOOT__
+#define __MICROBLAZE_BOOT__
+
+#include "hw.h"
+
+void microblaze_load_kernel(CPUMBState *env, target_phys_addr_t ddr_base,
+                            uint32_t ramsize, const char *dtb_filename,
+                                  void (*machine_cpu_reset)(CPUMBState *));
+
+#endif /* __MICROBLAZE_BOOT __ */
diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze_pic_cpu.c
index 8b5623ce28..ff36a526fc 100644
--- a/hw/microblaze_pic_cpu.c
+++ b/hw/microblaze_pic_cpu.c
@@ -29,7 +29,7 @@
 
 static void microblaze_pic_cpu_handler(void *opaque, int irq, int level)
 {
-    CPUState *env = (CPUState *)opaque;
+    CPUMBState *env = (CPUMBState *)opaque;
     int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
 
     if (level)
@@ -38,7 +38,7 @@ static void microblaze_pic_cpu_handler(void *opaque, int irq, int level)
         cpu_reset_interrupt(env, type);
 }
 
-qemu_irq *microblaze_pic_init_cpu(CPUState *env)
+qemu_irq *microblaze_pic_init_cpu(CPUMBState *env)
 {
     return qemu_allocate_irqs(microblaze_pic_cpu_handler, env, 2);
 }
diff --git a/hw/microblaze_pic_cpu.h b/hw/microblaze_pic_cpu.h
index 4c76275976..43090a48ef 100644
--- a/hw/microblaze_pic_cpu.h
+++ b/hw/microblaze_pic_cpu.h
@@ -3,6 +3,6 @@
 
 #include "qemu-common.h"
 
-qemu_irq *microblaze_pic_init_cpu(CPUState *env);
+qemu_irq *microblaze_pic_init_cpu(CPUMBState *env);
 
 #endif /*  MICROBLAZE_PIC_CPU_H */
diff --git a/hw/milkymist.c b/hw/milkymist.c
index eaef0c24c3..8bb6a97b22 100644
--- a/hw/milkymist.c
+++ b/hw/milkymist.c
@@ -37,7 +37,7 @@
 #define KERNEL_LOAD_ADDR 0x40000000
 
 typedef struct {
-    CPUState *env;
+    CPULM32State *env;
     target_phys_addr_t bootstrap_pc;
     target_phys_addr_t flash_base;
     target_phys_addr_t initrd_base;
@@ -47,7 +47,7 @@ typedef struct {
 
 static void cpu_irq_handler(void *opaque, int irq, int level)
 {
-    CPUState *env = opaque;
+    CPULM32State *env = opaque;
 
     if (level) {
         cpu_interrupt(env, CPU_INTERRUPT_HARD);
@@ -59,9 +59,9 @@ static void cpu_irq_handler(void *opaque, int irq, int level)
 static void main_cpu_reset(void *opaque)
 {
     ResetInfo *reset_info = opaque;
-    CPUState *env = reset_info->env;
+    CPULM32State *env = reset_info->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     /* init defaults */
     env->pc = reset_info->bootstrap_pc;
@@ -79,7 +79,7 @@ milkymist_init(ram_addr_t ram_size_not_used,
                           const char *kernel_cmdline,
                           const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPULM32State *env;
     int kernel_size;
     DriveInfo *dinfo;
     MemoryRegion *address_space_mem = get_system_memory();
diff --git a/hw/mips_cpudevs.h b/hw/mips_cpudevs.h
index db82b4105c..6bea24bf10 100644
--- a/hw/mips_cpudevs.h
+++ b/hw/mips_cpudevs.h
@@ -7,9 +7,9 @@ uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr);
 uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr);
 
 /* mips_int.c */
-void cpu_mips_irq_init_cpu(CPUState *env);
+void cpu_mips_irq_init_cpu(CPUMIPSState *env);
 
 /* mips_timer.c */
-void cpu_mips_clock_init(CPUState *);
+void cpu_mips_clock_init(CPUMIPSState *);
 
 #endif
diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c
index e3ba9dd42d..37dc711e08 100644
--- a/hw/mips_fulong2e.c
+++ b/hw/mips_fulong2e.c
@@ -29,7 +29,6 @@
 #include "mips.h"
 #include "mips_cpudevs.h"
 #include "pci.h"
-#include "usb-uhci.h"
 #include "qemu-char.h"
 #include "sysemu.h"
 #include "audio/audio.h"
@@ -103,7 +102,7 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
     va_end(ap);
 }
 
-static int64_t load_kernel (CPUState *env)
+static int64_t load_kernel (CPUMIPSState *env)
 {
     int64_t kernel_entry, kernel_low, kernel_high;
     int index = 0;
@@ -169,7 +168,7 @@ static int64_t load_kernel (CPUState *env)
     return kernel_entry;
 }
 
-static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr)
+static void write_bootloader (CPUMIPSState *env, uint8_t *base, int64_t kernel_addr)
 {
     uint32_t *p;
 
@@ -199,9 +198,9 @@ static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr)
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUMIPSState *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     /* TODO: 2E reset stuff */
     if (loaderparams.kernel_filename) {
         env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
@@ -249,7 +248,7 @@ static void network_init (void)
 
 static void cpu_request_exit(void *opaque, int irq, int level)
 {
-    CPUState *env = cpu_single_env;
+    CPUMIPSState *env = cpu_single_env;
 
     if (env && level) {
         cpu_exit(env);
@@ -273,7 +272,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
     i2c_bus *smbus;
     int i;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
-    CPUState *env;
+    CPUMIPSState *env;
 
     /* init CPUs */
     if (cpu_model == NULL) {
@@ -355,8 +354,10 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
     isa_bus_irqs(isa_bus, i8259);
 
     vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
-    usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2));
-    usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3));
+    pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2),
+                      "vt82c686b-usb-uhci");
+    pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3),
+                      "vt82c686b-usb-uhci");
 
     smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
                               0xeee1, NULL);
diff --git a/hw/mips_int.c b/hw/mips_int.c
index 477f6abf95..6423fd0bd9 100644
--- a/hw/mips_int.c
+++ b/hw/mips_int.c
@@ -26,7 +26,7 @@
 
 static void cpu_mips_irq_request(void *opaque, int irq, int level)
 {
-    CPUState *env = (CPUState *)opaque;
+    CPUMIPSState *env = (CPUMIPSState *)opaque;
 
     if (irq < 0 || irq > 7)
         return;
@@ -44,7 +44,7 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
     }
 }
 
-void cpu_mips_irq_init_cpu(CPUState *env)
+void cpu_mips_irq_init_cpu(CPUMIPSState *env)
 {
     qemu_irq *qi;
     int i;
@@ -55,7 +55,7 @@ void cpu_mips_irq_init_cpu(CPUState *env)
     }
 }
 
-void cpu_mips_soft_irq(CPUState *env, int irq, int level)
+void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level)
 {
     if (irq < 0 || irq > 2) {
         return;
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
index 2b4678e170..a6bc7badff 100644
--- a/hw/mips_jazz.c
+++ b/hw/mips_jazz.c
@@ -50,8 +50,8 @@ enum jazz_model_e
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
-    cpu_reset(env);
+    CPUMIPSState *env = opaque;
+    cpu_state_reset(env);
 }
 
 static uint64_t rtc_read(void *opaque, target_phys_addr_t addr, unsigned size)
@@ -97,7 +97,7 @@ static const MemoryRegionOps dma_dummy_ops = {
 
 static void cpu_request_exit(void *opaque, int irq, int level)
 {
-    CPUState *env = cpu_single_env;
+    CPUMIPSState *env = cpu_single_env;
 
     if (env && level) {
         cpu_exit(env);
@@ -112,7 +112,7 @@ static void mips_jazz_init(MemoryRegion *address_space,
 {
     char *filename;
     int bios_size, n;
-    CPUState *env;
+    CPUMIPSState *env;
     qemu_irq *rc4030, *i8259;
     rc4030_dma *dmas;
     void* rc4030_opaque;
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index b1563ed2a7..4752bb2865 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -33,7 +33,6 @@
 #include "mips.h"
 #include "mips_cpudevs.h"
 #include "pci.h"
-#include "usb-uhci.h"
 #include "vmware_vga.h"
 #include "qemu-char.h"
 #include "sysemu.h"
@@ -56,6 +55,13 @@
 #define ENVP_NB_ENTRIES	 	16
 #define ENVP_ENTRY_SIZE	 	256
 
+/* Hardware addresses */
+#define FLASH_ADDRESS 0x1e000000ULL
+#define FPGA_ADDRESS  0x1f000000ULL
+#define RESET_ADDRESS 0x1fc00000ULL
+
+#define FLASH_SIZE    0x400000
+
 #define MAX_IDE_BUS 2
 
 typedef struct {
@@ -332,9 +338,9 @@ static void malta_fpga_write(void *opaque, target_phys_addr_t addr,
         break;
 
     /* LEDBAR Register */
-    /* XXX: implement a 8-LED array */
     case 0x00408:
         s->leds = val & 0xff;
+        malta_fpga_update_display(s);
         break;
 
     /* ASCIIWORD Register */
@@ -501,7 +507,7 @@ static void network_init(void)
      a3 - RAM size in bytes
 */
 
-static void write_bootloader (CPUState *env, uint8_t *base,
+static void write_bootloader (CPUMIPSState *env, uint8_t *base,
                               int64_t kernel_entry)
 {
     uint32_t *p;
@@ -737,7 +743,7 @@ static int64_t load_kernel (void)
     return kernel_entry;
 }
 
-static void malta_mips_config(CPUState *env)
+static void malta_mips_config(CPUMIPSState *env)
 {
     env->mvp->CP0_MVPConf0 |= ((smp_cpus - 1) << CP0MVPC0_PVPE) |
                          ((smp_cpus * env->nr_threads - 1) << CP0MVPC0_PTC);
@@ -745,8 +751,8 @@ static void malta_mips_config(CPUState *env)
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
-    cpu_reset(env);
+    CPUMIPSState *env = opaque;
+    cpu_state_reset(env);
 
     /* The bootloader does not need to be rewritten as it is located in a
        read only location. The kernel location and the arguments table
@@ -760,7 +766,7 @@ static void main_cpu_reset(void *opaque)
 
 static void cpu_request_exit(void *opaque, int irq, int level)
 {
-    CPUState *env = cpu_single_env;
+    CPUMIPSState *env = cpu_single_env;
 
     if (env && level) {
         cpu_exit(env);
@@ -778,11 +784,11 @@ void mips_malta_init (ram_addr_t ram_size,
     MemoryRegion *system_memory = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *bios, *bios_alias = g_new(MemoryRegion, 1);
-    target_long bios_size;
+    target_long bios_size = FLASH_SIZE;
     int64_t kernel_entry;
     PCIBus *pci_bus;
     ISABus *isa_bus;
-    CPUState *env;
+    CPUMIPSState *env;
     qemu_irq *isa_irq;
     qemu_irq *cpu_exit_irq;
     int piix4_devfn;
@@ -792,7 +798,7 @@ void mips_malta_init (ram_addr_t ram_size,
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     DriveInfo *fd[MAX_FD];
     int fl_idx = 0;
-    int fl_sectors = 0;
+    int fl_sectors = bios_size >> 16;
     int be;
 
     DeviceState *dev = qdev_create(NULL, "mips-malta");
@@ -848,19 +854,26 @@ void mips_malta_init (ram_addr_t ram_size,
     be = 0;
 #endif
     /* FPGA */
-    malta_fpga_init(system_memory, 0x1f000000LL, env->irq[2], serial_hds[2]);
+    malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[2], serial_hds[2]);
 
-    /* Load firmware in flash / BIOS unless we boot directly into a kernel. */
+    /* Load firmware in flash / BIOS. */
+    dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+#ifdef DEBUG_BOARD_INIT
+    if (dinfo) {
+        printf("Register parallel flash %d size " TARGET_FMT_lx " at "
+               "addr %08llx '%s' %x\n",
+               fl_idx, bios_size, FLASH_ADDRESS,
+               bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+    }
+#endif
+    fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios",
+                               BIOS_SIZE, dinfo ? dinfo->bdrv : NULL,
+                               65536, fl_sectors,
+                               4, 0x0000, 0x0000, 0x0000, 0x0000, be);
+    bios = pflash_cfi01_get_memory(fl);
+    fl_idx++;
     if (kernel_filename) {
         /* Write a small bootloader to the flash location. */
-        bios = g_new(MemoryRegion, 1);
-        memory_region_init_ram(bios, "mips_malta.bios", BIOS_SIZE);
-        vmstate_register_ram_global(bios);
-        memory_region_set_readonly(bios, true);
-        memory_region_init_alias(bios_alias, "bios.1fc", bios, 0, BIOS_SIZE);
-        /* Map the bios at two physical locations, as on the real board. */
-        memory_region_add_subregion(system_memory, 0x1e000000LL, bios);
-        memory_region_add_subregion(system_memory, 0x1fc00000LL, bios_alias);
         loaderparams.ram_size = ram_size;
         loaderparams.kernel_filename = kernel_filename;
         loaderparams.kernel_cmdline = kernel_cmdline;
@@ -868,45 +881,15 @@ void mips_malta_init (ram_addr_t ram_size,
         kernel_entry = load_kernel();
         write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry);
     } else {
-        dinfo = drive_get(IF_PFLASH, 0, fl_idx);
-        if (dinfo) {
-            /* Load firmware from flash. */
-            bios_size = 0x400000;
-            fl_sectors = bios_size >> 16;
-#ifdef DEBUG_BOARD_INIT
-            printf("Register parallel flash %d size " TARGET_FMT_lx " at "
-                   "addr %08llx '%s' %x\n",
-                   fl_idx, bios_size, 0x1e000000LL,
-                   bdrv_get_device_name(dinfo->bdrv), fl_sectors);
-#endif
-            fl = pflash_cfi01_register(0x1e000000LL,
-                                       NULL, "mips_malta.bios", BIOS_SIZE,
-                                       dinfo->bdrv, 65536, fl_sectors,
-                                       4, 0x0000, 0x0000, 0x0000, 0x0000, be);
-            bios = pflash_cfi01_get_memory(fl);
-            /* Map the bios at two physical locations, as on the real board. */
-            memory_region_init_alias(bios_alias, "bios.1fc",
-                                     bios, 0, BIOS_SIZE);
-            memory_region_add_subregion(system_memory, 0x1fc00000LL,
-                                        bios_alias);
-           fl_idx++;
-        } else {
-            bios = g_new(MemoryRegion, 1);
-            memory_region_init_ram(bios, "mips_malta.bios", BIOS_SIZE);
-            vmstate_register_ram_global(bios);
-            memory_region_set_readonly(bios, true);
-            memory_region_init_alias(bios_alias, "bios.1fc",
-                                     bios, 0, BIOS_SIZE);
-            /* Map the bios at two physical locations, as on the real board. */
-            memory_region_add_subregion(system_memory, 0x1e000000LL, bios);
-            memory_region_add_subregion(system_memory, 0x1fc00000LL,
-                                        bios_alias);
+        /* Load firmware from flash. */
+        if (!dinfo) {
             /* Load a BIOS image. */
-            if (bios_name == NULL)
+            if (bios_name == NULL) {
                 bios_name = BIOS_FILENAME;
+            }
             filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
             if (filename) {
-                bios_size = load_image_targphys(filename, 0x1fc00000LL,
+                bios_size = load_image_targphys(filename, FLASH_ADDRESS,
                                                 BIOS_SIZE);
                 g_free(filename);
             } else {
@@ -933,6 +916,10 @@ void mips_malta_init (ram_addr_t ram_size,
 #endif
     }
 
+    /* Map the BIOS at a 2nd physical location, as on the real board. */
+    memory_region_init_alias(bios_alias, "bios.1fc", bios, 0, BIOS_SIZE);
+    memory_region_add_subregion(system_memory, RESET_ADDRESS, bios_alias);
+
     /* Board ID = 0x420 (Malta Board with CoreLV)
        XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should
        map to the board ID. */
@@ -965,7 +952,7 @@ void mips_malta_init (ram_addr_t ram_size,
 
     isa_bus_irqs(isa_bus, s->i8259);
     pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
-    usb_uhci_piix4_init(pci_bus, piix4_devfn + 2);
+    pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci");
     smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
                           isa_get_irq(NULL, 9), NULL, 0);
     /* TODO: Populate SPD eeprom data.  */
diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c
index 76c95b2ec0..1ea7b58323 100644
--- a/hw/mips_mipssim.c
+++ b/hw/mips_mipssim.c
@@ -46,7 +46,7 @@ static struct _loaderparams {
 } loaderparams;
 
 typedef struct ResetData {
-    CPUState *env;
+    CPUMIPSState *env;
     uint64_t vector;
 } ResetData;
 
@@ -105,9 +105,9 @@ static int64_t load_kernel(void)
 static void main_cpu_reset(void *opaque)
 {
     ResetData *s = (ResetData *)opaque;
-    CPUState *env = s->env;
+    CPUMIPSState *env = s->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->active_tc.PC = s->vector & ~(target_ulong)1;
     if (s->vector & 1) {
         env->hflags |= MIPS_HFLAG_M16;
@@ -140,7 +140,7 @@ mips_mipssim_init (ram_addr_t ram_size,
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *bios = g_new(MemoryRegion, 1);
-    CPUState *env;
+    CPUMIPSState *env;
     ResetData *reset_info;
     int bios_size;
 
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
index 83401f0648..e2da49c09d 100644
--- a/hw/mips_r4k.c
+++ b/hw/mips_r4k.c
@@ -65,7 +65,7 @@ static const MemoryRegionOps mips_qemu_ops = {
 };
 
 typedef struct ResetData {
-    CPUState *env;
+    CPUMIPSState *env;
     uint64_t vector;
 } ResetData;
 
@@ -143,9 +143,9 @@ static int64_t load_kernel(void)
 static void main_cpu_reset(void *opaque)
 {
     ResetData *s = (ResetData *)opaque;
-    CPUState *env = s->env;
+    CPUMIPSState *env = s->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->active_tc.PC = s->vector;
 }
 
@@ -162,7 +162,7 @@ void mips_r4k_init (ram_addr_t ram_size,
     MemoryRegion *bios;
     MemoryRegion *iomem = g_new(MemoryRegion, 1);
     int bios_size;
-    CPUState *env;
+    CPUMIPSState *env;
     ResetData *reset_info;
     int i;
     qemu_irq *i8259;
diff --git a/hw/mips_timer.c b/hw/mips_timer.c
index cf6ac694e3..7aa9004a0e 100644
--- a/hw/mips_timer.c
+++ b/hw/mips_timer.c
@@ -27,7 +27,7 @@
 #define TIMER_FREQ	100 * 1000 * 1000
 
 /* XXX: do not use a global */
-uint32_t cpu_mips_get_random (CPUState *env)
+uint32_t cpu_mips_get_random (CPUMIPSState *env)
 {
     static uint32_t lfsr = 1;
     static uint32_t prev_idx = 0;
@@ -42,7 +42,7 @@ uint32_t cpu_mips_get_random (CPUState *env)
 }
 
 /* MIPS R4K timer */
-static void cpu_mips_timer_update(CPUState *env)
+static void cpu_mips_timer_update(CPUMIPSState *env)
 {
     uint64_t now, next;
     uint32_t wait;
@@ -55,7 +55,7 @@ static void cpu_mips_timer_update(CPUState *env)
 }
 
 /* Expire the timer.  */
-static void cpu_mips_timer_expire(CPUState *env)
+static void cpu_mips_timer_expire(CPUMIPSState *env)
 {
     cpu_mips_timer_update(env);
     if (env->insn_flags & ISA_MIPS32R2) {
@@ -64,7 +64,7 @@ static void cpu_mips_timer_expire(CPUState *env)
     qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
 }
 
-uint32_t cpu_mips_get_count (CPUState *env)
+uint32_t cpu_mips_get_count (CPUMIPSState *env)
 {
     if (env->CP0_Cause & (1 << CP0Ca_DC)) {
         return env->CP0_Count;
@@ -83,7 +83,7 @@ uint32_t cpu_mips_get_count (CPUState *env)
     }
 }
 
-void cpu_mips_store_count (CPUState *env, uint32_t count)
+void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
 {
     if (env->CP0_Cause & (1 << CP0Ca_DC))
         env->CP0_Count = count;
@@ -97,7 +97,7 @@ void cpu_mips_store_count (CPUState *env, uint32_t count)
     }
 }
 
-void cpu_mips_store_compare (CPUState *env, uint32_t value)
+void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
 {
     env->CP0_Compare = value;
     if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
@@ -107,12 +107,12 @@ void cpu_mips_store_compare (CPUState *env, uint32_t value)
     qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
 }
 
-void cpu_mips_start_count(CPUState *env)
+void cpu_mips_start_count(CPUMIPSState *env)
 {
     cpu_mips_store_count(env, env->CP0_Count);
 }
 
-void cpu_mips_stop_count(CPUState *env)
+void cpu_mips_stop_count(CPUMIPSState *env)
 {
     /* Store the current value */
     env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock),
@@ -121,7 +121,7 @@ void cpu_mips_stop_count(CPUState *env)
 
 static void mips_timer_cb (void *opaque)
 {
-    CPUState *env;
+    CPUMIPSState *env;
 
     env = opaque;
 #if 0
@@ -139,7 +139,7 @@ static void mips_timer_cb (void *opaque)
     env->CP0_Count--;
 }
 
-void cpu_mips_clock_init (CPUState *env)
+void cpu_mips_clock_init (CPUMIPSState *env)
 {
     env->timer = qemu_new_timer_ns(vm_clock, &mips_timer_cb, env);
     env->CP0_Compare = 0;
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index 50d92f8f54..31072463f4 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -224,7 +224,7 @@ static NetClientInfo net_mipsnet_info = {
     .cleanup = mipsnet_cleanup,
 };
 
-static MemoryRegionOps mipsnet_ioport_ops = {
+static const MemoryRegionOps mipsnet_ioport_ops = {
     .read = mipsnet_ioport_read,
     .write = mipsnet_ioport_write,
     .impl.min_access_size = 1,
diff --git a/hw/mpc8544_guts.c b/hw/mpc8544_guts.c
index aeb2de7ccc..13b0dddc1e 100644
--- a/hw/mpc8544_guts.c
+++ b/hw/mpc8544_guts.c
@@ -62,7 +62,7 @@ static uint64_t mpc8544_guts_read(void *opaque, target_phys_addr_t addr,
                                   unsigned size)
 {
     uint32_t value = 0;
-    CPUState *env = cpu_single_env;
+    CPUPPCState *env = cpu_single_env;
 
     addr &= MPC8544_GUTS_MMIO_SIZE - 1;
     switch (addr) {
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 187a1aef5e..c9f845a3f2 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -1513,7 +1513,7 @@ static void musicpal_init(ram_addr_t ram_size,
                const char *kernel_filename, const char *kernel_cmdline,
                const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUARMState *env;
     qemu_irq *cpu_pic;
     qemu_irq pic[32];
     DeviceState *dev;
diff --git a/hw/nseries.c b/hw/nseries.c
index c5b31843dd..a5cfa8ccbc 100644
--- a/hw/nseries.c
+++ b/hw/nseries.c
@@ -45,7 +45,6 @@ struct n800_s {
         uint32_t (*txrx)(void *opaque, uint32_t value, int len);
         uWireSlave *chip;
     } ts;
-    i2c_bus *i2c;
 
     int keymap[0x80];
     DeviceState *kbd;
@@ -194,12 +193,10 @@ static void n8x0_i2c_setup(struct n800_s *s)
 {
     DeviceState *dev;
     qemu_irq tmp_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TMP105_GPIO);
-
-    /* Attach the CPU on one end of our I2C bus.  */
-    s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
+    i2c_bus *i2c = omap_i2c_bus(s->cpu->i2c[0]);
 
     /* Attach a menelaus PM chip */
-    dev = i2c_create_slave(s->i2c, "twl92230", N8X0_MENELAUS_ADDR);
+    dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR);
     qdev_connect_gpio_out(dev, 3,
                           qdev_get_gpio_in(s->cpu->ih[0],
                                            OMAP_INT_24XX_SYS_NIRQ));
@@ -207,7 +204,7 @@ static void n8x0_i2c_setup(struct n800_s *s)
     qemu_system_powerdown = qdev_get_gpio_in(dev, 3);
 
     /* Attach a TMP105 PM chip (A0 wired to ground) */
-    dev = i2c_create_slave(s->i2c, "tmp105", N8X0_TMP105_ADDR);
+    dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR);
     qdev_connect_gpio_out(dev, 0, tmp_irq);
 }
 
@@ -391,7 +388,8 @@ static void n810_kbd_setup(struct n800_s *s)
 
     /* Attach the LM8322 keyboard to the I2C bus,
      * should happen in n8x0_i2c_setup and s->kbd be initialised here.  */
-    s->kbd = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR);
+    s->kbd = i2c_create_slave(omap_i2c_bus(s->cpu->i2c[0]),
+                           "lm8323", N810_LM8323_ADDR);
     qdev_connect_gpio_out(s->kbd, 0, kbd_irq);
 }
 
diff --git a/hw/omap.h b/hw/omap.h
index 60fa34cef2..6c3d004719 100644
--- a/hw/omap.h
+++ b/hw/omap.h
@@ -764,16 +764,7 @@ void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover);
 void omap_mmc_enable(struct omap_mmc_s *s, int enable);
 
 /* omap_i2c.c */
-struct omap_i2c_s;
-struct omap_i2c_s *omap_i2c_init(MemoryRegion *sysmem,
-                                 target_phys_addr_t base,
-                                 qemu_irq irq,
-                                 qemu_irq *dma,
-                                 omap_clk clk);
-struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta,
-                qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk);
-void omap_i2c_reset(struct omap_i2c_s *s);
-i2c_bus *omap_i2c_bus(struct omap_i2c_s *s);
+i2c_bus *omap_i2c_bus(DeviceState *omap_i2c);
 
 # define cpu_is_omap310(cpu)		(cpu->mpu_model == omap310)
 # define cpu_is_omap1510(cpu)		(cpu->mpu_model == omap1510)
@@ -813,7 +804,7 @@ struct omap_mpu_state_s {
         omap3630,
     } mpu_model;
 
-    CPUState *env;
+    CPUARMState *env;
 
     qemu_irq *drq;
 
@@ -867,7 +858,7 @@ struct omap_mpu_state_s {
 
     struct omap_pwl_s *pwl;
     struct omap_pwt_s *pwt;
-    struct omap_i2c_s *i2c[2];
+    DeviceState *i2c[2];
 
     struct omap_rtc_s *rtc;
 
diff --git a/hw/omap1.c b/hw/omap1.c
index 1aa5f2388b..2a341bfe7f 100644
--- a/hw/omap1.c
+++ b/hw/omap1.c
@@ -3694,7 +3694,6 @@ static void omap1_mpu_reset(void *opaque)
     omap_uwire_reset(mpu->microwire);
     omap_pwl_reset(mpu->pwl);
     omap_pwt_reset(mpu->pwt);
-    omap_i2c_reset(mpu->i2c[0]);
     omap_rtc_reset(mpu->rtc);
     omap_mcbsp_reset(mpu->mcbsp1);
     omap_mcbsp_reset(mpu->mcbsp2);
@@ -3702,7 +3701,7 @@ static void omap1_mpu_reset(void *opaque)
     omap_lpg_reset(mpu->led[0]);
     omap_lpg_reset(mpu->led[1]);
     omap_clkm_reset(mpu);
-    cpu_reset(mpu->env);
+    cpu_state_reset(mpu->env);
 }
 
 static const struct omap_map_s {
@@ -3993,9 +3992,15 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
     s->pwt = omap_pwt_init(system_memory, 0xfffb6000,
                            omap_findclk(s, "armxor_ck"));
 
-    s->i2c[0] = omap_i2c_init(system_memory, 0xfffb3800,
-                              qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C),
-                    &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck"));
+    s->i2c[0] = qdev_create(NULL, "omap_i2c");
+    qdev_prop_set_uint8(s->i2c[0], "revision", 0x11);
+    qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck"));
+    qdev_init_nofail(s->i2c[0]);
+    busdev = sysbus_from_qdev(s->i2c[0]);
+    sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C));
+    sysbus_connect_irq(busdev, 1, s->drq[OMAP_DMA_I2C_TX]);
+    sysbus_connect_irq(busdev, 2, s->drq[OMAP_DMA_I2C_RX]);
+    sysbus_mmio_map(busdev, 0, 0xfffb3800);
 
     s->rtc = omap_rtc_init(system_memory, 0xfffb4800,
                            qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_TIMER),
diff --git a/hw/omap2.c b/hw/omap2.c
index a6851b0fb0..42fce5e986 100644
--- a/hw/omap2.c
+++ b/hw/omap2.c
@@ -2222,9 +2222,7 @@ static void omap2_mpu_reset(void *opaque)
     omap_mmc_reset(mpu->mmc);
     omap_mcspi_reset(mpu->mcspi[0]);
     omap_mcspi_reset(mpu->mcspi[1]);
-    omap_i2c_reset(mpu->i2c[0]);
-    omap_i2c_reset(mpu->i2c[1]);
-    cpu_reset(mpu->env);
+    cpu_state_reset(mpu->env);
 }
 
 static int omap2_validate_addr(struct omap_mpu_state_s *s,
@@ -2395,16 +2393,29 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
                     omap_findclk(s, "clk32-kHz"),
                     omap_findclk(s, "core_l4_iclk"));
 
-    s->i2c[0] = omap2_i2c_init(omap_l4tao(s->l4, 5),
-                    qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ),
-                    &s->drq[OMAP24XX_DMA_I2C1_TX],
-                    omap_findclk(s, "i2c1.fclk"),
-                    omap_findclk(s, "i2c1.iclk"));
-    s->i2c[1] = omap2_i2c_init(omap_l4tao(s->l4, 6),
-                    qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ),
-                    &s->drq[OMAP24XX_DMA_I2C2_TX],
-                    omap_findclk(s, "i2c2.fclk"),
-                    omap_findclk(s, "i2c2.iclk"));
+    s->i2c[0] = qdev_create(NULL, "omap_i2c");
+    qdev_prop_set_uint8(s->i2c[0], "revision", 0x34);
+    qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk"));
+    qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk"));
+    qdev_init_nofail(s->i2c[0]);
+    busdev = sysbus_from_qdev(s->i2c[0]);
+    sysbus_connect_irq(busdev, 0,
+                       qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ));
+    sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]);
+    sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C1_RX]);
+    sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 5), 0));
+
+    s->i2c[1] = qdev_create(NULL, "omap_i2c");
+    qdev_prop_set_uint8(s->i2c[1], "revision", 0x34);
+    qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk"));
+    qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk"));
+    qdev_init_nofail(s->i2c[1]);
+    busdev = sysbus_from_qdev(s->i2c[1]);
+    sysbus_connect_irq(busdev, 0,
+                       qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ));
+    sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]);
+    sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C2_RX]);
+    sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 6), 0));
 
     s->gpio = qdev_create(NULL, "omap2-gpio");
     qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model);
diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c
index 5ec422c566..20bc82e3b8 100644
--- a/hw/omap_i2c.c
+++ b/hw/omap_i2c.c
@@ -19,14 +19,20 @@
 #include "hw.h"
 #include "i2c.h"
 #include "omap.h"
+#include "sysbus.h"
 
-struct omap_i2c_s {
+
+typedef struct OMAPI2CState {
+    SysBusDevice busdev;
     MemoryRegion iomem;
     qemu_irq irq;
     qemu_irq drq[2];
     i2c_bus *bus;
 
     uint8_t revision;
+    void *iclk;
+    void *fclk;
+
     uint8_t mask;
     uint16_t stat;
     uint16_t dma;
@@ -40,12 +46,12 @@ struct omap_i2c_s {
     uint8_t divider;
     uint8_t times[2];
     uint16_t test;
-};
+} OMAPI2CState;
 
 #define OMAP2_INTR_REV	0x34
 #define OMAP2_GC_REV	0x34
 
-static void omap_i2c_interrupts_update(struct omap_i2c_s *s)
+static void omap_i2c_interrupts_update(OMAPI2CState *s)
 {
     qemu_set_irq(s->irq, s->stat & s->mask);
     if ((s->dma >> 15) & 1)					/* RDMA_EN */
@@ -54,7 +60,7 @@ static void omap_i2c_interrupts_update(struct omap_i2c_s *s)
         qemu_set_irq(s->drq[1], (s->stat >> 4) & 1);		/* XRDY */
 }
 
-static void omap_i2c_fifo_run(struct omap_i2c_s *s)
+static void omap_i2c_fifo_run(OMAPI2CState *s)
 {
     int ack = 1;
 
@@ -122,8 +128,10 @@ static void omap_i2c_fifo_run(struct omap_i2c_s *s)
         s->control &= ~(1 << 1);				/* STP */
 }
 
-void omap_i2c_reset(struct omap_i2c_s *s)
+static void omap_i2c_reset(DeviceState *dev)
 {
+    OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState,
+                                  sysbus_from_qdev(dev));
     s->mask = 0;
     s->stat = 0;
     s->dma = 0;
@@ -143,7 +151,7 @@ void omap_i2c_reset(struct omap_i2c_s *s)
 
 static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr)
 {
-    struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+    OMAPI2CState *s = opaque;
     int offset = addr & OMAP_MPUI_REG_MASK;
     uint16_t ret;
 
@@ -243,7 +251,7 @@ static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr)
 static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
                 uint32_t value)
 {
-    struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+    OMAPI2CState *s = opaque;
     int offset = addr & OMAP_MPUI_REG_MASK;
     int nack;
 
@@ -309,14 +317,14 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
         }
 
         if (value & 2)
-            omap_i2c_reset(s);
+            omap_i2c_reset(&s->busdev.qdev);
         break;
 
     case 0x24:	/* I2C_CON */
         s->control = value & 0xcf87;
         if (~value & (1 << 15)) {				/* I2C_EN */
             if (s->revision < OMAP2_INTR_REV)
-                omap_i2c_reset(s);
+                omap_i2c_reset(&s->busdev.qdev);
             break;
         }
         if ((value & (1 << 15)) && !(value & (1 << 10))) {	/* MST */
@@ -385,7 +393,7 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
 static void omap_i2c_writeb(void *opaque, target_phys_addr_t addr,
                 uint32_t value)
 {
-    struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+    OMAPI2CState *s = opaque;
     int offset = addr & OMAP_MPUI_REG_MASK;
 
     switch (offset) {
@@ -426,50 +434,59 @@ static const MemoryRegionOps omap_i2c_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-struct omap_i2c_s *omap_i2c_init(MemoryRegion *sysmem,
-                                 target_phys_addr_t base,
-                                 qemu_irq irq,
-                                 qemu_irq *dma,
-                                 omap_clk clk)
+static int omap_i2c_init(SysBusDevice *dev)
 {
-    struct omap_i2c_s *s = (struct omap_i2c_s *)
-            g_malloc0(sizeof(struct omap_i2c_s));
-
-    /* TODO: set a value greater or equal to real hardware */
-    s->revision = 0x11;
-    s->irq = irq;
-    s->drq[0] = dma[0];
-    s->drq[1] = dma[1];
-    s->bus = i2c_init_bus(NULL, "i2c");
-    omap_i2c_reset(s);
+    OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, dev);
 
-    memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c", 0x800);
-    memory_region_add_subregion(sysmem, base, &s->iomem);
-
-    return s;
+    if (!s->fclk) {
+        hw_error("omap_i2c: fclk not connected\n");
+    }
+    if (s->revision >= OMAP2_INTR_REV && !s->iclk) {
+        /* Note that OMAP1 doesn't have a separate interface clock */
+        hw_error("omap_i2c: iclk not connected\n");
+    }
+    sysbus_init_irq(dev, &s->irq);
+    sysbus_init_irq(dev, &s->drq[0]);
+    sysbus_init_irq(dev, &s->drq[1]);
+    memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c",
+                          (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    s->bus = i2c_init_bus(&dev->qdev, NULL);
+    return 0;
 }
 
-struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta,
-                qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk)
-{
-    struct omap_i2c_s *s = (struct omap_i2c_s *)
-            g_malloc0(sizeof(struct omap_i2c_s));
+static Property omap_i2c_properties[] = {
+    DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0),
+    DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk),
+    DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk),
+    DEFINE_PROP_END_OF_LIST(),
+};
 
-    s->revision = 0x34;
-    s->irq = irq;
-    s->drq[0] = dma[0];
-    s->drq[1] = dma[1];
-    s->bus = i2c_init_bus(NULL, "i2c");
-    omap_i2c_reset(s);
+static void omap_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    k->init = omap_i2c_init;
+    dc->props = omap_i2c_properties;
+    dc->reset = omap_i2c_reset;
+}
 
-    memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap2.i2c",
-                          omap_l4_region_size(ta, 0));
-    omap_l4_attach(ta, 0, &s->iomem);
+static TypeInfo omap_i2c_info = {
+    .name = "omap_i2c",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(OMAPI2CState),
+    .class_init = omap_i2c_class_init,
+};
 
-    return s;
+static void omap_i2c_register_types(void)
+{
+    type_register_static(&omap_i2c_info);
 }
 
-i2c_bus *omap_i2c_bus(struct omap_i2c_s *s)
+i2c_bus *omap_i2c_bus(DeviceState *omap_i2c)
 {
+    OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, sysbus_from_qdev(omap_i2c));
     return s->bus;
 }
+
+type_init(omap_i2c_register_types)
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index 9b036cb103..4c7696935f 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -692,12 +692,12 @@ static void open_eth_desc_write(void *opaque,
 }
 
 
-static MemoryRegionOps open_eth_reg_ops = {
+static const MemoryRegionOps open_eth_reg_ops = {
     .read = open_eth_reg_read,
     .write = open_eth_reg_write,
 };
 
-static MemoryRegionOps open_eth_desc_ops = {
+static const MemoryRegionOps open_eth_desc_ops = {
     .read = open_eth_desc_read,
     .write = open_eth_desc_write,
 };
diff --git a/hw/openpic.c b/hw/openpic.c
index 280b7a9bbb..58ef871f68 100644
--- a/hw/openpic.c
+++ b/hw/openpic.c
@@ -713,7 +713,7 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
     DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
     if (addr & 0xF)
         return;
-    addr -= 0x1100;
+    addr -= 0x10;
     addr &= 0xFFFF;
     idx = (addr & 0xFFF0) >> 6;
     addr = addr & 0x30;
@@ -746,7 +746,7 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
     retval = 0xFFFFFFFF;
     if (addr & 0xF)
         return retval;
-    addr -= 0x1100;
+    addr -= 0x10;
     addr &= 0xFFFF;
     idx = (addr & 0xFFF0) >> 6;
     addr = addr & 0x30;
@@ -1361,7 +1361,6 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr,
     if (addr & 0xF)
         return;
 
-    addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_EXT_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1385,7 +1384,6 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr)
     if (addr & 0xF)
         return retval;
 
-    addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_EXT_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1411,7 +1409,6 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr,
     if (addr & 0xF)
         return;
 
-    addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_INT_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1435,7 +1432,6 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr)
     if (addr & 0xF)
         return retval;
 
-    addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_INT_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1461,7 +1457,6 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr,
     if (addr & 0xF)
         return;
 
-    addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_MSG_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1485,7 +1480,6 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr)
     if (addr & 0xF)
         return retval;
 
-    addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_MSG_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1511,7 +1505,6 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr,
     if (addr & 0xF)
         return;
 
-    addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_MSI_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
@@ -1534,7 +1527,6 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr)
     if (addr & 0xF)
         return retval;
 
-    addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1);
     if (addr < MPIC_MSI_REG_SIZE) {
         idx += (addr & 0xFFF0) >> 5;
         if (addr & 0x10) {
diff --git a/hw/pc.c b/hw/pc.c
index 59a7f3928f..83a1b5b32b 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -140,7 +140,7 @@ void cpu_smm_register(cpu_set_smm_t callback, void *arg)
     smm_arg = arg;
 }
 
-void cpu_smm_update(CPUState *env)
+void cpu_smm_update(CPUX86State *env)
 {
     if (smm_set && smm_arg && env == first_cpu)
         smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg);
@@ -148,7 +148,7 @@ void cpu_smm_update(CPUState *env)
 
 
 /* IRQ handling */
-int cpu_get_pic_interrupt(CPUState *env)
+int cpu_get_pic_interrupt(CPUX86State *env)
 {
     int intno;
 
@@ -167,7 +167,7 @@ int cpu_get_pic_interrupt(CPUState *env)
 
 static void pic_irq_request(void *opaque, int irq, int level)
 {
-    CPUState *env = first_cpu;
+    CPUX86State *env = first_cpu;
 
     DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
     if (env->apic_state) {
@@ -335,6 +335,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
 {
     int val, nb, nb_heads, max_track, last_sect, i;
     FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
+    FDriveRate rate;
     BlockDriverState *fd[MAX_FD];
     static pc_cmos_init_late_arg arg;
 
@@ -383,7 +384,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
             if (fd[i] && bdrv_is_inserted(fd[i])) {
                 bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track,
                                               &last_sect, FDRIVE_DRV_NONE,
-                                              &fd_type[i]);
+                                              &fd_type[i], &rate);
             }
         }
     }
@@ -521,7 +522,7 @@ type_init(port92_register_types)
 
 static void handle_a20_line_change(void *opaque, int irq, int level)
 {
-    CPUState *cpu = opaque;
+    CPUX86State *cpu = opaque;
 
     /* XXX: send to all CPUs ? */
     /* XXX: add logic to handle multiple A20 line sources */
@@ -868,7 +869,7 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
     nb_ne2k++;
 }
 
-int cpu_is_bsp(CPUState *env)
+int cpu_is_bsp(CPUX86State *env)
 {
     /* We hard-wire the BSP to the first CPU. */
     return env->cpu_index == 0;
@@ -916,7 +917,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id)
 
 void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
 {
-    CPUState *s = opaque;
+    CPUX86State *s = opaque;
 
     if (level) {
         cpu_interrupt(s, CPU_INTERRUPT_SMI);
@@ -925,15 +926,15 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
 
 static void pc_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUX86State *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->halted = !cpu_is_bsp(env);
 }
 
-static CPUState *pc_new_cpu(const char *cpu_model)
+static CPUX86State *pc_new_cpu(const char *cpu_model)
 {
-    CPUState *env;
+    CPUX86State *env;
 
     env = cpu_init(cpu_model);
     if (!env) {
@@ -1069,7 +1070,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
 
 static void cpu_request_exit(void *opaque, int irq, int level)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
 
     if (env && level) {
         cpu_exit(env);
@@ -1095,7 +1096,13 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
 
     register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
 
-    if (!no_hpet) {
+    /*
+     * Check if an HPET shall be created.
+     *
+     * Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT
+     * when the HPET wants to take over. Thus we have to disable the latter.
+     */
+    if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) {
         hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL);
 
         if (hpet) {
@@ -1111,7 +1118,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
 
     qemu_register_boot_set(pc_boot_set, *rtc_state);
 
-    pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
+    if (kvm_irqchip_in_kernel()) {
+        pit = kvm_pit_init(isa_bus, 0x40);
+    } else {
+        pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
+    }
     if (hpet) {
         /* connect PIT to output control line of the HPET */
         qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(&pit->qdev, 0));
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 5e11d15026..3f99f9a7c2 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -28,8 +28,6 @@
 #include "pc.h"
 #include "apic.h"
 #include "pci.h"
-#include "usb-uhci.h"
-#include "usb-ohci.h"
 #include "net.h"
 #include "boards.h"
 #include "ide.h"
@@ -284,7 +282,7 @@ static void pc_init1(MemoryRegion *system_memory,
                  floppy, idebus[0], idebus[1], rtc_state);
 
     if (pci_enabled && usb_enabled) {
-        usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+        pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
     }
 
     if (pci_enabled && acpi_enabled) {
@@ -384,6 +382,10 @@ static QEMUMachine pc_machine_v1_0 = {
             .driver   = "pc-sysfw",
             .property = "rom_only",
             .value    = stringify(1),
+        }, {
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         { /* end of list */ }
     },
@@ -399,6 +401,10 @@ static QEMUMachine pc_machine_v0_15 = {
             .driver   = "pc-sysfw",
             .property = "rom_only",
             .value    = stringify(1),
+        }, {
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         { /* end of list */ }
     },
@@ -434,6 +440,10 @@ static QEMUMachine pc_machine_v0_14 = {
             .driver   = "virtio-balloon-pci",
             .property = "event_idx",
             .value    = "off",
+        },{
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         {
             .driver   = "pc-sysfw",
@@ -486,6 +496,10 @@ static QEMUMachine pc_machine_v0_13 = {
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
+        },{
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         {
             .driver   = "pc-sysfw",
@@ -542,6 +556,10 @@ static QEMUMachine pc_machine_v0_12 = {
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
+        },{
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         {
             .driver   = "pc-sysfw",
@@ -606,6 +624,10 @@ static QEMUMachine pc_machine_v0_11 = {
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
+        },{
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         {
             .driver   = "pc-sysfw",
@@ -682,6 +704,10 @@ static QEMUMachine pc_machine_v0_10 = {
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
+        },{
+            .driver   = "isa-fdc",
+            .property = "check_media_rate",
+            .value    = "off",
         },
         {
             .driver   = "pc-sysfw",
diff --git a/hw/pci.c b/hw/pci.c
index bf046bfcad..ed8ec99073 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -841,7 +841,6 @@ static int pci_unregister_device(DeviceState *dev)
 
     pci_unregister_io_regions(pci_dev);
     pci_del_option_rom(pci_dev);
-    g_free(pci_dev->romfile);
     do_pci_unregister_device(pci_dev);
     return 0;
 }
diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c
index 98978f89b6..31a434872d 100644
--- a/hw/petalogix_ml605_mmu.c
+++ b/hw/petalogix_ml605_mmu.c
@@ -32,35 +32,30 @@
 #include "sysemu.h"
 #include "devices.h"
 #include "boards.h"
-#include "device_tree.h"
 #include "xilinx.h"
-#include "loader.h"
-#include "elf.h"
 #include "blockdev.h"
 #include "pc.h"
 #include "exec-memory.h"
 
+#include "microblaze_boot.h"
 #include "microblaze_pic_cpu.h"
 #include "xilinx_axidma.h"
 
 #define LMB_BRAM_SIZE  (128 * 1024)
 #define FLASH_SIZE     (32 * 1024 * 1024)
 
-static struct
-{
-    uint32_t bootstrap_pc;
-    uint32_t cmdline;
-    uint32_t fdt;
-} boot_info;
+#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb"
 
-static void main_cpu_reset(void *opaque)
-{
-    CPUState *env = opaque;
+#define MEMORY_BASEADDR 0x50000000
+#define FLASH_BASEADDR 0x86000000
+#define INTC_BASEADDR 0x81800000
+#define TIMER_BASEADDR 0x83c00000
+#define UART16550_BASEADDR 0x83e00000
+#define AXIENET_BASEADDR 0x82780000
+#define AXIDMA_BASEADDR 0x84600000
 
-    cpu_reset(env);
-    env->regs[5] = boot_info.cmdline;
-    env->regs[7] = boot_info.fdt;
-    env->sregs[SR_PC] = boot_info.bootstrap_pc;
+static void machine_cpu_reset(CPUMBState *env)
+{
     env->pvr.regs[10] = 0x0e000000; /* virtex 6 */
     /* setup pvr to match kernel setting */
     env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
@@ -71,70 +66,6 @@ static void main_cpu_reset(void *opaque)
     env->pvr.regs[5] = 0xc56be000;
 }
 
-#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb"
-static int petalogix_load_device_tree(target_phys_addr_t addr,
-                                      uint32_t ramsize,
-                                      target_phys_addr_t initrd_base,
-                                      target_phys_addr_t initrd_size,
-                                      const char *kernel_cmdline)
-{
-    char *path;
-    int fdt_size;
-#ifdef CONFIG_FDT
-    void *fdt;
-    int r;
-
-    /* Try the local "mb.dtb" override.  */
-    fdt = load_device_tree("mb.dtb", &fdt_size);
-    if (!fdt) {
-        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
-        if (path) {
-            fdt = load_device_tree(path, &fdt_size);
-            g_free(path);
-        }
-        if (!fdt) {
-            return 0;
-        }
-    }
-
-    r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
-    if (r < 0) {
-        fprintf(stderr, "couldn't set /chosen/bootargs\n");
-    }
-    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
-#else
-    /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
-       to the kernel.  */
-    fdt_size = load_image_targphys("mb.dtb", addr, 0x10000);
-    if (fdt_size < 0) {
-        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
-        if (path) {
-            fdt_size = load_image_targphys(path, addr, 0x10000);
-            g_free(path);
-        }
-    }
-
-    if (kernel_cmdline) {
-        fprintf(stderr,
-                "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
-    }
-#endif
-    return fdt_size;
-}
-
-static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
-{
-    return addr - 0x30000000LL;
-}
-
-#define MEMORY_BASEADDR 0x50000000
-#define FLASH_BASEADDR 0x86000000
-#define INTC_BASEADDR 0x81800000
-#define TIMER_BASEADDR 0x83c00000
-#define UART16550_BASEADDR 0x83e00000
-#define AXIENET_BASEADDR 0x82780000
-#define AXIDMA_BASEADDR 0x84600000
-
 static void
 petalogix_ml605_init(ram_addr_t ram_size,
                           const char *boot_device,
@@ -144,8 +75,7 @@ petalogix_ml605_init(ram_addr_t ram_size,
 {
     MemoryRegion *address_space_mem = get_system_memory();
     DeviceState *dev;
-    CPUState *env;
-    int kernel_size;
+    CPUMBState *env;
     DriveInfo *dinfo;
     int i;
     target_phys_addr_t ddr_base = MEMORY_BASEADDR;
@@ -159,8 +89,6 @@ petalogix_ml605_init(ram_addr_t ram_size,
     }
     env = cpu_init(cpu_model);
 
-    qemu_register_reset(main_cpu_reset, env);
-
     /* Attach emulated BRAM through the LMB.  */
     memory_region_init_ram(phys_lmb_bram, "petalogix_ml605.lmb_bram",
                            LMB_BRAM_SIZE);
@@ -203,55 +131,9 @@ petalogix_ml605_init(ram_addr_t ram_size,
                                      irq[1], irq[0], 100 * 1000000);
     }
 
-    if (kernel_filename) {
-        uint64_t entry, low, high;
-        uint32_t base32;
-        int big_endian = 0;
-
-#ifdef TARGET_WORDS_BIGENDIAN
-        big_endian = 1;
-#endif
+    microblaze_load_kernel(env, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE,
+                                                            machine_cpu_reset);
 
-        /* Boots a kernel elf binary.  */
-        kernel_size = load_elf(kernel_filename, NULL, NULL,
-                               &entry, &low, &high,
-                               big_endian, ELF_MACHINE, 0);
-        base32 = entry;
-        if (base32 == 0xc0000000) {
-            kernel_size = load_elf(kernel_filename, translate_kernel_address,
-                                   NULL, &entry, NULL, NULL,
-                                   big_endian, ELF_MACHINE, 0);
-        }
-        /* Always boot into physical ram.  */
-        boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff);
-
-        /* If it wasn't an ELF image, try an u-boot image.  */
-        if (kernel_size < 0) {
-            target_phys_addr_t uentry, loadaddr;
-
-            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
-            boot_info.bootstrap_pc = uentry;
-            high = (loadaddr + kernel_size + 3) & ~3;
-        }
-
-        /* Not an ELF image nor an u-boot image, try a RAW image.  */
-        if (kernel_size < 0) {
-            kernel_size = load_image_targphys(kernel_filename, ddr_base,
-                                              ram_size);
-            boot_info.bootstrap_pc = ddr_base;
-            high = (ddr_base + kernel_size + 3) & ~3;
-        }
-
-        boot_info.cmdline = high + 4096;
-        if (kernel_cmdline && strlen(kernel_cmdline)) {
-            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
-        }
-        /* Provide a device-tree.  */
-        boot_info.fdt = boot_info.cmdline + 4096;
-        petalogix_load_device_tree(boot_info.fdt, ram_size,
-                                   0, 0,
-                                   kernel_cmdline);
-    }
 }
 
 static QEMUMachine petalogix_ml605_machine = {
diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c
index d448a41f68..ff154c7aa9 100644
--- a/hw/petalogix_s3adsp1800_mmu.c
+++ b/hw/petalogix_s3adsp1800_mmu.c
@@ -30,87 +30,29 @@
 #include "sysemu.h"
 #include "devices.h"
 #include "boards.h"
-#include "device_tree.h"
 #include "xilinx.h"
-#include "loader.h"
-#include "elf.h"
 #include "blockdev.h"
 #include "exec-memory.h"
 
+#include "microblaze_boot.h"
 #include "microblaze_pic_cpu.h"
 
 #define LMB_BRAM_SIZE  (128 * 1024)
 #define FLASH_SIZE     (16 * 1024 * 1024)
 
-static struct
-{
-    uint32_t bootstrap_pc;
-    uint32_t cmdline;
-    uint32_t fdt;
-} boot_info;
-
-static void main_cpu_reset(void *opaque)
-{
-    CPUState *env = opaque;
-
-    cpu_reset(env);
-    env->regs[5] = boot_info.cmdline;
-    env->regs[7] = boot_info.fdt;
-    env->sregs[SR_PC] = boot_info.bootstrap_pc;
-}
-
 #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb"
-static int petalogix_load_device_tree(target_phys_addr_t addr,
-                                      uint32_t ramsize,
-                                      target_phys_addr_t initrd_base,
-                                      target_phys_addr_t initrd_size,
-                                      const char *kernel_cmdline)
-{
-    char *path;
-    int fdt_size;
-#ifdef CONFIG_FDT
-    void *fdt;
-    int r;
-
-    /* Try the local "mb.dtb" override.  */
-    fdt = load_device_tree("mb.dtb", &fdt_size);
-    if (!fdt) {
-        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
-        if (path) {
-            fdt = load_device_tree(path, &fdt_size);
-            g_free(path);
-        }
-        if (!fdt)
-            return 0;
-    }
 
-    r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
-    if (r < 0)
-        fprintf(stderr, "couldn't set /chosen/bootargs\n");
-    cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
-#else
-    /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
-       to the kernel.  */
-    fdt_size = load_image_targphys("mb.dtb", addr, 0x10000);
-    if (fdt_size < 0) {
-        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
-        if (path) {
-            fdt_size = load_image_targphys(path, addr, 0x10000);
-	    g_free(path);
-        }
-    }
+#define MEMORY_BASEADDR 0x90000000
+#define FLASH_BASEADDR 0xa0000000
+#define INTC_BASEADDR 0x81800000
+#define TIMER_BASEADDR 0x83c00000
+#define UARTLITE_BASEADDR 0x84000000
+#define ETHLITE_BASEADDR 0x81000000
 
-    if (kernel_cmdline) {
-        fprintf(stderr,
-                "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
-    }
-#endif
-    return fdt_size;
-}
-
-static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+static void machine_cpu_reset(CPUMBState *env)
 {
-    return addr - 0x30000000LL;
+    /* FIXME: move to machine specfic cpu reset */
+    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
 }
 
 static void
@@ -121,11 +63,10 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size,
                           const char *initrd_filename, const char *cpu_model)
 {
     DeviceState *dev;
-    CPUState *env;
-    int kernel_size;
+    CPUMBState *env;
     DriveInfo *dinfo;
     int i;
-    target_phys_addr_t ddr_base = 0x90000000;
+    target_phys_addr_t ddr_base = MEMORY_BASEADDR;
     MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
     MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
     qemu_irq irq[32], *cpu_irq;
@@ -137,9 +78,6 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size,
     }
     env = cpu_init(cpu_model);
 
-    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
-    qemu_register_reset(main_cpu_reset, env);
-
     /* Attach emulated BRAM through the LMB.  */
     memory_region_init_ram(phys_lmb_bram,
                            "petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE);
@@ -151,72 +89,25 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size,
     memory_region_add_subregion(sysmem, ddr_base, phys_ram);
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
-    pflash_cfi01_register(0xa0000000,
+    pflash_cfi01_register(FLASH_BASEADDR,
                           NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE,
                           dinfo ? dinfo->bdrv : NULL, (64 * 1024),
                           FLASH_SIZE >> 16,
                           1, 0x89, 0x18, 0x0000, 0x0, 1);
 
     cpu_irq = microblaze_pic_init_cpu(env);
-    dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2);
+    dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 2);
     for (i = 0; i < 32; i++) {
         irq[i] = qdev_get_gpio_in(dev, i);
     }
 
-    sysbus_create_simple("xilinx,uartlite", 0x84000000, irq[3]);
+    sysbus_create_simple("xilinx,uartlite", UARTLITE_BASEADDR, irq[3]);
     /* 2 timers at irq 2 @ 62 Mhz.  */
-    xilinx_timer_create(0x83c00000, irq[0], 2, 62 * 1000000);
-    xilinx_ethlite_create(&nd_table[0], 0x81000000, irq[1], 0, 0);
-
-    if (kernel_filename) {
-        uint64_t entry, low, high;
-        uint32_t base32;
-        int big_endian = 0;
-
-#ifdef TARGET_WORDS_BIGENDIAN
-        big_endian = 1;
-#endif
-
-        /* Boots a kernel elf binary.  */
-        kernel_size = load_elf(kernel_filename, NULL, NULL,
-                               &entry, &low, &high,
-                               big_endian, ELF_MACHINE, 0);
-        base32 = entry;
-        if (base32 == 0xc0000000) {
-            kernel_size = load_elf(kernel_filename, translate_kernel_address,
-                                   NULL, &entry, NULL, NULL,
-                                   big_endian, ELF_MACHINE, 0);
-        }
-        /* Always boot into physical ram.  */
-        boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff);
-
-        /* If it wasn't an ELF image, try an u-boot image.  */
-        if (kernel_size < 0) {
-            target_phys_addr_t uentry, loadaddr;
-
-            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
-            boot_info.bootstrap_pc = uentry;
-            high = (loadaddr + kernel_size + 3) & ~3;
-        }
-
-        /* Not an ELF image nor an u-boot image, try a RAW image.  */
-        if (kernel_size < 0) {
-            kernel_size = load_image_targphys(kernel_filename, ddr_base,
-                                              ram_size);
-            boot_info.bootstrap_pc = ddr_base;
-            high = (ddr_base + kernel_size + 3) & ~3;
-        }
-
-        boot_info.cmdline = high + 4096;
-        if (kernel_cmdline && strlen(kernel_cmdline)) {
-            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
-        }
-        /* Provide a device-tree.  */
-        boot_info.fdt = boot_info.cmdline + 4096; 
-        petalogix_load_device_tree(boot_info.fdt, ram_size,
-                                   0, 0,
-                                   kernel_cmdline);
-    }
+    xilinx_timer_create(TIMER_BASEADDR, irq[0], 2, 62 * 1000000);
+    xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0);
+
+    microblaze_load_kernel(env, ddr_base, ram_size,
+                    BINARY_DEVICE_TREE_FILE, machine_cpu_reset);
 }
 
 static QEMUMachine petalogix_s3adsp1800_machine = {
diff --git a/hw/ppc.c b/hw/ppc.c
index 59882e2ecd..98546de991 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -47,10 +47,10 @@
 #  define LOG_TB(...) do { } while (0)
 #endif
 
-static void cpu_ppc_tb_stop (CPUState *env);
-static void cpu_ppc_tb_start (CPUState *env);
+static void cpu_ppc_tb_stop (CPUPPCState *env);
+static void cpu_ppc_tb_start (CPUPPCState *env);
 
-void ppc_set_irq(CPUState *env, int n_IRQ, int level)
+void ppc_set_irq(CPUPPCState *env, int n_IRQ, int level)
 {
     unsigned int old_pending = env->pending_interrupts;
 
@@ -77,7 +77,7 @@ void ppc_set_irq(CPUState *env, int n_IRQ, int level)
 /* PowerPC 6xx / 7xx internal IRQ controller */
 static void ppc6xx_set_irq (void *opaque, int pin, int level)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     int cur_level;
 
     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
@@ -131,13 +131,7 @@ static void ppc6xx_set_irq (void *opaque, int pin, int level)
             /* Level sensitive - active low */
             if (level) {
                 LOG_IRQ("%s: reset the CPU\n", __func__);
-                env->interrupt_request |= CPU_INTERRUPT_EXITTB;
-                /* XXX: TOFIX */
-#if 0
-                cpu_reset(env);
-#else
-                qemu_system_reset_request();
-#endif
+                cpu_interrupt(env, CPU_INTERRUPT_RESET);
             }
             break;
         case PPC6xx_INPUT_SRESET:
@@ -157,7 +151,7 @@ static void ppc6xx_set_irq (void *opaque, int pin, int level)
     }
 }
 
-void ppc6xx_irq_init (CPUState *env)
+void ppc6xx_irq_init (CPUPPCState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env,
                                                   PPC6xx_INPUT_NB);
@@ -167,7 +161,7 @@ void ppc6xx_irq_init (CPUState *env)
 /* PowerPC 970 internal IRQ controller */
 static void ppc970_set_irq (void *opaque, int pin, int level)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     int cur_level;
 
     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
@@ -214,10 +208,7 @@ static void ppc970_set_irq (void *opaque, int pin, int level)
         case PPC970_INPUT_HRESET:
             /* Level sensitive - active low */
             if (level) {
-#if 0 // XXX: TOFIX
-                LOG_IRQ("%s: reset the CPU\n", __func__);
-                cpu_reset(env);
-#endif
+                cpu_interrupt(env, CPU_INTERRUPT_RESET);
             }
             break;
         case PPC970_INPUT_SRESET:
@@ -242,7 +233,7 @@ static void ppc970_set_irq (void *opaque, int pin, int level)
     }
 }
 
-void ppc970_irq_init (CPUState *env)
+void ppc970_irq_init (CPUPPCState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env,
                                                   PPC970_INPUT_NB);
@@ -251,7 +242,7 @@ void ppc970_irq_init (CPUState *env)
 /* POWER7 internal IRQ controller */
 static void power7_set_irq (void *opaque, int pin, int level)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
 
     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
                 env, pin, level);
@@ -275,7 +266,7 @@ static void power7_set_irq (void *opaque, int pin, int level)
     }
 }
 
-void ppcPOWER7_irq_init (CPUState *env)
+void ppcPOWER7_irq_init (CPUPPCState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, env,
                                                   POWER7_INPUT_NB);
@@ -285,7 +276,7 @@ void ppcPOWER7_irq_init (CPUState *env)
 /* PowerPC 40x internal IRQ controller */
 static void ppc40x_set_irq (void *opaque, int pin, int level)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     int cur_level;
 
     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
@@ -355,7 +346,7 @@ static void ppc40x_set_irq (void *opaque, int pin, int level)
     }
 }
 
-void ppc40x_irq_init (CPUState *env)
+void ppc40x_irq_init (CPUPPCState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq,
                                                   env, PPC40x_INPUT_NB);
@@ -364,7 +355,7 @@ void ppc40x_irq_init (CPUState *env)
 /* PowerPC E500 internal IRQ controller */
 static void ppce500_set_irq (void *opaque, int pin, int level)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     int cur_level;
 
     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
@@ -416,7 +407,7 @@ static void ppce500_set_irq (void *opaque, int pin, int level)
     }
 }
 
-void ppce500_irq_init (CPUState *env)
+void ppce500_irq_init (CPUPPCState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq,
                                         env, PPCE500_INPUT_NB);
@@ -430,7 +421,7 @@ uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
     return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
 }
 
-uint64_t cpu_ppc_load_tbl (CPUState *env)
+uint64_t cpu_ppc_load_tbl (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -445,7 +436,7 @@ uint64_t cpu_ppc_load_tbl (CPUState *env)
     return tb;
 }
 
-static inline uint32_t _cpu_ppc_load_tbu(CPUState *env)
+static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -456,7 +447,7 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUState *env)
     return tb >> 32;
 }
 
-uint32_t cpu_ppc_load_tbu (CPUState *env)
+uint32_t cpu_ppc_load_tbu (CPUPPCState *env)
 {
     if (kvm_enabled()) {
         return env->spr[SPR_TBU];
@@ -473,7 +464,7 @@ static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
                 __func__, value, *tb_offsetp);
 }
 
-void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
+void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -484,7 +475,7 @@ void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
                      &tb_env->tb_offset, tb | (uint64_t)value);
 }
 
-static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value)
+static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -495,12 +486,12 @@ static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value)
                      &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
 }
 
-void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
+void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value)
 {
     _cpu_ppc_store_tbu(env, value);
 }
 
-uint64_t cpu_ppc_load_atbl (CPUState *env)
+uint64_t cpu_ppc_load_atbl (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -511,7 +502,7 @@ uint64_t cpu_ppc_load_atbl (CPUState *env)
     return tb;
 }
 
-uint32_t cpu_ppc_load_atbu (CPUState *env)
+uint32_t cpu_ppc_load_atbu (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -522,7 +513,7 @@ uint32_t cpu_ppc_load_atbu (CPUState *env)
     return tb >> 32;
 }
 
-void cpu_ppc_store_atbl (CPUState *env, uint32_t value)
+void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -533,7 +524,7 @@ void cpu_ppc_store_atbl (CPUState *env, uint32_t value)
                      &tb_env->atb_offset, tb | (uint64_t)value);
 }
 
-void cpu_ppc_store_atbu (CPUState *env, uint32_t value)
+void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb;
@@ -544,7 +535,7 @@ void cpu_ppc_store_atbu (CPUState *env, uint32_t value)
                      &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
 }
 
-static void cpu_ppc_tb_stop (CPUState *env)
+static void cpu_ppc_tb_stop (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb, atb, vmclk;
@@ -566,7 +557,7 @@ static void cpu_ppc_tb_stop (CPUState *env)
     }
 }
 
-static void cpu_ppc_tb_start (CPUState *env)
+static void cpu_ppc_tb_start (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t tb, atb, vmclk;
@@ -587,7 +578,7 @@ static void cpu_ppc_tb_start (CPUState *env)
     }
 }
 
-static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
+static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint32_t decr;
@@ -606,7 +597,7 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
     return decr;
 }
 
-uint32_t cpu_ppc_load_decr (CPUState *env)
+uint32_t cpu_ppc_load_decr (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
 
@@ -617,14 +608,14 @@ uint32_t cpu_ppc_load_decr (CPUState *env)
     return _cpu_ppc_load_decr(env, tb_env->decr_next);
 }
 
-uint32_t cpu_ppc_load_hdecr (CPUState *env)
+uint32_t cpu_ppc_load_hdecr (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
 
     return _cpu_ppc_load_decr(env, tb_env->hdecr_next);
 }
 
-uint64_t cpu_ppc_load_purr (CPUState *env)
+uint64_t cpu_ppc_load_purr (CPUPPCState *env)
 {
     ppc_tb_t *tb_env = env->tb_env;
     uint64_t diff;
@@ -637,23 +628,23 @@ uint64_t cpu_ppc_load_purr (CPUState *env)
 /* When decrementer expires,
  * all we need to do is generate or queue a CPU exception
  */
-static inline void cpu_ppc_decr_excp(CPUState *env)
+static inline void cpu_ppc_decr_excp(CPUPPCState *env)
 {
     /* Raise it */
     LOG_TB("raise decrementer exception\n");
     ppc_set_irq(env, PPC_INTERRUPT_DECR, 1);
 }
 
-static inline void cpu_ppc_hdecr_excp(CPUState *env)
+static inline void cpu_ppc_hdecr_excp(CPUPPCState *env)
 {
     /* Raise it */
     LOG_TB("raise decrementer exception\n");
     ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1);
 }
 
-static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
+static void __cpu_ppc_store_decr (CPUPPCState *env, uint64_t *nextp,
                                   struct QEMUTimer *timer,
-                                  void (*raise_excp)(CPUState *),
+                                  void (*raise_excp)(CPUPPCState *),
                                   uint32_t decr, uint32_t value,
                                   int is_excp)
 {
@@ -690,7 +681,7 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
     }
 }
 
-static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
+static inline void _cpu_ppc_store_decr(CPUPPCState *env, uint32_t decr,
                                        uint32_t value, int is_excp)
 {
     ppc_tb_t *tb_env = env->tb_env;
@@ -699,7 +690,7 @@ static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
                          &cpu_ppc_decr_excp, decr, value, is_excp);
 }
 
-void cpu_ppc_store_decr (CPUState *env, uint32_t value)
+void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value)
 {
     _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
 }
@@ -709,7 +700,7 @@ static void cpu_ppc_decr_cb (void *opaque)
     _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
 }
 
-static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr,
+static inline void _cpu_ppc_store_hdecr(CPUPPCState *env, uint32_t hdecr,
                                         uint32_t value, int is_excp)
 {
     ppc_tb_t *tb_env = env->tb_env;
@@ -720,7 +711,7 @@ static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr,
     }
 }
 
-void cpu_ppc_store_hdecr (CPUState *env, uint32_t value)
+void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value)
 {
     _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0);
 }
@@ -730,7 +721,7 @@ static void cpu_ppc_hdecr_cb (void *opaque)
     _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1);
 }
 
-void cpu_ppc_store_purr (CPUState *env, uint64_t value)
+void cpu_ppc_store_purr (CPUPPCState *env, uint64_t value)
 {
     ppc_tb_t *tb_env = env->tb_env;
 
@@ -740,7 +731,7 @@ void cpu_ppc_store_purr (CPUState *env, uint64_t value)
 
 static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     ppc_tb_t *tb_env = env->tb_env;
 
     tb_env->tb_freq = freq;
@@ -755,7 +746,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
 }
 
 /* Set up (once) timebase frequency (in Hz) */
-clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
+clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
 {
     ppc_tb_t *tb_env;
 
@@ -778,28 +769,28 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
 
 /* Specific helpers for POWER & PowerPC 601 RTC */
 #if 0
-static clk_setup_cb cpu_ppc601_rtc_init (CPUState *env)
+static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env)
 {
     return cpu_ppc_tb_init(env, 7812500);
 }
 #endif
 
-void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value)
+void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value)
 {
     _cpu_ppc_store_tbu(env, value);
 }
 
-uint32_t cpu_ppc601_load_rtcu (CPUState *env)
+uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env)
 {
     return _cpu_ppc_load_tbu(env);
 }
 
-void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value)
+void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value)
 {
     cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
 }
 
-uint32_t cpu_ppc601_load_rtcl (CPUState *env)
+uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env)
 {
     return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
 }
@@ -823,7 +814,7 @@ struct ppc40x_timer_t {
 /* Fixed interval timer */
 static void cpu_4xx_fit_cb (void *opaque)
 {
-    CPUState *env;
+    CPUPPCState *env;
     ppc_tb_t *tb_env;
     ppc40x_timer_t *ppc40x_timer;
     uint64_t now, next;
@@ -862,7 +853,7 @@ static void cpu_4xx_fit_cb (void *opaque)
 }
 
 /* Programmable interval timer */
-static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
+static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp)
 {
     ppc40x_timer_t *ppc40x_timer;
     uint64_t now, next;
@@ -891,7 +882,7 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
 
 static void cpu_4xx_pit_cb (void *opaque)
 {
-    CPUState *env;
+    CPUPPCState *env;
     ppc_tb_t *tb_env;
     ppc40x_timer_t *ppc40x_timer;
 
@@ -913,7 +904,7 @@ static void cpu_4xx_pit_cb (void *opaque)
 /* Watchdog timer */
 static void cpu_4xx_wdt_cb (void *opaque)
 {
-    CPUState *env;
+    CPUPPCState *env;
     ppc_tb_t *tb_env;
     ppc40x_timer_t *ppc40x_timer;
     uint64_t now, next;
@@ -978,7 +969,7 @@ static void cpu_4xx_wdt_cb (void *opaque)
     }
 }
 
-void store_40x_pit (CPUState *env, target_ulong val)
+void store_40x_pit (CPUPPCState *env, target_ulong val)
 {
     ppc_tb_t *tb_env;
     ppc40x_timer_t *ppc40x_timer;
@@ -990,14 +981,14 @@ void store_40x_pit (CPUState *env, target_ulong val)
     start_stop_pit(env, tb_env, 0);
 }
 
-target_ulong load_40x_pit (CPUState *env)
+target_ulong load_40x_pit (CPUPPCState *env)
 {
     return cpu_ppc_load_decr(env);
 }
 
 static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     ppc_tb_t *tb_env = env->tb_env;
 
     LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__,
@@ -1007,7 +998,7 @@ static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
     /* XXX: we should also update all timers */
 }
 
-clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
                                   unsigned int decr_excp)
 {
     ppc_tb_t *tb_env;
@@ -1093,7 +1084,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
     return -1;
 }
 
-int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
+int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque,
                       dcr_read_cb dcr_read, dcr_write_cb dcr_write)
 {
     ppc_dcr_t *dcr_env;
@@ -1116,7 +1107,7 @@ int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
     return 0;
 }
 
-int ppc_dcr_init (CPUState *env, int (*read_error)(int dcrn),
+int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn),
                   int (*write_error)(int dcrn))
 {
     ppc_dcr_t *dcr_env;
diff --git a/hw/ppc.h b/hw/ppc.h
index 9f911704af..2f3ea277bc 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -1,4 +1,4 @@
-void ppc_set_irq (CPUState *env, int n_IRQ, int level);
+void ppc_set_irq (CPUPPCState *env, int n_IRQ, int level);
 
 /* PowerPC hardware exceptions management helpers */
 typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
@@ -43,32 +43,32 @@ struct ppc_tb_t {
                                                */
 
 uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset);
-clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
+clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq);
 /* Embedded PowerPC DCR management */
 typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn);
 typedef void (*dcr_write_cb)(void *opaque, int dcrn, uint32_t val);
-int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
+int ppc_dcr_init (CPUPPCState *env, int (*dcr_read_error)(int dcrn),
                   int (*dcr_write_error)(int dcrn));
-int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
+int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque,
                       dcr_read_cb drc_read, dcr_write_cb dcr_write);
-clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
                                   unsigned int decr_excp);
 
 /* Embedded PowerPC reset */
-void ppc40x_core_reset (CPUState *env);
-void ppc40x_chip_reset (CPUState *env);
-void ppc40x_system_reset (CPUState *env);
+void ppc40x_core_reset (CPUPPCState *env);
+void ppc40x_chip_reset (CPUPPCState *env);
+void ppc40x_system_reset (CPUPPCState *env);
 void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val);
 
 extern CPUWriteMemoryFunc * const PPC_io_write[];
 extern CPUReadMemoryFunc * const PPC_io_read[];
 void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val);
 
-void ppc40x_irq_init (CPUState *env);
-void ppce500_irq_init (CPUState *env);
-void ppc6xx_irq_init (CPUState *env);
-void ppc970_irq_init (CPUState *env);
-void ppcPOWER7_irq_init (CPUState *env);
+void ppc40x_irq_init (CPUPPCState *env);
+void ppce500_irq_init (CPUPPCState *env);
+void ppc6xx_irq_init (CPUPPCState *env);
+void ppc970_irq_init (CPUPPCState *env);
+void ppcPOWER7_irq_init (CPUPPCState *env);
 
 /* PPC machines for OpenBIOS */
 enum {
@@ -89,4 +89,4 @@ enum {
 #define PPC_SERIAL_MM_BAUDBASE 399193
 
 /* ppc_booke.c */
-void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags);
+void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags);
diff --git a/hw/ppc405.h b/hw/ppc405.h
index d8fdf0930a..1f5dc5fd36 100644
--- a/hw/ppc405.h
+++ b/hw/ppc405.h
@@ -56,23 +56,23 @@ struct ppc4xx_bd_info_t {
 };
 
 /* PowerPC 405 core */
-ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
+ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
                                 uint32_t flags);
 
-CPUState *ppc405cr_init(MemoryRegion *address_space_mem,
+CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem,
                         MemoryRegion ram_memories[4],
                         target_phys_addr_t ram_bases[4],
                         target_phys_addr_t ram_sizes[4],
                         uint32_t sysclk, qemu_irq **picp,
                         int do_init);
-CPUState *ppc405ep_init(MemoryRegion *address_space_mem,
+CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
                         MemoryRegion ram_memories[2],
                         target_phys_addr_t ram_bases[2],
                         target_phys_addr_t ram_sizes[2],
                         uint32_t sysclk, qemu_irq **picp,
                         int do_init);
 /* IBM STBxxx microcontrollers */
-CPUState *ppc_stb025_init (MemoryRegion ram_memories[2],
+CPUPPCState *ppc_stb025_init (MemoryRegion ram_memories[2],
                            target_phys_addr_t ram_bases[2],
                            target_phys_addr_t ram_sizes[2],
                            uint32_t sysclk, qemu_irq **picp,
diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c
index 98079fa23f..89e5013b57 100644
--- a/hw/ppc405_uc.c
+++ b/hw/ppc405_uc.c
@@ -41,7 +41,7 @@
 #define DEBUG_CLOCKS
 //#define DEBUG_CLOCKS_LL
 
-ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
+ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
                                 uint32_t flags)
 {
     ram_addr_t bdloc;
@@ -169,7 +169,7 @@ static void ppc4xx_plb_reset (void *opaque)
     plb->besr = 0x00000000;
 }
 
-static void ppc4xx_plb_init(CPUState *env)
+static void ppc4xx_plb_init(CPUPPCState *env)
 {
     ppc4xx_plb_t *plb;
 
@@ -245,7 +245,7 @@ static void ppc4xx_pob_reset (void *opaque)
     pob->besr[1] = 0x0000000;
 }
 
-static void ppc4xx_pob_init(CPUState *env)
+static void ppc4xx_pob_init(CPUPPCState *env)
 {
     ppc4xx_pob_t *pob;
 
@@ -574,7 +574,7 @@ static void ebc_reset (void *opaque)
     ebc->cfg = 0x80400000;
 }
 
-static void ppc405_ebc_init(CPUState *env)
+static void ppc405_ebc_init(CPUPPCState *env)
 {
     ppc4xx_ebc_t *ebc;
 
@@ -657,7 +657,7 @@ static void ppc405_dma_reset (void *opaque)
     dma->pol = 0x00000000;
 }
 
-static void ppc405_dma_init(CPUState *env, qemu_irq irqs[4])
+static void ppc405_dma_init(CPUPPCState *env, qemu_irq irqs[4])
 {
     ppc405_dma_t *dma;
 
@@ -960,7 +960,7 @@ static void ocm_reset (void *opaque)
     ocm->dsacntl = dsacntl;
 }
 
-static void ppc405_ocm_init(CPUState *env)
+static void ppc405_ocm_init(CPUPPCState *env)
 {
     ppc405_ocm_t *ocm;
 
@@ -1713,7 +1713,7 @@ static void ppc40x_mal_reset (void *opaque)
     mal->txeobisr = 0x00000000;
 }
 
-static void ppc405_mal_init(CPUState *env, qemu_irq irqs[4])
+static void ppc405_mal_init(CPUPPCState *env, qemu_irq irqs[4])
 {
     ppc40x_mal_t *mal;
     int i;
@@ -1764,36 +1764,24 @@ static void ppc405_mal_init(CPUState *env, qemu_irq irqs[4])
 
 /*****************************************************************************/
 /* SPR */
-void ppc40x_core_reset (CPUState *env)
+void ppc40x_core_reset (CPUPPCState *env)
 {
     target_ulong dbsr;
 
     printf("Reset PowerPC core\n");
-    env->interrupt_request |= CPU_INTERRUPT_EXITTB;
-    /* XXX: TOFIX */
-#if 0
-    cpu_reset(env);
-#else
-    qemu_system_reset_request();
-#endif
+    cpu_interrupt(env, CPU_INTERRUPT_RESET);
     dbsr = env->spr[SPR_40x_DBSR];
     dbsr &= ~0x00000300;
     dbsr |= 0x00000100;
     env->spr[SPR_40x_DBSR] = dbsr;
 }
 
-void ppc40x_chip_reset (CPUState *env)
+void ppc40x_chip_reset (CPUPPCState *env)
 {
     target_ulong dbsr;
 
     printf("Reset PowerPC chip\n");
-    env->interrupt_request |= CPU_INTERRUPT_EXITTB;
-    /* XXX: TOFIX */
-#if 0
-    cpu_reset(env);
-#else
-    qemu_system_reset_request();
-#endif
+    cpu_interrupt(env, CPU_INTERRUPT_RESET);
     /* XXX: TODO reset all internal peripherals */
     dbsr = env->spr[SPR_40x_DBSR];
     dbsr &= ~0x00000300;
@@ -1801,13 +1789,13 @@ void ppc40x_chip_reset (CPUState *env)
     env->spr[SPR_40x_DBSR] = dbsr;
 }
 
-void ppc40x_system_reset (CPUState *env)
+void ppc40x_system_reset (CPUPPCState *env)
 {
     printf("Reset PowerPC system\n");
     qemu_system_reset_request();
 }
 
-void store_40x_dbcr0 (CPUState *env, uint32_t val)
+void store_40x_dbcr0 (CPUPPCState *env, uint32_t val)
 {
     switch ((val >> 28) & 0x3) {
     case 0x0:
@@ -2078,7 +2066,7 @@ static void ppc405cr_clk_init (ppc405cr_cpc_t *cpc)
     cpc->psr |= D << 17;
 }
 
-static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7],
+static void ppc405cr_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[7],
                                uint32_t sysclk)
 {
     ppc405cr_cpc_t *cpc;
@@ -2108,7 +2096,7 @@ static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7],
     qemu_register_reset(ppc405cr_cpc_reset, cpc);
 }
 
-CPUState *ppc405cr_init(MemoryRegion *address_space_mem,
+CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem,
                         MemoryRegion ram_memories[4],
                         target_phys_addr_t ram_bases[4],
                         target_phys_addr_t ram_sizes[4],
@@ -2117,7 +2105,7 @@ CPUState *ppc405cr_init(MemoryRegion *address_space_mem,
 {
     clk_setup_t clk_setup[PPC405CR_CLK_NB];
     qemu_irq dma_irqs[4];
-    CPUState *env;
+    CPUPPCState *env;
     qemu_irq *pic, *irqs;
 
     memset(clk_setup, 0, sizeof(clk_setup));
@@ -2420,7 +2408,7 @@ static void ppc405ep_cpc_reset (void *opaque)
 }
 
 /* XXX: sysclk should be between 25 and 100 MHz */
-static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8],
+static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8],
                                uint32_t sysclk)
 {
     ppc405ep_cpc_t *cpc;
@@ -2457,7 +2445,7 @@ static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8],
 #endif
 }
 
-CPUState *ppc405ep_init(MemoryRegion *address_space_mem,
+CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
                         MemoryRegion ram_memories[2],
                         target_phys_addr_t ram_bases[2],
                         target_phys_addr_t ram_sizes[2],
@@ -2466,7 +2454,7 @@ CPUState *ppc405ep_init(MemoryRegion *address_space_mem,
 {
     clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup;
     qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4];
-    CPUState *env;
+    CPUPPCState *env;
     qemu_irq *pic, *irqs;
 
     memset(clk_setup, 0, sizeof(clk_setup));
@@ -2483,6 +2471,8 @@ CPUState *ppc405ep_init(MemoryRegion *address_space_mem,
     ppc4xx_pob_init(env);
     /* OBP arbitrer */
     ppc4xx_opba_init(0xef600600);
+    /* Initialize timers */
+    ppc_booke_timers_init(env, sysclk, 0);
     /* Universal interrupt controller */
     irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
     irqs[PPCUIC_OUTPUT_INT] =
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index f86b16838a..220c81d06e 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -121,7 +121,7 @@ out:
 }
 
 /* Create reset TLB entries for BookE, spanning the 32bit addr space.  */
-static void mmubooke_create_initial_mapping(CPUState *env,
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
                                      target_ulong va,
                                      target_phys_addr_t pa)
 {
@@ -145,9 +145,9 @@ static void mmubooke_create_initial_mapping(CPUState *env,
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->gpr[1] = (16<<20) - 8;
     env->gpr[3] = FDT_ADDR;
     env->nip = entry;
@@ -172,7 +172,7 @@ static void bamboo_init(ram_addr_t ram_size,
     qemu_irq *pic;
     qemu_irq *irqs;
     PCIBus *pcibus;
-    CPUState *env;
+    CPUPPCState *env;
     uint64_t elf_entry;
     uint64_t elf_lowaddr;
     target_phys_addr_t loadaddr = 0;
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
index f969e44e1b..b511020aeb 100644
--- a/hw/ppc4xx.h
+++ b/hw/ppc4xx.h
@@ -28,7 +28,7 @@
 #include "pci.h"
 
 /* PowerPC 4xx core initialization */
-CPUState *ppc4xx_init (const char *cpu_model,
+CPUPPCState *ppc4xx_init (const char *cpu_model,
                        clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
                        uint32_t sysclk);
 
@@ -38,7 +38,7 @@ enum {
     PPCUIC_OUTPUT_CINT = 1,
     PPCUIC_OUTPUT_NB,
 };
-qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
+qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs,
                        uint32_t dcr_base, int has_ssr, int has_vr);
 
 ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
@@ -47,13 +47,13 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
                                target_phys_addr_t ram_sizes[],
                                const unsigned int sdram_bank_sizes[]);
 
-void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
+void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
                         MemoryRegion ram_memories[],
                         target_phys_addr_t *ram_bases,
                         target_phys_addr_t *ram_sizes,
                         int do_init);
 
-PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+PCIBus *ppc4xx_pci_init(CPUPPCState *env, qemu_irq pci_irqs[4],
                         target_phys_addr_t config_space,
                         target_phys_addr_t int_ack,
                         target_phys_addr_t special_cycle,
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
index 26040ac3ad..00e36f4109 100644
--- a/hw/ppc4xx_devs.c
+++ b/hw/ppc4xx_devs.c
@@ -38,13 +38,20 @@
 #  define LOG_UIC(...) do { } while (0)
 #endif
 
+static void ppc4xx_reset(void *opaque)
+{
+    CPUPPCState *env = opaque;
+
+    cpu_state_reset(env);
+}
+
 /*****************************************************************************/
 /* Generic PowerPC 4xx processor instantiation */
-CPUState *ppc4xx_init (const char *cpu_model,
+CPUPPCState *ppc4xx_init (const char *cpu_model,
                        clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
                        uint32_t sysclk)
 {
-    CPUState *env;
+    CPUPPCState *env;
 
     /* init CPUs */
     env = cpu_init(cpu_model);
@@ -60,7 +67,7 @@ CPUState *ppc4xx_init (const char *cpu_model,
     tb_clk->opaque = env;
     ppc_dcr_init(env, NULL, NULL);
     /* Register qemu callbacks */
-    qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+    qemu_register_reset(ppc4xx_reset, env);
 
     return env;
 }
@@ -288,7 +295,7 @@ static void ppcuic_reset (void *opaque)
     }
 }
 
-qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
+qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs,
                        uint32_t dcr_base, int has_ssr, int has_vr)
 {
     ppcuic_t *uic;
@@ -634,7 +641,7 @@ static void sdram_reset (void *opaque)
     sdram->cfg = 0x00800000;
 }
 
-void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
+void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
                         MemoryRegion *ram_memories,
                         target_phys_addr_t *ram_bases,
                         target_phys_addr_t *ram_sizes,
diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c
index 88719458b0..d51e7fad67 100644
--- a/hw/ppc_booke.c
+++ b/hw/ppc_booke.c
@@ -71,7 +71,7 @@ struct booke_timer_t {
     uint32_t flags;
 };
 
-static void booke_update_irq(CPUState *env)
+static void booke_update_irq(CPUPPCState *env)
 {
     ppc_set_irq(env, PPC_INTERRUPT_DECR,
                 (env->spr[SPR_BOOKE_TSR] & TSR_DIS
@@ -88,7 +88,7 @@ static void booke_update_irq(CPUState *env)
 
 /* Return the location of the bit of time base at which the FIT will raise an
    interrupt */
-static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env)
+static uint8_t booke_get_fit_target(CPUPPCState *env, ppc_tb_t *tb_env)
 {
     uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
 
@@ -106,7 +106,7 @@ static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env)
 
 /* Return the location of the bit of time base at which the WDT will raise an
    interrupt */
-static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env)
+static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env)
 {
     uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
 
@@ -122,7 +122,7 @@ static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env)
     return wp;
 }
 
-static void booke_update_fixed_timer(CPUState         *env,
+static void booke_update_fixed_timer(CPUPPCState         *env,
                                      uint8_t           target_bit,
                                      uint64_t          *next,
                                      struct QEMUTimer *timer)
@@ -153,7 +153,7 @@ static void booke_update_fixed_timer(CPUState         *env,
 
 static void booke_decr_cb(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
 
     env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
     booke_update_irq(env);
@@ -166,7 +166,7 @@ static void booke_decr_cb(void *opaque)
 
 static void booke_fit_cb(void *opaque)
 {
-    CPUState *env;
+    CPUPPCState *env;
     ppc_tb_t *tb_env;
     booke_timer_t *booke_timer;
 
@@ -185,7 +185,7 @@ static void booke_fit_cb(void *opaque)
 
 static void booke_wdt_cb(void *opaque)
 {
-    CPUState *env;
+    CPUPPCState *env;
     ppc_tb_t *tb_env;
     booke_timer_t *booke_timer;
 
@@ -203,13 +203,13 @@ static void booke_wdt_cb(void *opaque)
                              booke_timer->wdt_timer);
 }
 
-void store_booke_tsr(CPUState *env, target_ulong val)
+void store_booke_tsr(CPUPPCState *env, target_ulong val)
 {
     env->spr[SPR_BOOKE_TSR] &= ~val;
     booke_update_irq(env);
 }
 
-void store_booke_tcr(CPUState *env, target_ulong val)
+void store_booke_tcr(CPUPPCState *env, target_ulong val)
 {
     ppc_tb_t *tb_env = env->tb_env;
     booke_timer_t *booke_timer = tb_env->opaque;
@@ -231,7 +231,7 @@ void store_booke_tcr(CPUState *env, target_ulong val)
 
 }
 
-void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags)
+void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags)
 {
     ppc_tb_t *tb_env;
     booke_timer_t *booke_timer;
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index 506187b182..879651018b 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -54,7 +54,6 @@
 #include "nvram.h"
 #include "pc.h"
 #include "pci.h"
-#include "usb-ohci.h"
 #include "net.h"
 #include "sysemu.h"
 #include "boards.h"
@@ -122,6 +121,13 @@ static target_phys_addr_t round_page(target_phys_addr_t addr)
     return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
 }
 
+static void ppc_core99_reset(void *opaque)
+{
+    CPUPPCState *env = opaque;
+
+    cpu_state_reset(env);
+}
+
 /* PowerPC Mac99 hardware initialisation */
 static void ppc_core99_init (ram_addr_t ram_size,
                              const char *boot_device,
@@ -130,7 +136,7 @@ static void ppc_core99_init (ram_addr_t ram_size,
                              const char *initrd_filename,
                              const char *cpu_model)
 {
-    CPUState *env = NULL;
+    CPUPPCState *env = NULL;
     char *filename;
     qemu_irq *pic, **openpic_irqs;
     MemoryRegion *unin_memory = g_new(MemoryRegion, 1);
@@ -167,7 +173,7 @@ static void ppc_core99_init (ram_addr_t ram_size,
         }
         /* Set time-base frequency to 100 Mhz */
         cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
-        qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+        qemu_register_reset(ppc_core99_reset, env);
     }
 
     /* allocate RAM */
@@ -352,7 +358,7 @@ static void ppc_core99_init (ram_addr_t ram_size,
                dbdma_mem, cuda_mem, NULL, 3, ide_mem, escc_bar);
 
     if (usb_enabled) {
-        usb_ohci_init_pci(pci_bus, -1);
+        pci_create_simple(pci_bus, -1, "pci-ohci");
     }
 
     /* U3 needs to use USB for input because Linux doesn't support via-cuda
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 9295a34f59..7e73d37c34 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -34,7 +34,6 @@
 #include "net.h"
 #include "isa.h"
 #include "pci.h"
-#include "usb-ohci.h"
 #include "boards.h"
 #include "fw_cfg.h"
 #include "escc.h"
@@ -66,6 +65,13 @@ static target_phys_addr_t round_page(target_phys_addr_t addr)
     return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
 }
 
+static void ppc_heathrow_reset(void *opaque)
+{
+    CPUPPCState *env = opaque;
+
+    cpu_state_reset(env);
+}
+
 static void ppc_heathrow_init (ram_addr_t ram_size,
                                const char *boot_device,
                                const char *kernel_filename,
@@ -74,7 +80,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
                                const char *cpu_model)
 {
     MemoryRegion *sysmem = get_system_memory();
-    CPUState *env = NULL;
+    CPUPPCState *env = NULL;
     char *filename;
     qemu_irq *pic, **heathrow_irqs;
     int linux_boot, i;
@@ -105,7 +111,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
         }
         /* Set time-base frequency to 16.6 Mhz */
         cpu_ppc_tb_init(env,  16600000UL);
-        qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+        qemu_register_reset(ppc_heathrow_reset, env);
     }
 
     /* allocate RAM */
@@ -278,7 +284,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
                dbdma_mem, cuda_mem, nvr, 2, ide_mem, escc_bar);
 
     if (usb_enabled) {
-        usb_ohci_init_pci(pci_bus, -1);
+        pci_create_simple(pci_bus, -1, "pci-ohci");
     }
 
     if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index eb43fb5849..06d589d97b 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -30,7 +30,6 @@
 #include "isa.h"
 #include "pci.h"
 #include "pci_host.h"
-#include "usb-ohci.h"
 #include "ppc.h"
 #include "boards.h"
 #include "qemu-log.h"
@@ -464,13 +463,20 @@ static const MemoryRegionOps PPC_prep_io_ops = {
 
 static void cpu_request_exit(void *opaque, int irq, int level)
 {
-    CPUState *env = cpu_single_env;
+    CPUPPCState *env = cpu_single_env;
 
     if (env && level) {
         cpu_exit(env);
     }
 }
 
+static void ppc_prep_reset(void *opaque)
+{
+    CPUPPCState *env = opaque;
+
+    cpu_state_reset(env);
+}
+
 /* PowerPC PREP hardware initialisation */
 static void ppc_prep_init (ram_addr_t ram_size,
                            const char *boot_device,
@@ -480,7 +486,7 @@ static void ppc_prep_init (ram_addr_t ram_size,
                            const char *cpu_model)
 {
     MemoryRegion *sysmem = get_system_memory();
-    CPUState *env = NULL;
+    CPUPPCState *env = NULL;
     char *filename;
     nvram_t nvram;
     M48t59State *m48t59;
@@ -525,7 +531,7 @@ static void ppc_prep_init (ram_addr_t ram_size,
             /* Set time-base frequency to 100 Mhz */
             cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
         }
-        qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+        qemu_register_reset(ppc_prep_reset, env);
     }
 
     /* allocate RAM */
@@ -688,7 +694,7 @@ static void ppc_prep_init (ram_addr_t ram_size,
 #endif
 
     if (usb_enabled) {
-        usb_ohci_init_pci(pci_bus, -1);
+        pci_create_simple(pci_bus, -1, "pci-ohci");
     }
 
     m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59);
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index d69f78cf33..5ee8cb34c1 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -58,7 +58,7 @@ struct boot_info
     uint32_t entry;
 };
 
-static int mpc8544_load_device_tree(CPUState *env,
+static int mpc8544_load_device_tree(CPUPPCState *env,
                                     target_phys_addr_t addr,
                                     uint32_t ramsize,
                                     target_phys_addr_t initrd_base,
@@ -178,7 +178,7 @@ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
     return ffs(size >> 10) - 1;
 }
 
-static void mmubooke_create_initial_mapping(CPUState *env,
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
                                      target_ulong va,
                                      target_phys_addr_t pa)
 {
@@ -196,9 +196,9 @@ static void mmubooke_create_initial_mapping(CPUState *env,
 
 static void mpc8544ds_cpu_reset_sec(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     /* Secondary CPU starts in halted state for now. Needs to change when
        implementing non-kernel boot. */
@@ -208,10 +208,10 @@ static void mpc8544ds_cpu_reset_sec(void *opaque)
 
 static void mpc8544ds_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     struct boot_info *bi = env->load_info;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     /* Set initial guest state. */
     env->halted = 0;
@@ -231,7 +231,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     PCIBus *pci_bus;
-    CPUState *env = NULL;
+    CPUPPCState *env = NULL;
     uint64_t elf_entry;
     uint64_t elf_lowaddr;
     target_phys_addr_t entry=0;
@@ -244,7 +244,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
     qemu_irq **irqs, *mpic;
     DeviceState *dev;
-    CPUState *firstenv = NULL;
+    CPUPPCState *firstenv = NULL;
 
     /* Setup CPUs */
     if (cpu_model == NULL) {
diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c
index 6b8a189c0e..960b7b0c3d 100644
--- a/hw/ppce500_spin.c
+++ b/hw/ppce500_spin.c
@@ -49,7 +49,7 @@ typedef struct spin_state {
 } SpinState;
 
 typedef struct spin_kick {
-    CPUState *env;
+    CPUPPCState *env;
     SpinInfo *spin;
 } SpinKick;
 
@@ -73,7 +73,7 @@ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
     return (ffs(size >> 10) - 1) >> 1;
 }
 
-static void mmubooke_create_initial_mapping(CPUState *env,
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
                                      target_ulong va,
                                      target_phys_addr_t pa,
                                      target_phys_addr_t len)
@@ -91,7 +91,7 @@ static void mmubooke_create_initial_mapping(CPUState *env,
 static void spin_kick(void *data)
 {
     SpinKick *kick = data;
-    CPUState *env = kick->env;
+    CPUPPCState *env = kick->env;
     SpinInfo *curspin = kick->spin;
     target_phys_addr_t map_size = 64 * 1024 * 1024;
     target_phys_addr_t map_start;
@@ -121,7 +121,7 @@ static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value,
 {
     SpinState *s = opaque;
     int env_idx = addr / sizeof(SpinInfo);
-    CPUState *env;
+    CPUPPCState *env;
     SpinInfo *curspin = &s->spin[env_idx];
     uint8_t *curspin_p = (uint8_t*)curspin;
 
@@ -182,7 +182,7 @@ static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len)
     }
 }
 
-const MemoryRegionOps spin_rw_ops = {
+static const MemoryRegionOps spin_rw_ops = {
     .read = spin_read,
     .write = spin_write,
     .endianness = DEVICE_BIG_ENDIAN,
diff --git a/hw/pxa.h b/hw/pxa.h
index e7787393c7..025be34f86 100644
--- a/hw/pxa.h
+++ b/hw/pxa.h
@@ -65,11 +65,11 @@
 # define PXA2XX_INTERNAL_SIZE	0x40000
 
 /* pxa2xx_pic.c */
-DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env);
+DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUARMState *env);
 
 /* pxa2xx_gpio.c */
 DeviceState *pxa2xx_gpio_init(target_phys_addr_t base,
-                CPUState *env, DeviceState *pic, int lines);
+                CPUARMState *env, DeviceState *pic, int lines);
 void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler);
 
 /* pxa2xx_dma.c */
@@ -122,7 +122,7 @@ typedef struct PXA2xxI2SState PXA2xxI2SState;
 typedef struct PXA2xxFIrState PXA2xxFIrState;
 
 typedef struct {
-    CPUState *env;
+    CPUARMState *env;
     DeviceState *pic;
     qemu_irq reset;
     MemoryRegion sdram;
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 1ab27012c1..1d5c35f174 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -1507,8 +1507,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base,
 
     i2c_dev = sysbus_from_qdev(qdev_create(NULL, "pxa2xx_i2c"));
     qdev_prop_set_uint32(&i2c_dev->qdev, "size", region_size + 1);
-    qdev_prop_set_uint32(&i2c_dev->qdev, "offset",
-            base - (base & (~region_size) & TARGET_PAGE_MASK));
+    qdev_prop_set_uint32(&i2c_dev->qdev, "offset", base & region_size);
 
     qdev_init_nofail(&i2c_dev->qdev);
 
@@ -2045,7 +2044,7 @@ static void pxa2xx_reset(void *opaque, int line, int level)
     PXA2xxState *s = (PXA2xxState *) opaque;
 
     if (level && (s->pm_regs[PCFR >> 2] & 0x10)) {	/* GPR_EN */
-        cpu_reset(s->env);
+        cpu_state_reset(s->env);
         /* TODO: reset peripherals */
     }
 }
diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c
index 8ced0dd8ec..031015400b 100644
--- a/hw/pxa2xx_dma.c
+++ b/hw/pxa2xx_dma.c
@@ -18,9 +18,9 @@
 #define PXA2XX_DMA_NUM_REQUESTS 75
 
 typedef struct {
-    target_phys_addr_t descr;
-    target_phys_addr_t src;
-    target_phys_addr_t dest;
+    uint32_t descr;
+    uint32_t src;
+    uint32_t dest;
     uint32_t cmd;
     uint32_t state;
     int request;
@@ -512,9 +512,9 @@ static VMStateDescription vmstate_pxa2xx_dma_chan = {
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
     .fields = (VMStateField[]) {
-        VMSTATE_UINTTL(descr, PXA2xxDMAChannel),
-        VMSTATE_UINTTL(src, PXA2xxDMAChannel),
-        VMSTATE_UINTTL(dest, PXA2xxDMAChannel),
+        VMSTATE_UINT32(descr, PXA2xxDMAChannel),
+        VMSTATE_UINT32(src, PXA2xxDMAChannel),
+        VMSTATE_UINT32(dest, PXA2xxDMAChannel),
         VMSTATE_UINT32(cmd, PXA2xxDMAChannel),
         VMSTATE_UINT32(state, PXA2xxDMAChannel),
         VMSTATE_INT32(request, PXA2xxDMAChannel),
diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c
index d5f57162ed..09a408b781 100644
--- a/hw/pxa2xx_gpio.c
+++ b/hw/pxa2xx_gpio.c
@@ -20,7 +20,7 @@ struct PXA2xxGPIOInfo {
     qemu_irq irq0, irq1, irqX;
     int lines;
     int ncpu;
-    CPUState *cpu_env;
+    CPUARMState *cpu_env;
 
     /* XXX: GNU C vectors are more suitable */
     uint32_t ilevel[PXA2XX_GPIO_BANKS];
@@ -249,7 +249,7 @@ static const MemoryRegionOps pxa_gpio_ops = {
 };
 
 DeviceState *pxa2xx_gpio_init(target_phys_addr_t base,
-                CPUState *env, DeviceState *pic, int lines)
+                CPUARMState *env, DeviceState *pic, int lines)
 {
     DeviceState *dev;
 
diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c
index fcbdfb3fba..ee8bf577cb 100644
--- a/hw/pxa2xx_lcd.c
+++ b/hw/pxa2xx_lcd.c
@@ -19,15 +19,15 @@
 #include "framebuffer.h"
 
 struct DMAChannel {
-    target_phys_addr_t branch;
+    uint32_t branch;
     uint8_t up;
     uint8_t palette[1024];
     uint8_t pbuffer[1024];
     void (*redraw)(PXA2xxLCDState *s, target_phys_addr_t addr,
                    int *miny, int *maxy);
 
-    target_phys_addr_t descriptor;
-    target_phys_addr_t source;
+    uint32_t descriptor;
+    uint32_t source;
     uint32_t id;
     uint32_t command;
 };
@@ -929,11 +929,11 @@ static const VMStateDescription vmstate_dma_channel = {
     .minimum_version_id = 0,
     .minimum_version_id_old = 0,
     .fields      = (VMStateField[]) {
-        VMSTATE_UINTTL(branch, struct DMAChannel),
+        VMSTATE_UINT32(branch, struct DMAChannel),
         VMSTATE_UINT8(up, struct DMAChannel),
         VMSTATE_BUFFER(pbuffer, struct DMAChannel),
-        VMSTATE_UINTTL(descriptor, struct DMAChannel),
-        VMSTATE_UINTTL(source, struct DMAChannel),
+        VMSTATE_UINT32(descriptor, struct DMAChannel),
+        VMSTATE_UINT32(source, struct DMAChannel),
         VMSTATE_UINT32(id, struct DMAChannel),
         VMSTATE_UINT32(command, struct DMAChannel),
         VMSTATE_END_OF_LIST()
diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c
index 6b2bdb0df1..a806b80b0f 100644
--- a/hw/pxa2xx_pic.c
+++ b/hw/pxa2xx_pic.c
@@ -34,7 +34,7 @@
 typedef struct {
     SysBusDevice busdev;
     MemoryRegion iomem;
-    CPUState *cpu_env;
+    CPUARMState *cpu_env;
     uint32_t int_enabled[2];
     uint32_t int_pending[2];
     uint32_t is_fiq[2];
@@ -245,7 +245,7 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id)
     return 0;
 }
 
-DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env)
+DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUARMState *env)
 {
     DeviceState *dev = qdev_create(NULL, "pxa2xx_pic");
     PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, sysbus_from_qdev(dev));
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 0423af1c31..bff9152df5 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -421,10 +421,6 @@ static void set_string(Object *obj, Visitor *v, void *opaque,
         error_propagate(errp, local_err);
         return;
     }
-    if (!*str) {
-        g_free(str);
-        str = NULL;
-    }
     if (*ptr) {
         g_free(*ptr);
     }
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 708414376e..28ab182226 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -21,14 +21,30 @@
 
 #include "qxl.h"
 
-static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
+static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
 {
-    uint8_t *src = qxl->guest_primary.data;
-    uint8_t *dst = qxl->guest_primary.flipped;
+    uint8_t *src;
+    uint8_t *dst = qxl->vga.ds->surface->data;
     int len, i;
 
-    src += (qxl->guest_primary.surface.height - rect->top - 1) *
-        qxl->guest_primary.abs_stride;
+    if (is_buffer_shared(qxl->vga.ds->surface)) {
+        return;
+    }
+    if (!qxl->guest_primary.data) {
+        trace_qxl_render_blit_guest_primary_initialized();
+        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
+    }
+    trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
+            rect->left, rect->right, rect->top, rect->bottom);
+    src = qxl->guest_primary.data;
+    if (qxl->guest_primary.qxl_stride < 0) {
+        /* qxl surface is upside down, walk src scanlines
+         * in reverse order to flip it */
+        src += (qxl->guest_primary.surface.height - rect->top - 1) *
+            qxl->guest_primary.abs_stride;
+    } else {
+        src += rect->top * qxl->guest_primary.abs_stride;
+    }
     dst += rect->top  * qxl->guest_primary.abs_stride;
     src += rect->left * qxl->guest_primary.bytes_pp;
     dst += rect->left * qxl->guest_primary.bytes_pp;
@@ -37,7 +53,7 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
     for (i = rect->top; i < rect->bottom; i++) {
         memcpy(dst, src, len);
         dst += qxl->guest_primary.abs_stride;
-        src -= qxl->guest_primary.abs_stride;
+        src += qxl->guest_primary.qxl_stride;
     }
 }
 
@@ -71,84 +87,105 @@ void qxl_render_resize(PCIQXLDevice *qxl)
     }
 }
 
-void qxl_render_update(PCIQXLDevice *qxl)
+static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
+{
+    area->left   = 0;
+    area->right  = qxl->guest_primary.surface.width;
+    area->top    = 0;
+    area->bottom = qxl->guest_primary.surface.height;
+}
+
+static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
 {
     VGACommonState *vga = &qxl->vga;
-    QXLRect dirty[32], update;
-    void *ptr;
-    int i, redraw = 0;
-
-    if (!is_buffer_shared(vga->ds->surface)) {
-        dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__);
-        qxl->guest_primary.resized++;
-        qxl->guest_primary.commands++;
-        redraw = 1;
-    }
+    int i;
+    DisplaySurface *surface = vga->ds->surface;
 
     if (qxl->guest_primary.resized) {
         qxl->guest_primary.resized = 0;
-
-        if (qxl->guest_primary.flipped) {
-            g_free(qxl->guest_primary.flipped);
-            qxl->guest_primary.flipped = NULL;
-        }
-        qemu_free_displaysurface(vga->ds);
-
         qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
-        if (qxl->guest_primary.qxl_stride < 0) {
-            /* spice surface is upside down -> need extra buffer to flip */
-            qxl->guest_primary.flipped =
-                g_malloc(qxl->guest_primary.surface.width *
-                         qxl->guest_primary.abs_stride);
-            ptr = qxl->guest_primary.flipped;
-        } else {
-            ptr = qxl->guest_primary.data;
-        }
-        dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
-               __FUNCTION__,
+        qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
+        qxl->num_dirty_rects = 1;
+        trace_qxl_render_guest_primary_resized(
                qxl->guest_primary.surface.width,
                qxl->guest_primary.surface.height,
                qxl->guest_primary.qxl_stride,
                qxl->guest_primary.bytes_pp,
-               qxl->guest_primary.bits_pp,
-               qxl->guest_primary.flipped ? "yes" : "no");
-        vga->ds->surface =
+               qxl->guest_primary.bits_pp);
+    }
+    if (surface->width != qxl->guest_primary.surface.width ||
+        surface->height != qxl->guest_primary.surface.height) {
+        if (qxl->guest_primary.qxl_stride > 0) {
+            qemu_free_displaysurface(vga->ds);
             qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
                                             qxl->guest_primary.surface.height,
                                             qxl->guest_primary.bits_pp,
                                             qxl->guest_primary.abs_stride,
-                                            ptr);
-        dpy_resize(vga->ds);
-    }
-
-    update.left   = 0;
-    update.right  = qxl->guest_primary.surface.width;
-    update.top    = 0;
-    update.bottom = qxl->guest_primary.surface.height;
-
-    memset(dirty, 0, sizeof(dirty));
-    if (runstate_is_running() && qxl->guest_primary.commands) {
-        qxl->guest_primary.commands = 0;
-        qxl_spice_update_area(qxl, 0, &update,
-                              dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC);
-    }
-    if (redraw) {
-        memset(dirty, 0, sizeof(dirty));
-        dirty[0] = update;
+                                            qxl->guest_primary.data);
+        } else {
+            qemu_resize_displaysurface(vga->ds,
+                    qxl->guest_primary.surface.width,
+                    qxl->guest_primary.surface.height);
+        }
     }
-
-    for (i = 0; i < ARRAY_SIZE(dirty); i++) {
-        if (qemu_spice_rect_is_empty(dirty+i)) {
+    for (i = 0; i < qxl->num_dirty_rects; i++) {
+        if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
             break;
         }
-        if (qxl->guest_primary.flipped) {
-            qxl_flip(qxl, dirty+i);
-        }
+        qxl_blit(qxl, qxl->dirty+i);
         dpy_update(vga->ds,
-                   dirty[i].left, dirty[i].top,
-                   dirty[i].right - dirty[i].left,
-                   dirty[i].bottom - dirty[i].top);
+                   qxl->dirty[i].left, qxl->dirty[i].top,
+                   qxl->dirty[i].right - qxl->dirty[i].left,
+                   qxl->dirty[i].bottom - qxl->dirty[i].top);
     }
+    qxl->num_dirty_rects = 0;
+}
+
+/*
+ * use ssd.lock to protect render_update_cookie_num.
+ * qxl_render_update is called by io thread or vcpu thread, and the completion
+ * callbacks are called by spice_server thread, defering to bh called from the
+ * io thread.
+ */
+void qxl_render_update(PCIQXLDevice *qxl)
+{
+    QXLCookie *cookie;
+
+    qemu_mutex_lock(&qxl->ssd.lock);
+
+    if (!runstate_is_running() || !qxl->guest_primary.commands) {
+        qxl_render_update_area_unlocked(qxl);
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        return;
+    }
+
+    qxl->guest_primary.commands = 0;
+    qxl->render_update_cookie_num++;
+    qemu_mutex_unlock(&qxl->ssd.lock);
+    cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
+                            0);
+    qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
+    qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
+                          0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
+}
+
+void qxl_render_update_area_bh(void *opaque)
+{
+    PCIQXLDevice *qxl = opaque;
+
+    qemu_mutex_lock(&qxl->ssd.lock);
+    qxl_render_update_area_unlocked(qxl);
+    qemu_mutex_unlock(&qxl->ssd.lock);
+}
+
+void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
+{
+    qemu_mutex_lock(&qxl->ssd.lock);
+    trace_qxl_render_update_area_done(cookie);
+    qemu_bh_schedule(qxl->update_area_bh);
+    qxl->render_update_cookie_num--;
+    qemu_mutex_unlock(&qxl->ssd.lock);
+    g_free(cookie);
 }
 
 static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
diff --git a/hw/qxl.c b/hw/qxl.c
index f643667205..47a162e479 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -23,6 +23,7 @@
 #include "qemu-queue.h"
 #include "monitor.h"
 #include "sysemu.h"
+#include "trace.h"
 
 #include "qxl.h"
 
@@ -125,9 +126,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
 
 void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
 {
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
-#endif
     if (qxl->guestdebug) {
         va_list ap;
         va_start(ap, msg);
@@ -143,24 +142,26 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
                            struct QXLRect *area, struct QXLRect *dirty_rects,
                            uint32_t num_dirty_rects,
                            uint32_t clear_dirty_region,
-                           qxl_async_io async)
+                           qxl_async_io async, struct QXLCookie *cookie)
 {
+    trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right,
+                                area->top, area->bottom);
+    trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects,
+                                     clear_dirty_region);
     if (async == QXL_SYNC) {
         qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
                         dirty_rects, num_dirty_rects, clear_dirty_region);
     } else {
-#if SPICE_INTERFACE_QXL_MINOR >= 1
+        assert(cookie != NULL);
         spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
-                                    clear_dirty_region, 0);
-#else
-        abort();
-#endif
+                                    clear_dirty_region, (uintptr_t)cookie);
     }
 }
 
 static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
                                                     uint32_t id)
 {
+    trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
     qemu_mutex_lock(&qxl->track_lock);
     qxl->guest_surfaces.cmds[id] = 0;
     qxl->guest_surfaces.count--;
@@ -170,44 +171,50 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
 static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
                                            qxl_async_io async)
 {
+    QXLCookie *cookie;
+
+    trace_qxl_spice_destroy_surface_wait(qxl->id, id, async);
     if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
-        abort();
-#else
-        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id,
-                                        (uint64_t)id);
-#endif
+        cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                QXL_IO_DESTROY_SURFACE_ASYNC);
+        cookie->u.surface_id = id;
+        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
     } else {
         qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
-        qxl_spice_destroy_surface_wait_complete(qxl, id);
     }
 }
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
 static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
 {
-    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0);
+    trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
+                                         qxl->num_free_res);
+    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
+        (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                  QXL_IO_FLUSH_SURFACES_ASYNC));
 }
-#endif
 
 void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count)
 {
+    trace_qxl_spice_loadvm_commands(qxl->id, ext, count);
     qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count);
 }
 
 void qxl_spice_oom(PCIQXLDevice *qxl)
 {
+    trace_qxl_spice_oom(qxl->id);
     qxl->ssd.worker->oom(qxl->ssd.worker);
 }
 
 void qxl_spice_reset_memslots(PCIQXLDevice *qxl)
 {
+    trace_qxl_spice_reset_memslots(qxl->id);
     qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
 }
 
 static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
 {
+    trace_qxl_spice_destroy_surfaces_complete(qxl->id);
     qemu_mutex_lock(&qxl->track_lock);
     memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
     qxl->guest_surfaces.count = 0;
@@ -216,12 +223,11 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
 
 static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
 {
+    trace_qxl_spice_destroy_surfaces(qxl->id, async);
     if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
-        abort();
-#else
-        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0);
-#endif
+        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
+                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                          QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
     } else {
         qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
         qxl_spice_destroy_surfaces_complete(qxl);
@@ -230,11 +236,13 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
 
 void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
 {
+    trace_qxl_spice_reset_image_cache(qxl->id);
     qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
 }
 
 void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
 {
+    trace_qxl_spice_reset_cursor(qxl->id);
     qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
     qemu_mutex_lock(&qxl->track_lock);
     qxl->guest_cursor = 0;
@@ -416,7 +424,7 @@ static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
 {
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 
-    dprint(qxl, 1, "%s:\n", __FUNCTION__);
+    trace_qxl_interface_attach_worker(qxl->id);
     qxl->ssd.worker = qxl_worker;
 }
 
@@ -424,7 +432,7 @@ static void interface_set_compression_level(QXLInstance *sin, int level)
 {
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 
-    dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level);
+    trace_qxl_interface_set_compression_level(qxl->id, level);
     qxl->shadow_rom.compression_level = cpu_to_le32(level);
     qxl->rom->compression_level = cpu_to_le32(level);
     qxl_rom_set_dirty(qxl);
@@ -434,6 +442,7 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
 {
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 
+    trace_qxl_interface_set_mm_time(qxl->id, mm_time);
     qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
     qxl->rom->mm_clock = cpu_to_le32(mm_time);
     qxl_rom_set_dirty(qxl);
@@ -443,7 +452,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
 {
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 
-    dprint(qxl, 1, "%s:\n", __FUNCTION__);
+    trace_qxl_interface_get_init_info(qxl->id);
     info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
     info->memslot_id_bits = MEMSLOT_SLOT_BITS;
     info->num_memslots = NUM_MEMSLOTS;
@@ -490,7 +499,6 @@ static const char *io_port_to_string(uint32_t io_port)
         [QXL_IO_DESTROY_PRIMARY]        = "QXL_IO_DESTROY_PRIMARY",
         [QXL_IO_DESTROY_SURFACE_WAIT]   = "QXL_IO_DESTROY_SURFACE_WAIT",
         [QXL_IO_DESTROY_ALL_SURFACES]   = "QXL_IO_DESTROY_ALL_SURFACES",
-#if SPICE_INTERFACE_QXL_MINOR >= 1
         [QXL_IO_UPDATE_AREA_ASYNC]      = "QXL_IO_UPDATE_AREA_ASYNC",
         [QXL_IO_MEMSLOT_ADD_ASYNC]      = "QXL_IO_MEMSLOT_ADD_ASYNC",
         [QXL_IO_CREATE_PRIMARY_ASYNC]   = "QXL_IO_CREATE_PRIMARY_ASYNC",
@@ -500,7 +508,6 @@ static const char *io_port_to_string(uint32_t io_port)
                                         = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
         [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
         [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
-#endif
     };
     return io_port_to_string[io_port];
 }
@@ -514,9 +521,10 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
     QXLCommand *cmd;
     int notify, ret;
 
+    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode));
+
     switch (qxl->mode) {
     case QXL_MODE_VGA:
-        dprint(qxl, 2, "%s: vga\n", __FUNCTION__);
         ret = false;
         qemu_mutex_lock(&qxl->ssd.lock);
         if (qxl->ssd.update != NULL) {
@@ -527,19 +535,18 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
         }
         qemu_mutex_unlock(&qxl->ssd.lock);
         if (ret) {
-            dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
+            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
             qxl_log_command(qxl, "vga", ext);
         }
         return ret;
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
     case QXL_MODE_UNDEFINED:
-        dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
         ring = &qxl->ram->cmd_ring;
         if (SPICE_RING_IS_EMPTY(ring)) {
             return false;
         }
-        dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
+        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
         SPICE_RING_CONS_ITEM(ring, cmd);
         ext->cmd      = *cmd;
         ext->group_id = MEMSLOT_GROUP_GUEST;
@@ -564,6 +571,7 @@ static int interface_req_cmd_notification(QXLInstance *sin)
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
     int wait = 1;
 
+    trace_qxl_ring_command_req_notification(qxl->id);
     switch (qxl->mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
@@ -601,10 +609,11 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
     }
 
     SPICE_RING_PUSH(ring, notify);
-    dprint(d, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n",
-           d->num_free_res, notify ? "yes" : "no",
-           ring->prod - ring->cons, ring->num_items,
-           ring->prod, ring->cons);
+    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode),
+           d->guest_surfaces.count, d->num_free_res,
+           d->last_release, notify ? "yes" : "no");
+    trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
+           ring->num_items, ring->prod, ring->cons);
     if (notify) {
         qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
     }
@@ -651,7 +660,7 @@ static void interface_release_resource(QXLInstance *sin,
     }
     qxl->last_release = ext.info;
     qxl->num_free_res++;
-    dprint(qxl, 3, "%4d\r", qxl->num_free_res);
+    trace_qxl_ring_res_put(qxl->id, qxl->num_free_res);
     qxl_push_free_res(qxl, 0);
 }
 
@@ -663,6 +672,8 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *
     QXLCommand *cmd;
     int notify;
 
+    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode));
+
     switch (qxl->mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
@@ -686,6 +697,7 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *
         if (qxl->id == 0) {
             qxl_render_cursor(qxl, ext);
         }
+        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode));
         return true;
     default:
         return false;
@@ -698,6 +710,7 @@ static int interface_req_cursor_notification(QXLInstance *sin)
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
     int wait = 1;
 
+    trace_qxl_ring_cursor_req_notification(qxl->id);
     switch (qxl->mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
@@ -725,7 +738,6 @@ static int interface_flush_resources(QXLInstance *sin)
     PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
     int ret;
 
-    dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res);
     ret = qxl->num_free_res;
     if (ret) {
         qxl_push_free_res(qxl, 1);
@@ -735,12 +747,9 @@ static int interface_flush_resources(QXLInstance *sin)
 
 static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
-
 /* called from spice server thread context only */
-static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
+static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
     uint32_t current_async;
 
     qemu_mutex_lock(&qxl->async_lock);
@@ -748,9 +757,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
     qxl->current_async = QXL_UNDEFINED_IO;
     qemu_mutex_unlock(&qxl->async_lock);
 
-    dprint(qxl, 2, "async_complete: %d (%" PRId64 ") done\n",
-           current_async, cookie);
+    trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie);
+    if (!cookie) {
+        fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
+        return;
+    }
+    if (cookie && current_async != cookie->io) {
+        fprintf(stderr,
+                "qxl: %s: error: current_async = %d != %" PRId64 " = cookie->io\n",
+                __func__, current_async, cookie->io);
+    }
     switch (current_async) {
+    case QXL_IO_MEMSLOT_ADD_ASYNC:
+    case QXL_IO_DESTROY_PRIMARY_ASYNC:
+    case QXL_IO_UPDATE_AREA_ASYNC:
+    case QXL_IO_FLUSH_SURFACES_ASYNC:
+        break;
     case QXL_IO_CREATE_PRIMARY_ASYNC:
         qxl_create_guest_primary_complete(qxl);
         break;
@@ -758,13 +780,79 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
         qxl_spice_destroy_surfaces_complete(qxl);
         break;
     case QXL_IO_DESTROY_SURFACE_ASYNC:
-        qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie);
+        qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
         break;
+    default:
+        fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
+                current_async);
     }
     qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
 }
 
-#endif
+/* called from spice server thread context only */
+static void interface_update_area_complete(QXLInstance *sin,
+        uint32_t surface_id,
+        QXLRect *dirty, uint32_t num_updated_rects)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    int i;
+    int qxl_i;
+
+    qemu_mutex_lock(&qxl->ssd.lock);
+    if (surface_id != 0 || !qxl->render_update_cookie_num) {
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        return;
+    }
+    trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left,
+            dirty->right, dirty->top, dirty->bottom);
+    trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects);
+    if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) {
+        /*
+         * overflow - treat this as a full update. Not expected to be common.
+         */
+        trace_qxl_interface_update_area_complete_overflow(qxl->id,
+                                                          QXL_NUM_DIRTY_RECTS);
+        qxl->guest_primary.resized = 1;
+    }
+    if (qxl->guest_primary.resized) {
+        /*
+         * Don't bother copying or scheduling the bh since we will flip
+         * the whole area anyway on completion of the update_area async call
+         */
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        return;
+    }
+    qxl_i = qxl->num_dirty_rects;
+    for (i = 0; i < num_updated_rects; i++) {
+        qxl->dirty[qxl_i++] = dirty[i];
+    }
+    qxl->num_dirty_rects += num_updated_rects;
+    trace_qxl_interface_update_area_complete_schedule_bh(qxl->id,
+                                                         qxl->num_dirty_rects);
+    qemu_bh_schedule(qxl->update_area_bh);
+    qemu_mutex_unlock(&qxl->ssd.lock);
+}
+
+/* called from spice server thread context only */
+static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
+
+    switch (cookie->type) {
+    case QXL_COOKIE_TYPE_IO:
+        interface_async_complete_io(qxl, cookie);
+        g_free(cookie);
+        break;
+    case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
+        qxl_render_update_area_done(qxl, cookie);
+        break;
+    default:
+        fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
+                __func__, cookie->type);
+        g_free(cookie);
+    }
+}
 
 static const QXLInterface qxl_interface = {
     .base.type               = SPICE_INTERFACE_QXL,
@@ -785,9 +873,8 @@ static const QXLInterface qxl_interface = {
     .req_cursor_notification = interface_req_cursor_notification,
     .notify_update           = interface_notify_update,
     .flush_resources         = interface_flush_resources,
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     .async_complete          = interface_async_complete,
-#endif
+    .update_area_complete    = interface_update_area_complete,
 };
 
 static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -795,7 +882,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d)
     if (d->mode == QXL_MODE_VGA) {
         return;
     }
-    dprint(d, 1, "%s\n", __FUNCTION__);
+    trace_qxl_enter_vga_mode(d->id);
     qemu_spice_create_host_primary(&d->ssd);
     d->mode = QXL_MODE_VGA;
     memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
@@ -806,7 +893,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d)
     if (d->mode != QXL_MODE_VGA) {
         return;
     }
-    dprint(d, 1, "%s\n", __FUNCTION__);
+    trace_qxl_exit_vga_mode(d->id);
     qxl_destroy_primary(d, QXL_SYNC);
 }
 
@@ -843,7 +930,7 @@ static void qxl_reset_state(PCIQXLDevice *d)
 
 static void qxl_soft_reset(PCIQXLDevice *d)
 {
-    dprint(d, 1, "%s:\n", __FUNCTION__);
+    trace_qxl_soft_reset(d->id);
     qxl_check_state(d);
 
     if (d->id == 0) {
@@ -855,8 +942,7 @@ static void qxl_soft_reset(PCIQXLDevice *d)
 
 static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
 {
-    dprint(d, 1, "%s: start%s\n", __FUNCTION__,
-           loadvm ? " (loadvm)" : "");
+    trace_qxl_hard_reset(d->id, loadvm);
 
     qxl_spice_reset_cursor(d);
     qxl_spice_reset_image_cache(d);
@@ -871,13 +957,12 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
     }
     qemu_spice_create_host_memslot(&d->ssd);
     qxl_soft_reset(d);
-
-    dprint(d, 1, "%s: done\n", __FUNCTION__);
 }
 
 static void qxl_reset_handler(DeviceState *dev)
 {
     PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
+
     qxl_hard_reset(d, 0);
 }
 
@@ -886,8 +971,8 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
     VGACommonState *vga = opaque;
     PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
 
+    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val);
     if (qxl->mode != QXL_MODE_VGA) {
-        dprint(qxl, 1, "%s\n", __FUNCTION__);
         qxl_destroy_primary(qxl, QXL_SYNC);
         qxl_soft_reset(qxl);
     }
@@ -914,6 +999,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
     static const int regions[] = {
         QXL_RAM_RANGE_INDEX,
         QXL_VRAM_RANGE_INDEX,
+        QXL_VRAM64_RANGE_INDEX,
     };
     uint64_t guest_start;
     uint64_t guest_end;
@@ -927,9 +1013,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
     guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start);
     guest_end   = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end);
 
-    dprint(d, 1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n",
-           __FUNCTION__, slot_id,
-           guest_start, guest_end);
+    trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end);
 
     PANIC_ON(slot_id >= NUM_MEMSLOTS);
     PANIC_ON(guest_start > guest_end);
@@ -960,6 +1044,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
         virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram);
         break;
     case QXL_VRAM_RANGE_INDEX:
+    case 4 /* vram 64bit */:
         virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar);
         break;
     default:
@@ -975,10 +1060,6 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
     memslot.generation = d->rom->slot_generation = 0;
     qxl_rom_set_dirty(d);
 
-    dprint(d, 1, "%s: slot %d: host virt 0x%lx - 0x%lx\n",
-           __FUNCTION__, memslot.slot_id,
-           memslot.virt_start, memslot.virt_end);
-
     qemu_spice_add_memslot(&d->ssd, &memslot, async);
     d->guest_slots[slot_id].ptr = (void*)memslot.virt_start;
     d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
@@ -988,21 +1069,19 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
 
 static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
 {
-    dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id);
     qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
     d->guest_slots[slot_id].active = 0;
 }
 
 static void qxl_reset_memslots(PCIQXLDevice *d)
 {
-    dprint(d, 1, "%s:\n", __FUNCTION__);
     qxl_spice_reset_memslots(d);
     memset(&d->guest_slots, 0, sizeof(d->guest_slots));
 }
 
 static void qxl_reset_surfaces(PCIQXLDevice *d)
 {
-    dprint(d, 1, "%s:\n", __FUNCTION__);
+    trace_qxl_reset_surfaces(d->id);
     d->mode = QXL_MODE_UNDEFINED;
     qxl_spice_destroy_surfaces(d, QXL_SYNC);
 }
@@ -1044,9 +1123,6 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
     assert(qxl->mode != QXL_MODE_NATIVE);
     qxl_exit_vga_mode(qxl);
 
-    dprint(qxl, 1, "%s: %dx%d\n", __FUNCTION__,
-           le32_to_cpu(sc->width), le32_to_cpu(sc->height));
-
     surface.format     = le32_to_cpu(sc->format);
     surface.height     = le32_to_cpu(sc->height);
     surface.mem        = le64_to_cpu(sc->mem);
@@ -1055,6 +1131,10 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
     surface.width      = le32_to_cpu(sc->width);
     surface.type       = le32_to_cpu(sc->type);
     surface.flags      = le32_to_cpu(sc->flags);
+    trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
+                                   sc->format, sc->position);
+    trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
+                                        sc->flags);
 
     surface.mouse_mode = true;
     surface.group_id   = MEMSLOT_GROUP_GUEST;
@@ -1078,9 +1158,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
     if (d->mode == QXL_MODE_UNDEFINED) {
         return 0;
     }
-
-    dprint(d, 1, "%s\n", __FUNCTION__);
-
+    trace_qxl_destroy_primary(d->id);
     d->mode = QXL_MODE_UNDEFINED;
     qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
     qxl_spice_reset_cursor(d);
@@ -1107,8 +1185,8 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
         .mem        = devmem + d->shadow_rom.draw_area_offset,
     };
 
-    dprint(d, 1, "%s: mode %d  [ %d x %d @ %d bpp devmem 0x%" PRIx64 " ]\n",
-           __func__, modenr, mode->x_res, mode->y_res, mode->bits, devmem);
+    trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits,
+                       devmem);
     if (!loadvm) {
         qxl_hard_reset(d, 0);
     }
@@ -1137,9 +1215,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
     PCIQXLDevice *d = opaque;
     uint32_t io_port = addr;
     qxl_async_io async = QXL_SYNC;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     uint32_t orig_io_port = io_port;
-#endif
 
     switch (io_port) {
     case QXL_IO_RESET:
@@ -1149,28 +1225,23 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
     case QXL_IO_CREATE_PRIMARY:
     case QXL_IO_UPDATE_IRQ:
     case QXL_IO_LOG:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case QXL_IO_MEMSLOT_ADD_ASYNC:
     case QXL_IO_CREATE_PRIMARY_ASYNC:
-#endif
         break;
     default:
         if (d->mode != QXL_MODE_VGA) {
             break;
         }
-        dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n",
-            __func__, io_port, io_port_to_string(io_port));
-#if SPICE_INTERFACE_QXL_MINOR >= 1
+        trace_qxl_io_unexpected_vga_mode(d->id,
+            io_port, io_port_to_string(io_port));
         /* be nice to buggy guest drivers */
         if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
             io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
             qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
         }
-#endif
         return;
     }
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     /* we change the io_port to avoid ifdeffery in the main switch */
     orig_io_port = io_port;
     switch (io_port) {
@@ -1204,19 +1275,27 @@ async_common:
         }
         d->current_async = orig_io_port;
         qemu_mutex_unlock(&d->async_lock);
-        dprint(d, 2, "start async %d (%"PRId64")\n", io_port, val);
         break;
     default:
         break;
     }
-#endif
+    trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size,
+                       async);
 
     switch (io_port) {
     case QXL_IO_UPDATE_AREA:
     {
+        QXLCookie *cookie = NULL;
         QXLRect update = d->ram->update_area;
+
+        if (async == QXL_ASYNC) {
+            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                    QXL_IO_UPDATE_AREA_ASYNC);
+            cookie->u.area = update;
+        }
         qxl_spice_update_area(d, d->ram->update_surface,
-                              &update, NULL, 0, 0, async);
+                              cookie ? &cookie->u.area : &update,
+                              NULL, 0, 0, async, cookie);
         break;
     }
     case QXL_IO_NOTIFY_CMD:
@@ -1237,7 +1316,6 @@ async_common:
         d->oom_running = 0;
         break;
     case QXL_IO_SET_MODE:
-        dprint(d, 1, "QXL_SET_MODE %d\n", (int)val);
         qxl_set_mode(d, val, 0);
         break;
     case QXL_IO_LOG:
@@ -1247,7 +1325,6 @@ async_common:
         }
         break;
     case QXL_IO_RESET:
-        dprint(d, 1, "QXL_IO_RESET\n");
         qxl_hard_reset(d, 0);
         break;
     case QXL_IO_MEMSLOT_ADD:
@@ -1275,7 +1352,6 @@ async_common:
                           async);
             goto cancel_async;
         }
-        dprint(d, 1, "QXL_IO_CREATE_PRIMARY async=%d\n", async);
         d->guest_primary.surface = d->ram->create_surface;
         qxl_create_guest_primary(d, 0, async);
         break;
@@ -1285,11 +1361,9 @@ async_common:
                           async);
             goto cancel_async;
         }
-        dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (async=%d) (%s)\n", async,
-               qxl_mode_to_string(d->mode));
         if (!qxl_destroy_primary(d, async)) {
-            dprint(d, 1, "QXL_IO_DESTROY_PRIMARY_ASYNC in %s, ignored\n",
-                    qxl_mode_to_string(d->mode));
+            trace_qxl_io_destroy_primary_ignored(d->id,
+                                                 qxl_mode_to_string(d->mode));
             goto cancel_async;
         }
         break;
@@ -1301,7 +1375,6 @@ async_common:
         }
         qxl_spice_destroy_surface_wait(d, val, async);
         break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case QXL_IO_FLUSH_RELEASE: {
         QXLReleaseRing *ring = &d->ram->release_ring;
         if (ring->prod - ring->cons + 1 == ring->num_items) {
@@ -1310,19 +1383,11 @@ async_common:
                 ring->prod, ring->cons);
         }
         qxl_push_free_res(d, 1 /* flush */);
-        dprint(d, 1, "QXL_IO_FLUSH_RELEASE exit (%s, s#=%d, res#=%d,%p)\n",
-            qxl_mode_to_string(d->mode), d->guest_surfaces.count,
-            d->num_free_res, d->last_release);
         break;
     }
     case QXL_IO_FLUSH_SURFACES_ASYNC:
-        dprint(d, 1, "QXL_IO_FLUSH_SURFACES_ASYNC"
-                     " (%"PRId64") (%s, s#=%d, res#=%d)\n",
-               val, qxl_mode_to_string(d->mode), d->guest_surfaces.count,
-               d->num_free_res);
         qxl_spice_flush_surfaces_async(d);
         break;
-#endif
     case QXL_IO_DESTROY_ALL_SURFACES:
         d->mode = QXL_MODE_UNDEFINED;
         qxl_spice_destroy_surfaces(d, async);
@@ -1333,16 +1398,12 @@ async_common:
     }
     return;
 cancel_async:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     if (async) {
         qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
         qemu_mutex_lock(&d->async_lock);
         d->current_async = QXL_UNDEFINED_IO;
         qemu_mutex_unlock(&d->async_lock);
     }
-#else
-    return;
-#endif
 }
 
 static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
@@ -1350,7 +1411,7 @@ static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
 {
     PCIQXLDevice *d = opaque;
 
-    dprint(d, 1, "%s: unexpected\n", __FUNCTION__);
+    trace_qxl_io_read_unexpected(d->id);
     return 0xff;
 }
 
@@ -1396,16 +1457,17 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
 
 static void init_pipe_signaling(PCIQXLDevice *d)
 {
-   if (pipe(d->pipe) < 0) {
-       dprint(d, 1, "%s: pipe creation failed\n", __FUNCTION__);
-       return;
-   }
-   fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
-   fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
-   fcntl(d->pipe[0], F_SETOWN, getpid());
+    if (pipe(d->pipe) < 0) {
+        fprintf(stderr, "%s:%s: qxl pipe creation failed\n",
+                __FILE__, __func__);
+        exit(1);
+    }
+    fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
+    fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
+    fcntl(d->pipe[0], F_SETOWN, getpid());
 
-   qemu_thread_get_self(&d->main);
-   qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
+    qemu_thread_get_self(&d->main);
+    qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
 }
 
 /* graphics console */
@@ -1500,8 +1562,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
         surface_offset -= vram_start;
         surface_size = cmd->u.surface_create.height *
                        abs(cmd->u.surface_create.stride);
-        dprint(qxl, 3, "%s: dirty surface %d, offset %d, size %d\n", __func__,
-               i, (int)surface_offset, surface_size);
+        trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size);
         qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size);
     }
 }
@@ -1545,6 +1606,10 @@ static void display_refresh(struct DisplayState *ds)
 {
     if (qxl0->mode == QXL_MODE_VGA) {
         qemu_spice_display_refresh(&qxl0->ssd);
+    } else {
+        qemu_mutex_lock(&qxl0->ssd.lock);
+        qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
+        qemu_mutex_unlock(&qxl0->ssd.lock);
     }
 }
 
@@ -1564,18 +1629,28 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
         qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
     }
 
-    /* vram (surfaces, bar 1) */
+    /* vram32 (surfaces, 32bit, bar 1) */
+    if (qxl->vram32_size_mb != -1) {
+        qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024;
+    }
+    if (qxl->vram32_size < 4096) {
+        qxl->vram32_size = 4096;
+    }
+
+    /* vram (surfaces, 64bit, bar 4+5) */
     if (qxl->vram_size_mb != -1) {
         qxl->vram_size = qxl->vram_size_mb * 1024 * 1024;
     }
-    if (qxl->vram_size < 4096) {
-        qxl->vram_size = 4096;
+    if (qxl->vram_size < qxl->vram32_size) {
+        qxl->vram_size = qxl->vram32_size;
     }
+
     if (qxl->revision == 1) {
+        qxl->vram32_size = 4096;
         qxl->vram_size = 4096;
     }
-
     qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+    qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
     qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
 }
 
@@ -1600,9 +1675,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     case 2: /* spice 0.6 -- qxl-2 */
         pci_device_rev = QXL_REVISION_STABLE_V06;
         break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case 3: /* qxl-3 */
-#endif
     default:
         pci_device_rev = QXL_DEFAULT_REVISION;
         break;
@@ -1619,6 +1692,8 @@ static int qxl_init_common(PCIQXLDevice *qxl)
 
     memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size);
     vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
+    memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar,
+                             0, qxl->vram32_size);
 
     io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
     if (qxl->revision == 1) {
@@ -1642,7 +1717,29 @@ static int qxl_init_common(PCIQXLDevice *qxl)
                      PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
 
     pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar);
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar);
+
+    if (qxl->vram32_size < qxl->vram_size) {
+        /*
+         * Make the 64bit vram bar show up only in case it is
+         * configured to be larger than the 32bit vram bar.
+         */
+        pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX,
+                         PCI_BASE_ADDRESS_SPACE_MEMORY |
+                         PCI_BASE_ADDRESS_MEM_TYPE_64 |
+                         PCI_BASE_ADDRESS_MEM_PREFETCH,
+                         &qxl->vram_bar);
+    }
+
+    /* print pci bar details */
+    dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
+           qxl->id == 0 ? "pri" : "sec",
+           qxl->vga.vram_size / (1024*1024));
+    dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
+           qxl->vram32_size / (1024*1024));
+    dprint(qxl, 1, "vram/64: %d MB %s\n",
+           qxl->vram_size / (1024*1024),
+           qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
 
     qxl->ssd.qxl.base.sif = &qxl_interface.base;
     qxl->ssd.qxl.id = qxl->id;
@@ -1652,6 +1749,8 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     init_pipe_signaling(qxl);
     qxl_reset_state(qxl);
 
+    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+
     return 0;
 }
 
@@ -1697,7 +1796,7 @@ static void qxl_pre_save(void *opaque)
     PCIQXLDevice* d = opaque;
     uint8_t *ram_start = d->vga.vram_ptr;
 
-    dprint(d, 1, "%s:\n", __FUNCTION__);
+    trace_qxl_pre_save(d->id);
     if (d->last_release == NULL) {
         d->last_release_offset = 0;
     } else {
@@ -1710,10 +1809,9 @@ static int qxl_pre_load(void *opaque)
 {
     PCIQXLDevice* d = opaque;
 
-    dprint(d, 1, "%s: start\n", __FUNCTION__);
+    trace_qxl_pre_load(d->id);
     qxl_hard_reset(d, 1);
     qxl_exit_vga_mode(d);
-    dprint(d, 1, "%s: done\n", __FUNCTION__);
     return 0;
 }
 
@@ -1725,7 +1823,6 @@ static void qxl_create_memslots(PCIQXLDevice *d)
         if (!d->guest_slots[i].active) {
             continue;
         }
-        dprint(d, 1, "%s: restoring guest slot %d\n", __func__, i);
         qxl_add_memslot(d, i, 0, QXL_SYNC);
     }
 }
@@ -1737,8 +1834,6 @@ static int qxl_post_load(void *opaque, int version)
     QXLCommandExt *cmds;
     int in, out, newmode;
 
-    dprint(d, 1, "%s: start\n", __FUNCTION__);
-
     assert(d->last_release_offset < d->vga.vram_size);
     if (d->last_release_offset == 0) {
         d->last_release = NULL;
@@ -1748,8 +1843,7 @@ static int qxl_post_load(void *opaque, int version)
 
     d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
 
-    dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__,
-        qxl_mode_to_string(d->mode));
+    trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode));
     newmode = d->mode;
     d->mode = QXL_MODE_UNDEFINED;
 
@@ -1791,8 +1885,6 @@ static int qxl_post_load(void *opaque, int version)
         qxl_set_mode(d, d->shadow_rom.mode, 1);
         break;
     }
-    dprint(d, 1, "%s: done\n", __FUNCTION__);
-
     return 0;
 }
 
@@ -1859,7 +1951,7 @@ static VMStateDescription qxl_vmstate = {
 static Property qxl_properties[] = {
         DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
                            64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size,
                            64 * 1024 * 1024),
         DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
                            QXL_DEFAULT_REVISION),
@@ -1867,7 +1959,8 @@ static Property qxl_properties[] = {
         DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
         DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
         DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
-        DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram_size_mb, -1),
+        DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, 0),
+        DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0),
         DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/qxl.h b/hw/qxl.h
index d0629916ad..11a0db3f7d 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -16,8 +16,14 @@ enum qxl_mode {
     QXL_MODE_NATIVE,
 };
 
+#ifndef QXL_VRAM64_RANGE_INDEX
+#define QXL_VRAM64_RANGE_INDEX 4
+#endif
+
 #define QXL_UNDEFINED_IO UINT32_MAX
 
+#define QXL_NUM_DIRTY_RECTS 64
+
 typedef struct PCIQXLDevice {
     PCIDevice          pci;
     SimpleSpiceDisplay ssd;
@@ -52,7 +58,7 @@ typedef struct PCIQXLDevice {
         uint32_t       abs_stride;
         uint32_t       bits_pp;
         uint32_t       bytes_pp;
-        uint8_t        *data, *flipped;
+        uint8_t        *data;
     } guest_primary;
 
     struct surfaces {
@@ -86,6 +92,8 @@ typedef struct PCIQXLDevice {
     /* vram pci bar */
     uint32_t           vram_size;
     MemoryRegion       vram_bar;
+    uint32_t           vram32_size;
+    MemoryRegion       vram32_bar;
 
     /* io bar */
     MemoryRegion       io_bar;
@@ -93,6 +101,13 @@ typedef struct PCIQXLDevice {
     /* user-friendly properties (in megabytes) */
     uint32_t          ram_size_mb;
     uint32_t          vram_size_mb;
+    uint32_t          vram32_size_mb;
+
+    /* qxl_render_update state */
+    int                render_update_cookie_num;
+    int                num_dirty_rects;
+    QXLRect            dirty[QXL_NUM_DIRTY_RECTS];
+    QEMUBH            *update_area_bh;
 } PCIQXLDevice;
 
 #define PANIC_ON(x) if ((x)) {                         \
@@ -108,11 +123,7 @@ typedef struct PCIQXLDevice {
         }                                                               \
     } while (0)
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
 #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
-#else
-#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06
-#endif
 
 /* qxl.c */
 void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
@@ -122,7 +133,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
                            struct QXLRect *area, struct QXLRect *dirty_rects,
                            uint32_t num_dirty_rects,
                            uint32_t clear_dirty_region,
-                           qxl_async_io async);
+                           qxl_async_io async, QXLCookie *cookie);
 void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count);
 void qxl_spice_oom(PCIQXLDevice *qxl);
@@ -138,9 +149,5 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
 void qxl_render_resize(PCIQXLDevice *qxl);
 void qxl_render_update(PCIQXLDevice *qxl);
 void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
-#if SPICE_INTERFACE_QXL_MINOR >= 1
-void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id,
-                                 struct QXLRect *area,
-                                 uint32_t clear_dirty_region,
-                                 int is_vga);
-#endif
+void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
+void qxl_render_update_area_bh(void *opaque);
diff --git a/hw/r2d.c b/hw/r2d.c
index c80f9e3a29..c55de0141b 100644
--- a/hw/r2d.c
+++ b/hw/r2d.c
@@ -192,16 +192,16 @@ static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem,
 }
 
 typedef struct ResetData {
-    CPUState *env;
+    CPUSH4State *env;
     uint32_t vector;
 } ResetData;
 
 static void main_cpu_reset(void *opaque)
 {
     ResetData *s = (ResetData *)opaque;
-    CPUState *env = s->env;
+    CPUSH4State *env = s->env;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->pc = s->vector;
 }
 
@@ -224,7 +224,7 @@ static void r2d_init(ram_addr_t ram_size,
 	      const char *kernel_filename, const char *kernel_cmdline,
 	      const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env;
+    CPUSH4State *env;
     ResetData *reset_info;
     struct SH7750State *s;
     MemoryRegion *sdram = g_new(MemoryRegion, 1);
diff --git a/hw/realview.c b/hw/realview.c
index ae1bbcdac3..cf55204c96 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -12,7 +12,6 @@
 #include "primecell.h"
 #include "devices.h"
 #include "pci.h"
-#include "usb-ohci.h"
 #include "net.h"
 #include "sysemu.h"
 #include "boards.h"
@@ -129,7 +128,7 @@ static void realview_init(ram_addr_t ram_size,
                      const char *initrd_filename, const char *cpu_model,
                      enum realview_board_type board_type)
 {
-    CPUState *env = NULL;
+    CPUARMState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
     MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
@@ -305,7 +304,7 @@ static void realview_init(ram_addr_t ram_size,
         sysbus_connect_irq(busdev, 3, pic[51]);
         pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
         if (usb_enabled) {
-            usb_ohci_init_pci(pci_bus, -1);
+            pci_create_simple(pci_bus, -1, "pci-ohci");
         }
         n = drive_get_max_bus(IF_SCSI);
         while (n >= 0) {
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index c450e4bb5b..be1f5f1061 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -85,7 +85,7 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
     return bus;
 }
 
-static void s390_virtio_irq(CPUState *env, int config_change, uint64_t token)
+static void s390_virtio_irq(CPUS390XState *env, int config_change, uint64_t token)
 {
     if (kvm_enabled()) {
         kvm_s390_virtio_irq(env, config_change, token);
@@ -116,7 +116,7 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev)
     s390_virtio_device_sync(dev);
 
     if (dev->qdev.hotplugged) {
-        CPUState *env = s390_cpu_addr2state(0);
+        CPUS390XState *env = s390_cpu_addr2state(0);
         s390_virtio_irq(env, VIRTIO_PARAM_DEV_ADD, dev->dev_offs);
     }
 
@@ -331,7 +331,7 @@ static void virtio_s390_notify(void *opaque, uint16_t vector)
 {
     VirtIOS390Device *dev = (VirtIOS390Device*)opaque;
     uint64_t token = s390_virtio_device_vq_token(dev, vector);
-    CPUState *env = s390_cpu_addr2state(0);
+    CPUS390XState *env = s390_cpu_addr2state(0);
 
     s390_virtio_irq(env, 0, token);
 }
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index 51123a7535..1ebe70d0e3 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -61,9 +61,9 @@
 #define MAX_BLK_DEVS                    10
 
 static VirtIOS390Bus *s390_bus;
-static CPUState **ipi_states;
+static CPUS390XState **ipi_states;
 
-CPUState *s390_cpu_addr2state(uint16_t cpu_addr)
+CPUS390XState *s390_cpu_addr2state(uint16_t cpu_addr)
 {
     if (cpu_addr >= smp_cpus) {
         return NULL;
@@ -72,7 +72,7 @@ CPUState *s390_cpu_addr2state(uint16_t cpu_addr)
     return ipi_states[cpu_addr];
 }
 
-int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall)
+int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall)
 {
     int r = 0, i;
 
@@ -129,7 +129,7 @@ int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall)
  */
 static unsigned s390_running_cpus;
 
-void s390_add_running_cpu(CPUState *env)
+void s390_add_running_cpu(CPUS390XState *env)
 {
     if (env->halted) {
         s390_running_cpus++;
@@ -138,7 +138,7 @@ void s390_add_running_cpu(CPUState *env)
     }
 }
 
-unsigned s390_del_running_cpu(CPUState *env)
+unsigned s390_del_running_cpu(CPUS390XState *env)
 {
     if (env->halted == 0) {
         assert(s390_running_cpus >= 1);
@@ -157,7 +157,7 @@ static void s390_init(ram_addr_t my_ram_size,
                       const char *initrd_filename,
                       const char *cpu_model)
 {
-    CPUState *env = NULL;
+    CPUS390XState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     ram_addr_t kernel_size = 0;
@@ -205,10 +205,10 @@ static void s390_init(ram_addr_t my_ram_size,
         cpu_model = "host";
     }
 
-    ipi_states = g_malloc(sizeof(CPUState *) * smp_cpus);
+    ipi_states = g_malloc(sizeof(CPUS390XState *) * smp_cpus);
 
     for (i = 0; i < smp_cpus; i++) {
-        CPUState *tmp_env;
+        CPUS390XState *tmp_env;
 
         tmp_env = cpu_init(cpu_model);
         if (!env) {
@@ -224,13 +224,17 @@ static void s390_init(ram_addr_t my_ram_size,
     s390_add_running_cpu(env);
 
     if (kernel_filename) {
-        kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0));
 
-        if (lduw_be_phys(KERN_IMAGE_START) != 0x0dd0) {
-            fprintf(stderr, "Specified image is not an s390 boot image\n");
-            exit(1);
+        kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, NULL,
+                               NULL, 1, ELF_MACHINE, 0);
+        if (kernel_size == -1UL) {
+            kernel_size = load_image_targphys(kernel_filename, 0, ram_size);
         }
-
+        /*
+         * we can not rely on the ELF entry point, since up to 3.2 this
+         * value was 0x800 (the SALIPL loader) and it wont work. For
+         * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
+         */
         env->psw.addr = KERN_IMAGE_START;
         env->psw.mask = 0x0000000180000000ULL;
     } else {
@@ -243,7 +247,7 @@ static void s390_init(ram_addr_t my_ram_size,
         }
 
         bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
-        bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR));
+        bios_size = load_image_targphys(bios_filename, ZIPL_LOAD_ADDR, 4096);
         g_free(bios_filename);
 
         if ((long)bios_size < 0) {
@@ -263,15 +267,17 @@ static void s390_init(ram_addr_t my_ram_size,
         while (kernel_size + 0x100000 > initrd_offset) {
             initrd_offset += 0x100000;
         }
-        initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset));
-
-        stq_be_phys(INITRD_PARM_START, initrd_offset);
-        stq_be_phys(INITRD_PARM_SIZE, initrd_size);
+        initrd_size = load_image_targphys(initrd_filename, initrd_offset,
+                                          ram_size - initrd_offset);
+        /* we have to overwrite values in the kernel image, which are "rom" */
+        memcpy(rom_ptr(INITRD_PARM_START), &initrd_offset, 8);
+        memcpy(rom_ptr(INITRD_PARM_SIZE), &initrd_size, 8);
     }
 
     if (kernel_cmdline) {
-        cpu_physical_memory_write(KERN_PARM_AREA, kernel_cmdline,
-                                  strlen(kernel_cmdline) + 1);
+        /* we have to overwrite values in the kernel image, which are "rom" */
+        memcpy(rom_ptr(KERN_PARM_AREA), kernel_cmdline,
+               strlen(kernel_cmdline) + 1);
     }
 
     /* Create VirtIO network adapters */
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 2cb5a18da2..8e76c5d32c 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -7,6 +7,7 @@
 #include "trace.h"
 #include "dma.h"
 
+static char *scsibus_get_dev_path(DeviceState *dev);
 static char *scsibus_get_fw_dev_path(DeviceState *dev);
 static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
 static void scsi_req_dequeue(SCSIRequest *req);
@@ -14,6 +15,7 @@ static void scsi_req_dequeue(SCSIRequest *req);
 static struct BusInfo scsi_bus_info = {
     .name  = "SCSI",
     .size  = sizeof(SCSIBus),
+    .get_dev_path = scsibus_get_dev_path,
     .get_fw_dev_path = scsibus_get_fw_dev_path,
     .props = (Property[]) {
         DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
@@ -1423,6 +1425,22 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
     sdev->unit_attention = sense;
 }
 
+static char *scsibus_get_dev_path(DeviceState *dev)
+{
+    SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
+    DeviceState *hba = dev->parent_bus->parent;
+    char *id = NULL;
+
+    if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) {
+        id = hba->parent_bus->info->get_dev_path(hba);
+    }
+    if (id) {
+        return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
+    } else {
+        return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
+    }
+}
+
 static char *scsibus_get_fw_dev_path(DeviceState *dev)
 {
     SCSIDevice *d = SCSI_DEVICE(dev);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index add399e97b..9949786e52 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -471,8 +471,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
 
         case 0x83: /* Device identification page, mandatory */
         {
-            int max_len = 255 - 8;
-            int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs));
+            const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs);
+            int max_len = s->serial ? 20 : 255 - 8;
+            int id_len = strlen(str);
 
             if (id_len > max_len) {
                 id_len = max_len;
@@ -486,7 +487,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
             outbuf[buflen++] = 0;   // reserved
             outbuf[buflen++] = id_len; // length of data following
 
-            memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len);
+            memcpy(outbuf+buflen, str, id_len);
             buflen += id_len;
             break;
         }
@@ -1152,9 +1153,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
     outbuf = r->iov.iov_base;
     switch (req->cmd.buf[0]) {
     case TEST_UNIT_READY:
-        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
-            goto not_ready;
-        }
+        assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
         break;
     case INQUIRY:
         buflen = scsi_disk_emulate_inquiry(req, outbuf);
@@ -1209,7 +1208,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
         memset(outbuf, 0, 8);
         bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
         if (!nb_sectors) {
-            goto not_ready;
+            scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+            return -1;
         }
         if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
             goto illegal_request;
@@ -1269,7 +1269,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
             memset(outbuf, 0, req->cmd.xfer);
             bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
             if (!nb_sectors) {
-                goto not_ready;
+                scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+                return -1;
             }
             if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
                 goto illegal_request;
@@ -1314,14 +1315,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
     buflen = MIN(buflen, req->cmd.xfer);
     return buflen;
 
-not_ready:
-    if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
-        scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
-    } else {
-        scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
-    }
-    return -1;
-
 illegal_request:
     if (r->req.status == -1) {
         scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
@@ -1356,6 +1349,30 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
 #endif
 
     switch (command) {
+    case INQUIRY:
+    case MODE_SENSE:
+    case MODE_SENSE_10:
+    case RESERVE:
+    case RESERVE_10:
+    case RELEASE:
+    case RELEASE_10:
+    case START_STOP:
+    case ALLOW_MEDIUM_REMOVAL:
+    case GET_CONFIGURATION:
+    case GET_EVENT_STATUS_NOTIFICATION:
+    case MECHANISM_STATUS:
+    case REQUEST_SENSE:
+        break;
+
+    default:
+        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+            return 0;
+        }
+        break;
+    }
+
+    switch (command) {
     case TEST_UNIT_READY:
     case INQUIRY:
     case MODE_SENSE:
diff --git a/hw/sh.h b/hw/sh.h
index 6488db25c4..40df18c5dc 100644
--- a/hw/sh.h
+++ b/hw/sh.h
@@ -11,7 +11,7 @@
 struct SH7750State;
 struct MemoryRegion;
 
-struct SH7750State *sh7750_init(CPUState * cpu, struct MemoryRegion *sysmem);
+struct SH7750State *sh7750_init(CPUSH4State * cpu, struct MemoryRegion *sysmem);
 
 typedef struct {
     /* The callback will be triggered if any of the designated lines change */
diff --git a/hw/sh7750.c b/hw/sh7750.c
index 4f4d8e7d05..e7129283d1 100644
--- a/hw/sh7750.c
+++ b/hw/sh7750.c
@@ -712,7 +712,7 @@ static void sh7750_mmct_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static const struct MemoryRegionOps sh7750_mmct_ops = {
+static const MemoryRegionOps sh7750_mmct_ops = {
     .read = sh7750_mmct_read,
     .write = sh7750_mmct_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
diff --git a/hw/sh_intc.c b/hw/sh_intc.c
index b24ec77582..7d31ced858 100644
--- a/hw/sh_intc.c
+++ b/hw/sh_intc.c
@@ -283,7 +283,7 @@ static void sh_intc_write(void *opaque, target_phys_addr_t offset,
 #endif
 }
 
-static const struct MemoryRegionOps sh_intc_ops = {
+static const MemoryRegionOps sh_intc_ops = {
     .read = sh_intc_read,
     .write = sh_intc_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
diff --git a/hw/shix.c b/hw/shix.c
index e259c17c52..dd9ce174f9 100644
--- a/hw/shix.c
+++ b/hw/shix.c
@@ -43,7 +43,7 @@ static void shix_init(ram_addr_t ram_size,
 	       const char *initrd_filename, const char *cpu_model)
 {
     int ret;
-    CPUState *env;
+    CPUSH4State *env;
     struct SH7750State *s;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *rom = g_new(MemoryRegion, 1);
diff --git a/hw/spapr.c b/hw/spapr.c
index dffb6a2a50..bfaf260d54 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -83,7 +83,8 @@
 
 sPAPREnvironment *spapr;
 
-qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
+qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num,
+                            enum xics_irq_type type)
 {
     uint32_t irq;
     qemu_irq qirq;
@@ -95,7 +96,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
         irq = spapr->next_irq++;
     }
 
-    qirq = xics_find_qirq(spapr->icp, irq);
+    qirq = xics_assign_irq(spapr->icp, irq, type);
     if (!qirq) {
         return NULL;
     }
@@ -110,7 +111,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
 static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr)
 {
     int ret = 0, offset;
-    CPUState *env;
+    CPUPPCState *env;
     char cpu_model[32];
     int smt = kvmppc_smt_threads();
 
@@ -155,7 +156,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
                                    long hash_shift)
 {
     void *fdt;
-    CPUState *env;
+    CPUPPCState *env;
     uint64_t mem_reg_property[2];
     uint32_t start_prop = cpu_to_be32(initrd_base);
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
@@ -476,7 +477,7 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
     return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
 }
 
-static void emulate_spapr_hypercall(CPUState *env)
+static void emulate_spapr_hypercall(CPUPPCState *env)
 {
     env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
 }
@@ -502,6 +503,13 @@ static void spapr_reset(void *opaque)
 
 }
 
+static void spapr_cpu_reset(void *opaque)
+{
+    CPUPPCState *env = opaque;
+
+    cpu_state_reset(env);
+}
+
 /* pSeries LPAR / sPAPR hardware init */
 static void ppc_spapr_init(ram_addr_t ram_size,
                            const char *boot_device,
@@ -510,7 +518,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
                            const char *initrd_filename,
                            const char *cpu_model)
 {
-    CPUState *env;
+    CPUPPCState *env;
     int i;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
@@ -560,7 +568,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
         }
         /* Set time-base frequency to 512 MHz */
         cpu_ppc_tb_init(env, TIMEBASE_FREQ);
-        qemu_register_reset((QEMUResetHandler *)&cpu_reset, env);
+        qemu_register_reset(spapr_cpu_reset, env);
 
         env->hreset_vector = 0x60;
         env->hreset_excp_prefix = 0;
diff --git a/hw/spapr.h b/hw/spapr.h
index e946a3433e..11160b02da 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -278,15 +278,26 @@ extern sPAPREnvironment *spapr;
     do { } while (0)
 #endif
 
-typedef target_ulong (*spapr_hcall_fn)(CPUState *env, sPAPREnvironment *spapr,
+typedef target_ulong (*spapr_hcall_fn)(CPUPPCState *env, sPAPREnvironment *spapr,
                                        target_ulong opcode,
                                        target_ulong *args);
 
 void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
-target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
+target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode,
                              target_ulong *args);
 
-qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num);
+qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num,
+                            enum xics_irq_type type);
+
+static inline qemu_irq spapr_allocate_msi(uint32_t hint, uint32_t *irq_num)
+{
+    return spapr_allocate_irq(hint, irq_num, XICS_MSI);
+}
+
+static inline qemu_irq spapr_allocate_lsi(uint32_t hint, uint32_t *irq_num)
+{
+    return spapr_allocate_irq(hint, irq_num, XICS_LSI);
+}
 
 static inline uint32_t rtas_ld(target_ulong phys, int n)
 {
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 6ac7384013..634763eefd 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -92,7 +92,7 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
     return rb;
 }
 
-static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_enter(CPUPPCState *env, sPAPREnvironment *spapr,
                             target_ulong opcode, target_ulong *args)
 {
     target_ulong flags = args[0];
@@ -181,7 +181,7 @@ enum {
     REMOVE_HW = 3,
 };
 
-static target_ulong remove_hpte(CPUState *env, target_ulong ptex,
+static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
                                 target_ulong avpn,
                                 target_ulong flags,
                                 target_ulong *vp, target_ulong *rp)
@@ -219,7 +219,7 @@ static target_ulong remove_hpte(CPUState *env, target_ulong ptex,
     return REMOVE_SUCCESS;
 }
 
-static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_remove(CPUPPCState *env, sPAPREnvironment *spapr,
                              target_ulong opcode, target_ulong *args)
 {
     target_ulong flags = args[0];
@@ -265,7 +265,7 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
 
 #define H_BULK_REMOVE_MAX_BATCH        4
 
-static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_bulk_remove(CPUPPCState *env, sPAPREnvironment *spapr,
                                   target_ulong opcode, target_ulong *args)
 {
     int i;
@@ -311,7 +311,7 @@ static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_protect(CPUPPCState *env, sPAPREnvironment *spapr,
                               target_ulong opcode, target_ulong *args)
 {
     target_ulong flags = args[0];
@@ -356,7 +356,7 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_set_dabr(CPUPPCState *env, sPAPREnvironment *spapr,
                                target_ulong opcode, target_ulong *args)
 {
     /* FIXME: actually implement this */
@@ -375,7 +375,7 @@ static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
 #define VPA_SHARED_PROC_OFFSET 0x9
 #define VPA_SHARED_PROC_VAL    0x2
 
-static target_ulong register_vpa(CPUState *env, target_ulong vpa)
+static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa)
 {
     uint16_t size;
     uint8_t tmp;
@@ -410,7 +410,7 @@ static target_ulong register_vpa(CPUState *env, target_ulong vpa)
     return H_SUCCESS;
 }
 
-static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
+static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa)
 {
     if (env->slb_shadow) {
         return H_RESOURCE;
@@ -424,7 +424,7 @@ static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
     return H_SUCCESS;
 }
 
-static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
+static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr)
 {
     uint32_t size;
 
@@ -451,13 +451,13 @@ static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
     return H_SUCCESS;
 }
 
-static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr)
+static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr)
 {
     env->slb_shadow = 0;
     return H_SUCCESS;
 }
 
-static target_ulong register_dtl(CPUState *env, target_ulong addr)
+static target_ulong register_dtl(CPUPPCState *env, target_ulong addr)
 {
     uint32_t size;
 
@@ -482,7 +482,7 @@ static target_ulong register_dtl(CPUState *env, target_ulong addr)
     return H_SUCCESS;
 }
 
-static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
+static target_ulong deregister_dtl(CPUPPCState *emv, target_ulong addr)
 {
     env->dispatch_trace_log = 0;
     env->dtl_size = 0;
@@ -490,14 +490,14 @@ static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
     return H_SUCCESS;
 }
 
-static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_register_vpa(CPUPPCState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
 {
     target_ulong flags = args[0];
     target_ulong procno = args[1];
     target_ulong vpa = args[2];
     target_ulong ret = H_PARAMETER;
-    CPUState *tenv;
+    CPUPPCState *tenv;
 
     for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) {
         if (tenv->cpu_index == procno) {
@@ -538,7 +538,7 @@ static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
     return ret;
 }
 
-static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_cede(CPUPPCState *env, sPAPREnvironment *spapr,
                            target_ulong opcode, target_ulong *args)
 {
     env->msr |= (1ULL << MSR_EE);
@@ -549,7 +549,7 @@ static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_rtas(CPUPPCState *env, sPAPREnvironment *spapr,
                            target_ulong opcode, target_ulong *args)
 {
     target_ulong rtas_r3 = args[0];
@@ -561,7 +561,7 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
                            nret, rtas_r3 + 12 + 4*nargs);
 }
 
-static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_logical_load(CPUPPCState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
 {
     target_ulong size = args[0];
@@ -584,7 +584,7 @@ static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr,
     return H_PARAMETER;
 }
 
-static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_logical_store(CPUPPCState *env, sPAPREnvironment *spapr,
                                     target_ulong opcode, target_ulong *args)
 {
     target_ulong size = args[0];
@@ -608,14 +608,14 @@ static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr,
     return H_PARAMETER;
 }
 
-static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_logical_icbi(CPUPPCState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
 {
     /* Nothing to do on emulation, KVM will trap this in the kernel */
     return H_SUCCESS;
 }
 
-static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_logical_dcbf(CPUPPCState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
 {
     /* Nothing to do on emulation, KVM will trap this in the kernel */
@@ -644,7 +644,7 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
     *slot = fn;
 }
 
-target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
+target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode,
                              target_ulong *args)
 {
     if (msr_pr) {
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index 77d40479c8..cfc777804b 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -254,7 +254,7 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
     return 0;
 }
 
-static target_ulong h_register_logical_lan(CPUState *env,
+static target_ulong h_register_logical_lan(CPUPPCState *env,
                                            sPAPREnvironment *spapr,
                                            target_ulong opcode,
                                            target_ulong *args)
@@ -320,7 +320,7 @@ static target_ulong h_register_logical_lan(CPUState *env,
 }
 
 
-static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_free_logical_lan(CPUPPCState *env, sPAPREnvironment *spapr,
                                        target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -343,7 +343,7 @@ static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_add_logical_lan_buffer(CPUState *env,
+static target_ulong h_add_logical_lan_buffer(CPUPPCState *env,
                                              sPAPREnvironment *spapr,
                                              target_ulong opcode,
                                              target_ulong *args)
@@ -392,7 +392,7 @@ static target_ulong h_add_logical_lan_buffer(CPUState *env,
     return H_SUCCESS;
 }
 
-static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_send_logical_lan(CPUPPCState *env, sPAPREnvironment *spapr,
                                        target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -461,7 +461,7 @@ static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_multicast_ctrl(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_multicast_ctrl(CPUPPCState *env, sPAPREnvironment *spapr,
                                      target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c
index cfdd9ddd41..e7ef551c1c 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -32,13 +32,6 @@
 
 #include "hw/pci_internals.h"
 
-static const uint32_t bars[] = {
-    PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1,
-    PCI_BASE_ADDRESS_2, PCI_BASE_ADDRESS_3,
-    PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5
-    /*, PCI_ROM_ADDRESS*/
-};
-
 static PCIDevice *find_dev(sPAPREnvironment *spapr,
                            uint64_t buid, uint32_t config_addr)
 {
@@ -187,69 +180,6 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level)
     qemu_set_irq(phb->lsi_table[irq_num].qirq, level);
 }
 
-static int spapr_phb_init(SysBusDevice *s)
-{
-    sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s);
-    int i;
-
-    /* Initialize the LSI table */
-    for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) {
-        qemu_irq qirq;
-        uint32_t num;
-
-        qirq = spapr_allocate_irq(0, &num);
-        if (!qirq) {
-            return -1;
-        }
-
-        phb->lsi_table[i].dt_irq = num;
-        phb->lsi_table[i].qirq = qirq;
-    }
-
-    return 0;
-}
-
-static int spapr_main_pci_host_init(PCIDevice *d)
-{
-    return 0;
-}
-
-static void spapr_main_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = spapr_main_pci_host_init;
-}
-
-static TypeInfo spapr_main_pci_host_info = {
-    .name          = "spapr-pci-host-bridge-pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init    = spapr_main_pci_host_class_init,
-};
-
-static void spapr_phb_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = spapr_phb_init;
-}
-
-static TypeInfo spapr_phb_info = {
-    .name          = "spapr-pci-host-bridge",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(sPAPRPHBState),
-    .class_init    = spapr_phb_class_init,
-};
-
-static void spapr_register_types(void)
-{
-    type_register_static(&spapr_phb_info);
-    type_register_static(&spapr_main_pci_host_info);
-}
-
-type_init(spapr_register_types)
-
 static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr,
                               unsigned size)
 {
@@ -281,41 +211,35 @@ static void spapr_io_write(void *opaque, target_phys_addr_t addr,
     assert(0);
 }
 
-static MemoryRegionOps spapr_io_ops = {
+static const MemoryRegionOps spapr_io_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
     .read = spapr_io_read,
     .write = spapr_io_write
 };
 
-void spapr_create_phb(sPAPREnvironment *spapr,
-                      const char *busname, uint64_t buid,
-                      uint64_t mem_win_addr, uint64_t mem_win_size,
-                      uint64_t io_win_addr)
+/*
+ * PHB PCI device
+ */
+static int spapr_phb_init(SysBusDevice *s)
 {
-    DeviceState *dev;
-    SysBusDevice *s;
-    sPAPRPHBState *phb;
+    sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s);
+    char *namebuf;
+    int i;
     PCIBus *bus;
-    char namebuf[strlen(busname)+11];
-
-    dev = qdev_create(NULL, "spapr-pci-host-bridge");
-    qdev_init_nofail(dev);
-    s = sysbus_from_qdev(dev);
-    phb = FROM_SYSBUS(sPAPRPHBState, s);
 
-    phb->mem_win_addr = mem_win_addr;
+    phb->dtbusname = g_strdup_printf("pci@%" PRIx64, phb->buid);
+    namebuf = alloca(strlen(phb->dtbusname) + 32);
 
-    sprintf(namebuf, "%s-mem", busname);
+    /* Initialize memory regions */
+    sprintf(namebuf, "%s.mmio", phb->dtbusname);
     memory_region_init(&phb->memspace, namebuf, INT64_MAX);
 
-    sprintf(namebuf, "%s-memwindow", busname);
+    sprintf(namebuf, "%s.mmio-alias", phb->dtbusname);
     memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace,
-                             SPAPR_PCI_MEM_WIN_BUS_OFFSET, mem_win_size);
-    memory_region_add_subregion(get_system_memory(), mem_win_addr,
+                             SPAPR_PCI_MEM_WIN_BUS_OFFSET, phb->mem_win_size);
+    memory_region_add_subregion(get_system_memory(), phb->mem_win_addr,
                                 &phb->memwindow);
 
-    phb->io_win_addr = io_win_addr;
-
     /* On ppc, we only have MMIO no specific IO space from the CPU
      * perspective.  In theory we ought to be able to embed the PCI IO
      * memory region direction in the system memory space.  However,
@@ -324,33 +248,92 @@ void spapr_create_phb(sPAPREnvironment *spapr,
      * system io address space.  This hack to bounce things via
      * system_io works around the problem until all the users of
      * old_portion are updated */
-    sprintf(namebuf, "%s-io", busname);
+    sprintf(namebuf, "%s.io", phb->dtbusname);
     memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE);
     /* FIXME: fix to support multiple PHBs */
     memory_region_add_subregion(get_system_io(), 0, &phb->iospace);
 
-    sprintf(namebuf, "%s-iowindow", busname);
+    sprintf(namebuf, "%s.io-alias", phb->dtbusname);
     memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb,
                           namebuf, SPAPR_PCI_IO_WIN_SIZE);
-    memory_region_add_subregion(get_system_memory(), io_win_addr,
+    memory_region_add_subregion(get_system_memory(), phb->io_win_addr,
                                 &phb->iowindow);
 
-    phb->host_state.bus = bus = pci_register_bus(&phb->busdev.qdev, busname,
-                                                 pci_spapr_set_irq,
-                                                 pci_spapr_map_irq,
-                                                 phb,
-                                                 &phb->memspace, &phb->iospace,
-                                                 PCI_DEVFN(0, 0),
-                                                 SPAPR_PCI_NUM_LSI);
+    bus = pci_register_bus(&phb->busdev.qdev,
+                           phb->busname ? phb->busname : phb->dtbusname,
+                           pci_spapr_set_irq, pci_spapr_map_irq, phb,
+                           &phb->memspace, &phb->iospace,
+                           PCI_DEVFN(0, 0), SPAPR_PCI_NUM_LSI);
+    phb->host_state.bus = bus;
+
+    QLIST_INSERT_HEAD(&spapr->phbs, phb, list);
+
+    /* Initialize the LSI table */
+    for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) {
+        qemu_irq qirq;
+        uint32_t num;
+
+        qirq = spapr_allocate_lsi(0, &num);
+        if (!qirq) {
+            return -1;
+        }
+
+        phb->lsi_table[i].dt_irq = num;
+        phb->lsi_table[i].qirq = qirq;
+    }
+
+    return 0;
+}
+
+static Property spapr_phb_properties[] = {
+    DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, 0),
+    DEFINE_PROP_STRING("busname", sPAPRPHBState, busname),
+    DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, 0),
+    DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, 0x20000000),
+    DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, 0),
+    DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, 0x10000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_phb_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    sdc->init = spapr_phb_init;
+    dc->props = spapr_phb_properties;
 
     spapr_rtas_register("read-pci-config", rtas_read_pci_config);
     spapr_rtas_register("write-pci-config", rtas_write_pci_config);
     spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config);
     spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config);
+}
 
-    QLIST_INSERT_HEAD(&spapr->phbs, phb, list);
+static TypeInfo spapr_phb_info = {
+    .name          = "spapr-pci-host-bridge",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(sPAPRPHBState),
+    .class_init    = spapr_phb_class_init,
+};
+
+void spapr_create_phb(sPAPREnvironment *spapr,
+                      const char *busname, uint64_t buid,
+                      uint64_t mem_win_addr, uint64_t mem_win_size,
+                      uint64_t io_win_addr)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, spapr_phb_info.name);
 
-    /* pci_bus_set_mem_base(bus, mem_va_start - SPAPR_PCI_MEM_BAR_START); */
+    if (busname) {
+        qdev_prop_set_string(dev, "busname", g_strdup(busname));
+    }
+    qdev_prop_set_uint64(dev, "buid", buid);
+    qdev_prop_set_uint64(dev, "mem_win_addr", mem_win_addr);
+    qdev_prop_set_uint64(dev, "mem_win_size", mem_win_size);
+    qdev_prop_set_uint64(dev, "io_win_addr", io_win_addr);
+
+    qdev_init_nofail(dev);
 }
 
 /* Macros to operate with address in OF binding to PCI */
@@ -442,3 +425,9 @@ int spapr_populate_pci_devices(sPAPRPHBState *phb,
 
     return 0;
 }
+
+static void register_types(void)
+{
+    type_register_static(&spapr_phb_info);
+}
+type_init(register_types)
diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h
index 213340c915..039f85bd4b 100644
--- a/hw/spapr_pci.h
+++ b/hw/spapr_pci.h
@@ -33,9 +33,11 @@ typedef struct sPAPRPHBState {
     PCIHostState host_state;
 
     uint64_t buid;
+    char *busname;
+    char *dtbusname;
 
     MemoryRegion memspace, iospace;
-    target_phys_addr_t mem_win_addr, io_win_addr;
+    target_phys_addr_t mem_win_addr, mem_win_size, io_win_addr, io_win_size;
     MemoryRegion memwindow, iowindow;
 
     struct {
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index c0723b3039..09465853ba 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -118,7 +118,7 @@ static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
                                          uint32_t nret, target_ulong rets)
 {
     target_ulong id;
-    CPUState *env;
+    CPUPPCState *env;
 
     if (nargs != 1 || nret != 2) {
         rtas_st(rets, 0, -3);
@@ -151,7 +151,7 @@ static void rtas_start_cpu(sPAPREnvironment *spapr,
                            uint32_t nret, target_ulong rets)
 {
     target_ulong id, start, r3;
-    CPUState *env;
+    CPUPPCState *env;
 
     if (nargs != 3 || nret != 1) {
         rtas_st(rets, 0, -3);
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index ea317efbe4..dbf5a9017e 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -194,7 +194,7 @@ static void rtce_init(VIOsPAPRDevice *dev)
     }
 }
 
-static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_put_tce(CPUPPCState *env, sPAPREnvironment *spapr,
                               target_ulong opcode, target_ulong *args)
 {
     target_ulong liobn = args[0];
@@ -405,7 +405,7 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr)
 /*
  * CRQ handling
  */
-static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_reg_crq(CPUPPCState *env, sPAPREnvironment *spapr,
                               target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -453,7 +453,7 @@ static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_free_crq(CPUPPCState *env, sPAPREnvironment *spapr,
                                target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -474,7 +474,7 @@ static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_send_crq(CPUPPCState *env, sPAPREnvironment *spapr,
                                target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -498,7 +498,7 @@ static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr,
     return H_HARDWARE;
 }
 
-static target_ulong h_enable_crq(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_enable_crq(CPUPPCState *env, sPAPREnvironment *spapr,
                                  target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -670,7 +670,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
         dev->qdev.id = id;
     }
 
-    dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num);
+    dev->qirq = spapr_allocate_msi(dev->vio_irq_num, &dev->vio_irq_num);
     if (!dev->qirq) {
         return -1;
     }
@@ -680,7 +680,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
     return pc->init(dev);
 }
 
-static target_ulong h_vio_signal(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_vio_signal(CPUPPCState *env, sPAPREnvironment *spapr,
                                  target_ulong opcode,
                                  target_ulong *args)
 {
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index 3efe24211e..60e22b1600 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -72,7 +72,7 @@ static int spapr_vty_init(VIOsPAPRDevice *sdev)
 /* Forward declaration */
 static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg);
 
-static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_put_term_char(CPUPPCState *env, sPAPREnvironment *spapr,
                                     target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
@@ -99,7 +99,7 @@ static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_get_term_char(CPUPPCState *env, sPAPREnvironment *spapr,
                                     target_ulong opcode, target_ulong *args)
 {
     target_ulong reg = args[0];
diff --git a/hw/strongarm.h b/hw/strongarm.h
index 684f61bee3..02acac3db1 100644
--- a/hw/strongarm.h
+++ b/hw/strongarm.h
@@ -53,7 +53,7 @@ enum {
 };
 
 typedef struct {
-    CPUState *env;
+    CPUARMState *env;
     MemoryRegion sdram;
     DeviceState *pic;
     DeviceState *gpio;
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 99fb219b3a..7bcbf37e98 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -228,7 +228,7 @@ void sun4m_irq_info(Monitor *mon)
         slavio_irq_info(mon, slavio_intctl);
 }
 
-void cpu_check_irqs(CPUState *env)
+void cpu_check_irqs(CPUSPARCState *env)
 {
     if (env->pil_in && (env->interrupt_index == 0 ||
                         (env->interrupt_index & ~15) == TT_EXTINT)) {
@@ -253,7 +253,7 @@ void cpu_check_irqs(CPUState *env)
     }
 }
 
-static void cpu_kick_irq(CPUState *env)
+static void cpu_kick_irq(CPUSPARCState *env)
 {
     env->halted = 0;
     cpu_check_irqs(env);
@@ -262,7 +262,7 @@ static void cpu_kick_irq(CPUState *env)
 
 static void cpu_set_irq(void *opaque, int irq, int level)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
     if (level) {
         trace_sun4m_cpu_set_irq_raise(irq);
@@ -281,17 +281,17 @@ static void dummy_cpu_set_irq(void *opaque, int irq, int level)
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->halted = 0;
 }
 
 static void secondary_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     env->halted = 1;
 }
 
@@ -809,7 +809,7 @@ static TypeInfo ram_info = {
 static void cpu_devinit(const char *cpu_model, unsigned int id,
                         uint64_t prom_addr, qemu_irq **cpu_irqs)
 {
-    CPUState *env;
+    CPUSPARCState *env;
 
     env = cpu_init(cpu_model);
     if (!env) {
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 423108f236..237e20c1bf 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -81,7 +81,7 @@
 #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
 #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
 
-#define MAX_PILS 16
+#define IVEC_MAX             0x30
 
 #define TICK_MAX             0x7fffffffffffffffULL
 
@@ -243,7 +243,7 @@ static unsigned long sun4u_load_kernel(const char *kernel_filename,
     return kernel_size;
 }
 
-void cpu_check_irqs(CPUState *env)
+void cpu_check_irqs(CPUSPARCState *env)
 {
     uint32_t pil = env->pil_in |
                   (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER));
@@ -297,30 +297,36 @@ void cpu_check_irqs(CPUState *env)
     }
 }
 
-static void cpu_kick_irq(CPUState *env)
+static void cpu_kick_irq(CPUSPARCState *env)
 {
     env->halted = 0;
     cpu_check_irqs(env);
     qemu_cpu_kick(env);
 }
 
-static void cpu_set_irq(void *opaque, int irq, int level)
+static void cpu_set_ivec_irq(void *opaque, int irq, int level)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
     if (level) {
-        CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
-        env->pil_in |= 1 << irq;
-        cpu_kick_irq(env);
-    } else {
-        CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
-        env->pil_in &= ~(1 << irq);
-        cpu_check_irqs(env);
+        CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
+        env->interrupt_index = TT_IVEC;
+        env->pil_in |= 1 << 5;
+        env->ivec_status |= 0x20;
+        env->ivec_data[0] = (0x1f << 6) | irq;
+        env->ivec_data[1] = 0;
+        env->ivec_data[2] = 0;
+        cpu_interrupt(env, CPU_INTERRUPT_HARD);
+      } else {
+        CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
+        env->pil_in &= ~(1 << 5);
+        env->ivec_status &= ~0x20;
+        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
     }
 }
 
 typedef struct ResetData {
-    CPUState *env;
+    CPUSPARCState *env;
     uint64_t prom_addr;
 } ResetData;
 
@@ -344,7 +350,7 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s)
     qemu_get_timer(f, s->qtimer);
 }
 
-static CPUTimer* cpu_timer_create(const char* name, CPUState *env,
+static CPUTimer* cpu_timer_create(const char* name, CPUSPARCState *env,
                                   QEMUBHFunc *cb, uint32_t frequency,
                                   uint64_t disabled_mask)
 {
@@ -373,10 +379,10 @@ static void cpu_timer_reset(CPUTimer *timer)
 static void main_cpu_reset(void *opaque)
 {
     ResetData *s = (ResetData *)opaque;
-    CPUState *env = s->env;
+    CPUSPARCState *env = s->env;
     static unsigned int nr_resets;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
 
     cpu_timer_reset(env->tick);
     cpu_timer_reset(env->stick);
@@ -396,7 +402,7 @@ static void main_cpu_reset(void *opaque)
 
 static void tick_irq(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
     CPUTimer* timer = env->tick;
 
@@ -413,7 +419,7 @@ static void tick_irq(void *opaque)
 
 static void stick_irq(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
     CPUTimer* timer = env->stick;
 
@@ -430,7 +436,7 @@ static void stick_irq(void *opaque)
 
 static void hstick_irq(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUSPARCState *env = opaque;
 
     CPUTimer* timer = env->hstick;
 
@@ -521,13 +527,29 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
     }
 }
 
-static void dummy_isa_irq_handler(void *opaque, int n, int level)
+static void isa_irq_handler(void *opaque, int n, int level)
 {
+    static const int isa_irq_to_ivec[16] = {
+        [1] = 0x29, /* keyboard */
+        [4] = 0x2b, /* serial */
+        [6] = 0x27, /* floppy */
+        [7] = 0x22, /* parallel */
+        [12] = 0x2a, /* mouse */
+    };
+    qemu_irq *irqs = opaque;
+    int ivec;
+
+    assert(n < 16);
+    ivec = isa_irq_to_ivec[n];
+    EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec);
+    if (ivec) {
+        qemu_set_irq(irqs[ivec], level);
+    }
 }
 
 /* EBUS (Eight bit bus) bridge */
 static ISABus *
-pci_ebus_init(PCIBus *bus, int devfn)
+pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
 {
     qemu_irq *isa_irq;
     PCIDevice *pci_dev;
@@ -536,7 +558,7 @@ pci_ebus_init(PCIBus *bus, int devfn)
     pci_dev = pci_create_simple(bus, devfn, "ebus");
     isa_bus = DO_UPCAST(ISABus, qbus,
                         qdev_get_child_bus(&pci_dev->qdev, "isa.0"));
-    isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
+    isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16);
     isa_bus_irqs(isa_bus, isa_irq);
     return isa_bus;
 }
@@ -714,9 +736,9 @@ static TypeInfo ram_info = {
     .class_init    = ram_class_init,
 };
 
-static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
+static CPUSPARCState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
 {
-    CPUState *env;
+    CPUSPARCState *env;
     ResetData *reset_info;
 
     uint32_t   tick_frequency = 100*1000000;
@@ -755,13 +777,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
                         const char *initrd_filename, const char *cpu_model,
                         const struct hwdef *hwdef)
 {
-    CPUState *env;
+    CPUSPARCState *env;
     M48t59State *nvram;
     unsigned int i;
     long initrd_size, kernel_size;
     PCIBus *pci_bus, *pci_bus2, *pci_bus3;
     ISABus *isa_bus;
-    qemu_irq *irq;
+    qemu_irq *ivec_irqs, *pbm_irqs;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     DriveInfo *fd[MAX_FD];
     void *fw_cfg;
@@ -774,14 +796,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
 
     prom_init(hwdef->prom_addr, bios_name);
 
-
-    irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
-    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2,
-                           &pci_bus3);
+    ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX);
+    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2,
+                           &pci_bus3, &pbm_irqs);
     pci_vga_init(pci_bus);
 
     // XXX Should be pci_bus3
-    isa_bus = pci_ebus_init(pci_bus, -1);
+    isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs);
 
     i = 0;
     if (hwdef->console_serial_base) {
diff --git a/hw/usb-ohci.h b/hw/usb-ohci.h
deleted file mode 100644
index eefcef3602..0000000000
--- a/hw/usb-ohci.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef QEMU_USB_OHCI_H
-#define QEMU_USB_OHCI_H
-
-#include "qemu-common.h"
-
-void usb_ohci_init_pci(struct PCIBus *bus, int devfn);
-
-#endif
-
diff --git a/hw/usb-uhci.h b/hw/usb-uhci.h
deleted file mode 100644
index 3e4d3777d4..0000000000
--- a/hw/usb-uhci.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef QEMU_USB_UHCI_H
-#define QEMU_USB_UHCI_H
-
-#include "qemu-common.h"
-
-void usb_uhci_piix3_init(PCIBus *bus, int devfn);
-void usb_uhci_piix4_init(PCIBus *bus, int devfn);
-void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn);
-
-#endif
diff --git a/hw/usb.h b/hw/usb.h
index 4470ea890a..e95085f0b3 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -39,11 +39,12 @@
 #define USB_TOKEN_IN    0x69 /* device -> host */
 #define USB_TOKEN_OUT   0xe1 /* host -> device */
 
-#define USB_RET_NODEV  (-1)
-#define USB_RET_NAK    (-2)
-#define USB_RET_STALL  (-3)
-#define USB_RET_BABBLE (-4)
-#define USB_RET_ASYNC  (-5)
+#define USB_RET_NODEV   (-1)
+#define USB_RET_NAK     (-2)
+#define USB_RET_STALL   (-3)
+#define USB_RET_BABBLE  (-4)
+#define USB_RET_IOERROR (-5)
+#define USB_RET_ASYNC   (-6)
 
 #define USB_SPEED_LOW   0
 #define USB_SPEED_FULL  1
@@ -176,6 +177,7 @@ struct USBEndpoint {
     uint8_t type;
     uint8_t ifnum;
     int max_packet_size;
+    bool pipeline;
     USBDevice *dev;
     QTAILQ_HEAD(, USBPacket) queue;
 };
@@ -325,6 +327,7 @@ struct USBPacket {
     int pid;
     USBEndpoint *ep;
     QEMUIOVector iov;
+    uint64_t parameter; /* control transfers */
     int result; /* transfer length or USB_RET_* status code */
     /* Internal use by the USB layer.  */
     USBPacketState state;
@@ -333,6 +336,7 @@ struct USBPacket {
 
 void usb_packet_init(USBPacket *p);
 void usb_packet_set_state(USBPacket *p, USBPacketState state);
+void usb_packet_check_state(USBPacket *p, USBPacketState expected);
 void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep);
 void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
 int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
@@ -363,6 +367,7 @@ void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum);
 void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
                                 uint16_t raw);
 int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
+void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
 
 void usb_attach(USBPort *port);
 void usb_detach(USBPort *port);
@@ -373,12 +378,12 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
 int set_usb_string(uint8_t *buf, const char *str);
 
 /* usb-linux.c */
-USBDevice *usb_host_device_open(const char *devname);
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
 int usb_host_device_close(const char *devname);
 void usb_host_info(Monitor *mon);
 
 /* usb-bt.c */
-USBDevice *usb_bt_init(HCIInfo *hci);
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci);
 
 /* usb ports of the VM */
 
@@ -431,7 +436,8 @@ struct USBBusOps {
 void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
 USBBus *usb_bus_find(int busnr);
 void usb_legacy_register(const char *typename, const char *usbdevice_name,
-                         USBDevice *(*usbdevice_init)(const char *params));
+                         USBDevice *(*usbdevice_init)(USBBus *bus,
+                                                      const char *params));
 USBDevice *usb_create(USBBus *bus, const char *name);
 USBDevice *usb_create_simple(USBBus *bus, const char *name);
 USBDevice *usbdevice_create(const char *cmdline);
diff --git a/hw/usb-bus.c b/hw/usb/bus.c
index ae79a4527b..d3f835895d 100644
--- a/hw/usb-bus.c
+++ b/hw/usb/bus.c
@@ -1,6 +1,6 @@
-#include "hw.h"
-#include "usb.h"
-#include "qdev.h"
+#include "hw/hw.h"
+#include "hw/usb.h"
+#include "hw/qdev.h"
 #include "sysemu.h"
 #include "monitor.h"
 #include "trace.h"
@@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory
 {
     const char *name;
     const char *usbdevice_name;
-    USBDevice *(*usbdevice_init)(const char *params);
+    USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
 } LegacyUSBFactory;
 
 static GSList *legacy_usb_factory;
 
 void usb_legacy_register(const char *typename, const char *usbdevice_name,
-                         USBDevice *(*usbdevice_init)(const char *params))
+                         USBDevice *(*usbdevice_init)(USBBus *bus,
+                                                      const char *params))
 {
     if (usbdevice_name) {
         LegacyUSBFactory *f = g_malloc0(sizeof(*f));
@@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name)
 {
     DeviceState *dev;
 
-#if 1
-    /* temporary stopgap until all usb is properly qdev-ified */
-    if (!bus) {
-        bus = usb_bus_find(-1);
-        if (!bus)
-            return NULL;
-        error_report("%s: no bus specified, using \"%s\" for \"%s\"",
-                __FUNCTION__, bus->qbus.name, name);
-    }
-#endif
-
     dev = qdev_create(&bus->qbus, name);
     return USB_DEVICE(dev);
 }
@@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline)
         }
         return usb_create_simple(bus, f->name);
     }
-    return f->usbdevice_init(params);
+    return f->usbdevice_init(bus, params);
 }
 
 static void usb_device_class_init(ObjectClass *klass, void *data)
diff --git a/hw/usb.c b/hw/usb/core.c
index e5b8f335dc..a4048fe3e0 100644
--- a/hw/usb.c
+++ b/hw/usb/core.c
@@ -24,8 +24,9 @@
  * THE SOFTWARE.
  */
 #include "qemu-common.h"
-#include "usb.h"
+#include "hw/usb.h"
 #include "iov.h"
+#include "trace.h"
 
 void usb_attach(USBPort *port)
 {
@@ -94,6 +95,7 @@ void usb_wakeup(USBEndpoint *ep)
 #define SETUP_STATE_SETUP 1
 #define SETUP_STATE_DATA  2
 #define SETUP_STATE_ACK   3
+#define SETUP_STATE_PARAM 4
 
 static int do_token_setup(USBDevice *s, USBPacket *p)
 {
@@ -225,6 +227,50 @@ static int do_token_out(USBDevice *s, USBPacket *p)
     }
 }
 
+static int do_parameter(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int i, ret = 0;
+
+    for (i = 0; i < 8; i++) {
+        s->setup_buf[i] = p->parameter >> (i*8);
+    }
+
+    s->setup_state = SETUP_STATE_PARAM;
+    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
+    s->setup_index = 0;
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+
+    if (s->setup_len > sizeof(s->data_buf)) {
+        fprintf(stderr,
+                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
+                s->setup_len, sizeof(s->data_buf));
+        return USB_RET_STALL;
+    }
+
+    if (p->pid == USB_TOKEN_OUT) {
+        usb_packet_copy(p, s->data_buf, s->setup_len);
+    }
+
+    ret = usb_device_handle_control(s, p, request, value, index,
+                                    s->setup_len, s->data_buf);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (ret < s->setup_len) {
+        s->setup_len = ret;
+    }
+    if (p->pid == USB_TOKEN_IN) {
+        usb_packet_copy(p, s->data_buf, s->setup_len);
+    }
+
+    return ret;
+}
+
 /* ctrl complete function for devices which use usb_generic_handle_packet and
    may return USB_RET_ASYNC from their handle_control callback. Device code
    which does this *must* call this function instead of the normal
@@ -249,6 +295,16 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
         p->result = 0;
         break;
 
+    case SETUP_STATE_PARAM:
+        if (p->result < s->setup_len) {
+            s->setup_len = p->result;
+        }
+        if (p->pid == USB_TOKEN_IN) {
+            p->result = 0;
+            usb_packet_copy(p, s->data_buf, s->setup_len);
+        }
+        break;
+
     default:
         break;
     }
@@ -291,6 +347,9 @@ static int usb_process_one(USBPacket *p)
 
     if (p->ep->nr == 0) {
         /* control pipe */
+        if (p->parameter) {
+            return do_parameter(dev, p);
+        }
         switch (p->pid) {
         case USB_TOKEN_SETUP:
             return do_token_setup(dev, p);
@@ -319,10 +378,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
     }
     assert(dev == p->ep->dev);
     assert(dev->state == USB_STATE_DEFAULT);
-    assert(p->state == USB_PACKET_SETUP);
+    usb_packet_check_state(p, USB_PACKET_SETUP);
     assert(p->ep != NULL);
 
-    if (QTAILQ_EMPTY(&p->ep->queue)) {
+    if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
         ret = usb_process_one(p);
         if (ret == USB_RET_ASYNC) {
             usb_packet_set_state(p, USB_PACKET_ASYNC);
@@ -347,7 +406,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
     USBEndpoint *ep = p->ep;
     int ret;
 
-    assert(p->state == USB_PACKET_ASYNC);
+    usb_packet_check_state(p, USB_PACKET_ASYNC);
     assert(QTAILQ_FIRST(&ep->queue) == p);
     usb_packet_set_state(p, USB_PACKET_COMPLETE);
     QTAILQ_REMOVE(&ep->queue, p, queue);
@@ -355,7 +414,10 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
 
     while (!QTAILQ_EMPTY(&ep->queue)) {
         p = QTAILQ_FIRST(&ep->queue);
-        assert(p->state == USB_PACKET_QUEUED);
+        if (p->state == USB_PACKET_ASYNC) {
+            break;
+        }
+        usb_packet_check_state(p, USB_PACKET_QUEUED);
         ret = usb_process_one(p);
         if (ret == USB_RET_ASYNC) {
             usb_packet_set_state(p, USB_PACKET_ASYNC);
@@ -388,9 +450,8 @@ void usb_packet_init(USBPacket *p)
     qemu_iovec_init(&p->iov, 1);
 }
 
-void usb_packet_set_state(USBPacket *p, USBPacketState state)
+static const char *usb_packet_state_name(USBPacketState state)
 {
-#ifdef DEBUG
     static const char *name[] = {
         [USB_PACKET_UNDEFINED] = "undef",
         [USB_PACKET_SETUP]     = "setup",
@@ -399,28 +460,36 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
         [USB_PACKET_COMPLETE]  = "complete",
         [USB_PACKET_CANCELED]  = "canceled",
     };
-    static const char *rets[] = {
-        [-USB_RET_NODEV]  = "NODEV",
-        [-USB_RET_NAK]    = "NAK",
-        [-USB_RET_STALL]  = "STALL",
-        [-USB_RET_BABBLE] = "BABBLE",
-        [-USB_RET_ASYNC]  = "ASYNC",
-    };
-    char add[16] = "";
+    if (state < ARRAY_SIZE(name)) {
+        return name[state];
+    }
+    return "INVALID";
+}
 
-    if (state == USB_PACKET_COMPLETE) {
-        if (p->result < 0) {
-            snprintf(add, sizeof(add), " - %s", rets[-p->result]);
-        } else {
-            snprintf(add, sizeof(add), " - %d", p->result);
-        }
+void usb_packet_check_state(USBPacket *p, USBPacketState expected)
+{
+    USBDevice *dev;
+    USBBus *bus;
+
+    if (p->state == expected) {
+        return;
     }
-    fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n",
-            p->ep->dev->qdev.parent_bus->name,
-            p->ep->dev->port->path,
-            p->ep->dev->addr, p->ep->nr,
-            p, name[p->state], name[state], add);
-#endif
+    dev = p->ep->dev;
+    bus = usb_bus_from_device(dev);
+    trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p,
+                                 usb_packet_state_name(p->state),
+                                 usb_packet_state_name(expected));
+    assert(!"usb packet state check failed");
+}
+
+void usb_packet_set_state(USBPacket *p, USBPacketState state)
+{
+    USBDevice *dev = p->ep->dev;
+    USBBus *bus = usb_bus_from_device(dev);
+
+    trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
+                                  usb_packet_state_name(p->state),
+                                  usb_packet_state_name(state));
     p->state = state;
 }
 
@@ -430,6 +499,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
     p->pid = pid;
     p->ep = ep;
     p->result = 0;
+    p->parameter = 0;
     qemu_iovec_reset(&p->iov);
     usb_packet_set_state(p, USB_PACKET_SETUP);
 }
@@ -482,6 +552,7 @@ void usb_ep_init(USBDevice *dev)
     dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
     dev->ep_ctl.ifnum = 0;
     dev->ep_ctl.dev = dev;
+    dev->ep_ctl.pipeline = false;
     QTAILQ_INIT(&dev->ep_ctl.queue);
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
         dev->ep_in[ep].nr = ep + 1;
@@ -494,6 +565,8 @@ void usb_ep_init(USBDevice *dev)
         dev->ep_out[ep].ifnum = 0;
         dev->ep_in[ep].dev = dev;
         dev->ep_out[ep].dev = dev;
+        dev->ep_in[ep].pipeline = false;
+        dev->ep_out[ep].pipeline = false;
         QTAILQ_INIT(&dev->ep_in[ep].queue);
         QTAILQ_INIT(&dev->ep_out[ep].queue);
     }
@@ -607,3 +680,9 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
     struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
     return uep->max_packet_size;
 }
+
+void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
+{
+    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+    uep->pipeline = enabled;
+}
diff --git a/hw/usb-desc.c b/hw/usb/desc.c
index 3c3ed6a802..9847a75b83 100644
--- a/hw/usb-desc.c
+++ b/hw/usb/desc.c
@@ -1,5 +1,5 @@
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "trace.h"
 
 /* ------------------------------------------------------------------ */
@@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         break;
 
     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-        data[0] = dev->config->bConfigurationValue;
+        /*
+         * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
+         * the non zero value of bConfigurationValue.
+         */
+        data[0] = dev->config ? dev->config->bConfigurationValue : 0;
         ret = 1;
         break;
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         trace_usb_set_config(dev->addr, value, ret);
         break;
 
-    case DeviceRequest | USB_REQ_GET_STATUS:
+    case DeviceRequest | USB_REQ_GET_STATUS: {
+        const USBDescConfig *config = dev->config ?
+            dev->config : &dev->device->confs[0];
+
         data[0] = 0;
-        if (dev->config->bmAttributes & 0x40) {
+        /*
+         * Default state: Device behavior when this request is received while
+         *                the device is in the Default state is not specified.
+         * We return the same value that a configured device would return if
+         * it used the first configuration.
+         */
+        if (config->bmAttributes & 0x40) {
             data[0] |= 1 << USB_DEVICE_SELF_POWERED;
         }
         if (dev->remote_wakeup) {
@@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         data[1] = 0x00;
         ret = 2;
         break;
+    }
     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
         if (value == USB_DEVICE_REMOTE_WAKEUP) {
             dev->remote_wakeup = 0;
diff --git a/hw/usb-desc.h b/hw/usb/desc.h
index d6e07ea5d2..d6e07ea5d2 100644
--- a/hw/usb-desc.h
+++ b/hw/usb/desc.h
diff --git a/hw/usb-audio.c b/hw/usb/dev-audio.c
index fed136117b..426b95c82b 100644
--- a/hw/usb-audio.c
+++ b/hw/usb/dev-audio.c
@@ -30,10 +30,10 @@
  */
 
 #include "qemu-common.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "hw.h"
-#include "audiodev.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/hw.h"
+#include "hw/audiodev.h"
 #include "audio/audio.h"
 
 #define USBAUDIO_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
diff --git a/hw/usb-bt.c b/hw/usb/dev-bluetooth.c
index 649bdcf2d7..195370c24a 100644
--- a/hw/usb-bt.c
+++ b/hw/usb/dev-bluetooth.c
@@ -19,10 +19,10 @@
  */
 
 #include "qemu-common.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "net.h"
-#include "bt.h"
+#include "hw/bt.h"
 
 struct USBBtState {
     USBDevice dev;
@@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev)
     return 0;
 }
 
-USBDevice *usb_bt_init(HCIInfo *hci)
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
 {
     USBDevice *dev;
     struct USBBtState *s;
 
     if (!hci)
         return NULL;
-    dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
+    dev = usb_create_simple(bus, "usb-bt-dongle");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-hid.c b/hw/usb/dev-hid.c
index 7fc0bd81aa..f29544d954 100644
--- a/hw/usb-hid.c
+++ b/hw/usb/dev-hid.c
@@ -22,12 +22,12 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "hw.h"
+#include "hw/hw.h"
 #include "console.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "qemu-timer.h"
-#include "hid.h"
+#include "hw/hid.h"
 
 /* HID interface requests */
 #define GET_REPORT   0xa101
@@ -466,6 +466,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_IN:
         if (p->ep->nr == 1) {
             int64_t curtime = qemu_get_clock_ns(vm_clock);
+            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+                hid_pointer_activate(hs);
+            }
             if (!hid_has_events(hs) &&
                 (!hs->idle || hs->next_idle_clock - curtime > 0)) {
                 return USB_RET_NAK;
diff --git a/hw/usb-hub.c b/hw/usb/dev-hub.c
index a12856e2e9..eb4e711207 100644
--- a/hw/usb-hub.c
+++ b/hw/usb/dev-hub.c
@@ -22,8 +22,8 @@
  * THE SOFTWARE.
  */
 #include "qemu-common.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 
 //#define DEBUG
 
diff --git a/hw/usb-net.c b/hw/usb/dev-network.c
index 49d5d4db65..cff55f223e 100644
--- a/hw/usb-net.c
+++ b/hw/usb/dev-network.c
@@ -24,8 +24,8 @@
  */
 
 #include "qemu-common.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "net.h"
 #include "qemu-queue.h"
 #include "sysemu.h"
@@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_net_init(const char *cmdline)
+static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
 {
     USBDevice *dev;
     QemuOpts *opts;
@@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline)
         return NULL;
     }
 
-    dev = usb_create(NULL /* FIXME */, "usb-net");
+    dev = usb_create(bus, "usb-net");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-serial.c b/hw/usb/dev-serial.c
index 52676e8f7b..8dcac8bc88 100644
--- a/hw/usb-serial.c
+++ b/hw/usb/dev-serial.c
@@ -10,8 +10,8 @@
 
 #include "qemu-common.h"
 #include "qemu-error.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "qemu-char.h"
 
 //#define DEBUG_Serial
@@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_serial_init(const char *filename)
+static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 {
     USBDevice *dev;
     CharDriverState *cdrv;
@@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename)
     if (!cdrv)
         return NULL;
 
-    dev = usb_create(NULL /* FIXME */, "usb-serial");
+    dev = usb_create(bus, "usb-serial");
     if (!dev) {
         return NULL;
     }
@@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename)
     return dev;
 }
 
-static USBDevice *usb_braille_init(const char *unused)
+static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
 {
     USBDevice *dev;
     CharDriverState *cdrv;
@@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused)
     if (!cdrv)
         return NULL;
 
-    dev = usb_create(NULL /* FIXME */, "usb-braille");
+    dev = usb_create(bus, "usb-braille");
     qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
     qdev_init_nofail(&dev->qdev);
 
diff --git a/hw/usb-ccid.c b/hw/usb/dev-smartcard-reader.c
index 0b2ac8037a..8e66675d86 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -5,7 +5,7 @@
  *
  * Written by Alon Levy, with contributions from Robert Relyea.
  *
- * Based on usb-serial.c, see it's copyright and attributions below.
+ * Based on usb-serial.c, see its copyright and attributions below.
  *
  * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
  * See the COPYING file in the top-level directory.
@@ -36,8 +36,8 @@
 
 #include "qemu-common.h"
 #include "qemu-error.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 #include "monitor.h"
 
 #include "hw/ccid.h"
@@ -447,7 +447,7 @@ static const USBDescDevice desc_device = {
         {
             .bNumInterfaces        = 1,
             .bConfigurationValue   = 1,
-            .bmAttributes          = 0xa0,
+            .bmAttributes          = 0xe0,
             .bMaxPower             = 50,
             .nif = 1,
             .ifs = &desc_iface0,
diff --git a/hw/usb-msd.c b/hw/usb/dev-storage.c
index 5fbd2d021b..bdbe7bdd11 100644
--- a/hw/usb-msd.c
+++ b/hw/usb/dev-storage.c
@@ -10,9 +10,9 @@
 #include "qemu-common.h"
 #include "qemu-option.h"
 #include "qemu-config.h"
-#include "usb.h"
-#include "usb-desc.h"
-#include "scsi.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/scsi.h"
 #include "console.h"
 #include "monitor.h"
 #include "sysemu.h"
@@ -193,9 +193,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
     int len;
 
     DPRINTF("Command status %d tag 0x%x, len %zd\n",
-            s->csw.status, s->csw.tag, p->iov.size);
+            s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size);
 
-    assert(s->csw.sig == 0x53425355);
+    assert(s->csw.sig == cpu_to_le32(0x53425355));
     len = MIN(sizeof(s->csw), p->iov.size);
     usb_packet_copy(p, &s->csw, len);
     memset(&s->csw, 0, sizeof(s->csw));
@@ -233,7 +233,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
 
     s->csw.sig = cpu_to_le32(0x53425355);
     s->csw.tag = cpu_to_le32(req->tag);
-    s->csw.residue = s->residue;
+    s->csw.residue = cpu_to_le32(s->residue);
     s->csw.status = status != 0;
 
     if (s->packet) {
@@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_msd_init(const char *filename)
+static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
 {
     static int nr=0;
     char id[8];
@@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename)
     }
 
     /* create guest device */
-    dev = usb_create(NULL /* FIXME */, "usb-storage");
+    dev = usb_create(bus, "usb-storage");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-wacom.c b/hw/usb/dev-wacom.c
index 197e2dced5..c1cfd74403 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb/dev-wacom.c
@@ -25,10 +25,10 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "hw.h"
+#include "hw/hw.h"
 #include "console.h"
-#include "usb.h"
-#include "usb-desc.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
 
 /* Interface requests */
 #define WACOM_GET_REPORT	0x2101
diff --git a/hw/usb-ehci.c b/hw/usb/hcd-ehci.c
index e699814305..60f9f5bdb5 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -22,10 +22,10 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "hw.h"
+#include "hw/hw.h"
 #include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
 #include "monitor.h"
 #include "trace.h"
 #include "dma.h"
@@ -347,7 +347,6 @@ enum async_state {
 struct EHCIQueue {
     EHCIState *ehci;
     QTAILQ_ENTRY(EHCIQueue) next;
-    bool async_schedule;
     uint32_t seen;
     uint64_t ts;
 
@@ -367,6 +366,8 @@ struct EHCIQueue {
     int usb_status;
 };
 
+typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
+
 struct EHCIState {
     PCIDevice dev;
     USBBus bus;
@@ -410,14 +411,14 @@ struct EHCIState {
     USBPort ports[NB_PORTS];
     USBPort *companion_ports[NB_PORTS];
     uint32_t usbsts_pending;
-    QTAILQ_HEAD(, EHCIQueue) queues;
+    EHCIQueueHead aqueues;
+    EHCIQueueHead pqueues;
 
     uint32_t a_fetch_addr;   // which address to look at next
     uint32_t p_fetch_addr;   // which address to look at next
 
     USBPacket ipacket;
     QEMUSGList isgl;
-    int isoch_pause;
 
     uint64_t last_run_ns;
 };
@@ -660,31 +661,34 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
 
 static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
 {
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q;
 
     q = g_malloc0(sizeof(*q));
     q->ehci = ehci;
-    q->async_schedule = async;
-    QTAILQ_INSERT_HEAD(&ehci->queues, q, next);
+    QTAILQ_INSERT_HEAD(head, q, next);
     trace_usb_ehci_queue_action(q, "alloc");
     return q;
 }
 
-static void ehci_free_queue(EHCIQueue *q)
+static void ehci_free_queue(EHCIQueue *q, int async)
 {
+    EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues;
     trace_usb_ehci_queue_action(q, "free");
     if (q->async == EHCI_ASYNC_INFLIGHT) {
         usb_cancel_packet(&q->packet);
     }
-    QTAILQ_REMOVE(&q->ehci->queues, q, next);
+    QTAILQ_REMOVE(head, q, next);
     g_free(q);
 }
 
-static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr)
+static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
+                                        int async)
 {
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q;
 
-    QTAILQ_FOREACH(q, &ehci->queues, next) {
+    QTAILQ_FOREACH(q, head, next) {
         if (addr == q->qhaddr) {
             return q;
         }
@@ -692,43 +696,46 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr)
     return NULL;
 }
 
-static void ehci_queues_rip_unused(EHCIState *ehci)
+static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
 {
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q, *tmp;
 
-    QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
         if (q->seen) {
             q->seen = 0;
             q->ts = ehci->last_run_ns;
             continue;
         }
-        if (ehci->last_run_ns < q->ts + 250000000) {
+        if (!flush && ehci->last_run_ns < q->ts + 250000000) {
             /* allow 0.25 sec idle */
             continue;
         }
-        ehci_free_queue(q);
+        ehci_free_queue(q, async);
     }
 }
 
-static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev)
+static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
 {
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q, *tmp;
 
-    QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
         if (!usb_packet_is_inflight(&q->packet) ||
             q->packet.ep->dev != dev) {
             continue;
         }
-        ehci_free_queue(q);
+        ehci_free_queue(q, async);
     }
 }
 
-static void ehci_queues_rip_all(EHCIState *ehci)
+static void ehci_queues_rip_all(EHCIState *ehci, int async)
 {
+    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q, *tmp;
 
-    QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
-        ehci_free_queue(q);
+    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+        ehci_free_queue(q, async);
     }
 }
 
@@ -773,7 +780,8 @@ static void ehci_detach(USBPort *port)
         return;
     }
 
-    ehci_queues_rip_device(s, port->dev);
+    ehci_queues_rip_device(s, port->dev, 0);
+    ehci_queues_rip_device(s, port->dev, 1);
 
     *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
     *portsc |= PORTSC_CSC;
@@ -793,7 +801,8 @@ static void ehci_child_detach(USBPort *port, USBDevice *child)
         return;
     }
 
-    ehci_queues_rip_device(s, child);
+    ehci_queues_rip_device(s, child, 0);
+    ehci_queues_rip_device(s, child, 1);
 }
 
 static void ehci_wakeup(USBPort *port)
@@ -897,7 +906,6 @@ static void ehci_reset(void *opaque)
 
     s->astate = EST_INACTIVE;
     s->pstate = EST_INACTIVE;
-    s->isoch_pause = -1;
     s->attach_poll_counter = 0;
 
     for(i = 0; i < NB_PORTS; i++) {
@@ -911,7 +919,9 @@ static void ehci_reset(void *opaque)
             usb_device_reset(devs[i]);
         }
     }
-    ehci_queues_rip_all(s);
+    ehci_queues_rip_all(s, 0);
+    ehci_queues_rip_all(s, 1);
+    qemu_del_timer(s->frame_timer);
 }
 
 static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
@@ -1064,13 +1074,14 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
 
         if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
             qemu_del_timer(s->frame_timer);
-            // TODO - should finish out some stuff before setting halt
+            ehci_queues_rip_all(s, 0);
+            ehci_queues_rip_all(s, 1);
             ehci_set_usbsts(s, USBSTS_HALT);
         }
 
         if (val & USBCMD_HCRESET) {
             ehci_reset(s);
-            val &= ~USBCMD_HCRESET;
+            val = s->usbcmd;
         }
 
         /* not supporting dynamic frame list size at the moment */
@@ -1278,8 +1289,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 
 static void ehci_execute_complete(EHCIQueue *q)
 {
-    int c_err, reload;
-
     assert(q->async != EHCI_ASYNC_INFLIGHT);
     q->async = EHCI_ASYNC_NONE;
 
@@ -1287,15 +1296,11 @@ static void ehci_execute_complete(EHCIQueue *q)
             q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
 
     if (q->usb_status < 0) {
-err:
-        /* TO-DO: put this is in a function that can be invoked below as well */
-        c_err = get_field(q->qh.token, QTD_TOKEN_CERR);
-        c_err--;
-        set_field(&q->qh.token, c_err, QTD_TOKEN_CERR);
-
         switch(q->usb_status) {
+        case USB_RET_IOERROR:
         case USB_RET_NODEV:
             q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
+            set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
             ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
             break;
         case USB_RET_STALL:
@@ -1303,16 +1308,8 @@ err:
             ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
             break;
         case USB_RET_NAK:
-            /* 4.10.3 */
-            reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
-            if ((q->pid == USB_TOKEN_IN) && reload) {
-                int nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
-                nakcnt--;
-                set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
-            } else if (!reload) {
-                return;
-            }
-            break;
+            set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
+            return; /* We're not done yet with this transaction */
         case USB_RET_BABBLE:
             q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
             ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
@@ -1323,15 +1320,13 @@ err:
             assert(0);
             break;
         }
+    } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
+        q->usb_status = USB_RET_BABBLE;
+        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
+        ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
     } else {
-        // DPRINTF("Short packet condition\n");
         // TODO check 4.12 for splits
 
-        if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
-            q->usb_status = USB_RET_BABBLE;
-            goto err;
-        }
-
         if (q->tbytes && q->pid == USB_TOKEN_IN) {
             q->tbytes -= q->usb_status;
         } else {
@@ -1347,7 +1342,7 @@ err:
     q->qh.token ^= QTD_TOKEN_DTOGGLE;
     q->qh.token &= ~QTD_TOKEN_ACTIVE;
 
-    if ((q->usb_status >= 0) && (q->qh.token & QTD_TOKEN_IOC)) {
+    if (q->qh.token & QTD_TOKEN_IOC) {
         ehci_record_interrupt(q->ehci, USBSTS_INT);
     }
 }
@@ -1458,45 +1453,41 @@ static int ehci_process_itd(EHCIState *ehci,
 
             dev = ehci_find_device(ehci, devaddr);
             ep = usb_ep_get(dev, pid, endp);
-            usb_packet_setup(&ehci->ipacket, pid, ep);
-            usb_packet_map(&ehci->ipacket, &ehci->isgl);
-
-            ret = usb_handle_packet(dev, &ehci->ipacket);
-
-            usb_packet_unmap(&ehci->ipacket);
+            if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+                usb_packet_setup(&ehci->ipacket, pid, ep);
+                usb_packet_map(&ehci->ipacket, &ehci->isgl);
+                ret = usb_handle_packet(dev, &ehci->ipacket);
+                assert(ret != USB_RET_ASYNC);
+                usb_packet_unmap(&ehci->ipacket);
+            } else {
+                DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
+                ret = USB_RET_NAK;
+            }
             qemu_sglist_destroy(&ehci->isgl);
 
-#if 0
-            /*  In isoch, there is no facility to indicate a NAK so let's
-             *  instead just complete a zero-byte transaction.  Setting
-             *  DBERR seems too draconian.
-             */
-
-            if (ret == USB_RET_NAK) {
-                if (ehci->isoch_pause > 0) {
-                    DPRINTF("ISOCH: received a NAK but paused so returning\n");
-                    ehci->isoch_pause--;
-                    return 0;
-                } else if (ehci->isoch_pause == -1) {
-                    DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
-                    // Pause frindex for up to 50 msec waiting for data from
-                    // remote
-                    ehci->isoch_pause = 50;
-                    return 0;
-                } else {
-                    DPRINTF("ISOCH: isoch pause timeout! return 0\n");
+            if (ret < 0) {
+                switch (ret) {
+                default:
+                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
+                    /* Fall through */
+                case USB_RET_IOERROR:
+                case USB_RET_NODEV:
+                    /* 3.3.2: XACTERR is only allowed on IN transactions */
+                    if (dir) {
+                        itd->transact[i] |= ITD_XACT_XACTERR;
+                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    }
+                    break;
+                case USB_RET_BABBLE:
+                    itd->transact[i] |= ITD_XACT_BABBLE;
+                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    break;
+                case USB_RET_NAK:
+                    /* no data for us, so do a zero-length transfer */
                     ret = 0;
+                    break;
                 }
-            } else {
-                DPRINTF("ISOCH: received ACK, clearing pause\n");
-                ehci->isoch_pause = -1;
             }
-#else
-            if (ret == USB_RET_NAK) {
-                ret = 0;
-            }
-#endif
-
             if (ret >= 0) {
                 if (!dir) {
                     /* OUT */
@@ -1505,10 +1496,9 @@ static int ehci_process_itd(EHCIState *ehci,
                     /* IN */
                     set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
                 }
-
-                if (itd->transact[i] & ITD_XACT_IOC) {
-                    ehci_record_interrupt(ehci, USBSTS_INT);
-                }
+            }
+            if (itd->transact[i] & ITD_XACT_IOC) {
+                ehci_record_interrupt(ehci, USBSTS_INT);
             }
             itd->transact[i] &= ~ITD_XACT_ACTIVE;
         }
@@ -1531,7 +1521,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
         ehci_set_usbsts(ehci, USBSTS_REC);
     }
 
-    ehci_queues_rip_unused(ehci);
+    ehci_queues_rip_unused(ehci, async, 0);
 
     /*  Find the head of the list (4.9.1.1) */
     for(i = 0; i < MAX_QH; i++) {
@@ -1573,8 +1563,7 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async)
     int again = 0;
     uint32_t entry = ehci_get_fetch_addr(ehci, async);
 
-    if (entry < 0x1000) {
-        DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
+    if (NLPTR_TBIT(entry)) {
         ehci_set_state(ehci, async, EST_ACTIVE);
         goto out;
     }
@@ -1616,10 +1605,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
 {
     uint32_t entry;
     EHCIQueue *q;
-    int reload;
 
     entry = ehci_get_fetch_addr(ehci, async);
-    q = ehci_find_queue_by_qh(ehci, entry);
+    q = ehci_find_queue_by_qh(ehci, entry, async);
     if (NULL == q) {
         q = ehci_alloc_queue(ehci, async);
     }
@@ -1674,15 +1662,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
     }
 #endif
 
-    reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
-    if (reload) {
-        set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
-    }
-
     if (q->qh.token & QTD_TOKEN_HALT) {
         ehci_set_state(ehci, async, EST_HORIZONTALQH);
 
-    } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (q->qh.current_qtd > 0x1000)) {
+    } else if ((q->qh.token & QTD_TOKEN_ACTIVE) &&
+               (NLPTR_TBIT(q->qh.current_qtd) == 0)) {
         q->qtdaddr = q->qh.current_qtd;
         ehci_set_state(ehci, async, EST_FETCHQTD);
 
@@ -1761,7 +1745,6 @@ static int ehci_state_advqueue(EHCIQueue *q, int async)
      * want data and alt-next qTD is valid
      */
     if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
-        (q->qh.altnext_qtd > 0x1000) &&
         (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
         q->qtdaddr = q->qh.altnext_qtd;
         ehci_set_state(q->ehci, async, EST_FETCHQTD);
@@ -1769,8 +1752,7 @@ static int ehci_state_advqueue(EHCIQueue *q, int async)
     /*
      *  next qTD is valid
      */
-    } else if ((q->qh.next_qtd > 0x1000) &&
-               (NLPTR_TBIT(q->qh.next_qtd) == 0)) {
+    } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
         q->qtdaddr = q->qh.next_qtd;
         ehci_set_state(q->ehci, async, EST_FETCHQTD);
 
@@ -1839,25 +1821,11 @@ static void ehci_flush_qh(EHCIQueue *q)
 static int ehci_state_execute(EHCIQueue *q, int async)
 {
     int again = 0;
-    int reload, nakcnt;
-    int smask;
 
     if (ehci_qh_do_overlay(q) != 0) {
         return -1;
     }
 
-    smask = get_field(q->qh.epcap, QH_EPCAP_SMASK);
-
-    if (!smask) {
-        reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
-        nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
-        if (reload && !nakcnt) {
-            ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
-            again = 1;
-            goto out;
-        }
-    }
-
     // TODO verify enough time remains in the uframe as in 4.4.1.1
     // TODO write back ptr to async list when done or out of time
     // TODO Windows does not seem to ever set the MULT field
@@ -1899,7 +1867,6 @@ out:
 static int ehci_state_executing(EHCIQueue *q, int async)
 {
     int again = 0;
-    int reload, nakcnt;
 
     ehci_execute_complete(q);
     if (q->usb_status == USB_RET_ASYNC) {
@@ -1919,21 +1886,8 @@ static int ehci_state_executing(EHCIQueue *q, int async)
         // counter decrements to 0
     }
 
-    reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
-    if (reload) {
-        nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
-        if (q->usb_status == USB_RET_NAK) {
-            if (nakcnt) {
-                nakcnt--;
-            }
-        } else {
-            nakcnt = reload;
-        }
-        set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
-    }
-
     /* 4.10.5 */
-    if ((q->usb_status == USB_RET_NAK) || (q->qh.token & QTD_TOKEN_ACTIVE)) {
+    if (q->usb_status == USB_RET_NAK) {
         ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
     } else {
         ehci_set_state(q->ehci, async, EST_WRITEBACK);
@@ -2071,7 +2025,7 @@ static void ehci_advance_state(EHCIState *ehci,
 
 static void ehci_advance_async_state(EHCIState *ehci)
 {
-    int async = 1;
+    const int async = 1;
 
     switch(ehci_get_state(ehci, async)) {
     case EST_INACTIVE:
@@ -2084,23 +2038,13 @@ static void ehci_advance_async_state(EHCIState *ehci)
 
     case EST_ACTIVE:
         if ( !(ehci->usbcmd & USBCMD_ASE)) {
+            ehci_queues_rip_all(ehci, async);
             ehci_clear_usbsts(ehci, USBSTS_ASS);
             ehci_set_state(ehci, async, EST_INACTIVE);
             break;
         }
 
-        /* If the doorbell is set, the guest wants to make a change to the
-         * schedule. The host controller needs to release cached data.
-         * (section 4.8.2)
-         */
-        if (ehci->usbcmd & USBCMD_IAAD) {
-            DPRINTF("ASYNC: doorbell request acknowledged\n");
-            ehci->usbcmd &= ~USBCMD_IAAD;
-            ehci_set_interrupt(ehci, USBSTS_IAA);
-            break;
-        }
-
-        /* make sure guest has acknowledged */
+        /* make sure guest has acknowledged the doorbell interrupt */
         /* TO-DO: is this really needed? */
         if (ehci->usbsts & USBSTS_IAA) {
             DPRINTF("IAA status bit still set.\n");
@@ -2114,6 +2058,18 @@ static void ehci_advance_async_state(EHCIState *ehci)
 
         ehci_set_state(ehci, async, EST_WAITLISTHEAD);
         ehci_advance_state(ehci, async);
+
+        /* If the doorbell is set, the guest wants to make a change to the
+         * schedule. The host controller needs to release cached data.
+         * (section 4.8.2)
+         */
+        if (ehci->usbcmd & USBCMD_IAAD) {
+            /* Remove all unseen qhs from the async qhs queue */
+            ehci_queues_rip_unused(ehci, async, 1);
+            DPRINTF("ASYNC: doorbell request acknowledged\n");
+            ehci->usbcmd &= ~USBCMD_IAAD;
+            ehci_set_interrupt(ehci, USBSTS_IAA);
+        }
         break;
 
     default:
@@ -2128,7 +2084,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
 {
     uint32_t entry;
     uint32_t list;
-    int async = 0;
+    const int async = 0;
 
     // 4.6
 
@@ -2143,6 +2099,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
 
     case EST_ACTIVE:
         if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+            ehci_queues_rip_all(ehci, async);
             ehci_clear_usbsts(ehci, USBSTS_PSS);
             ehci_set_state(ehci, async, EST_INACTIVE);
             break;
@@ -2163,6 +2120,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
         ehci_set_fetch_addr(ehci, async,entry);
         ehci_set_state(ehci, async, EST_FETCHENTRY);
         ehci_advance_state(ehci, async);
+        ehci_queues_rip_unused(ehci, async, 0);
         break;
 
     default:
@@ -2190,9 +2148,7 @@ static void ehci_frame_timer(void *opaque)
 
     for (i = 0; i < frames; i++) {
         if ( !(ehci->usbsts & USBSTS_HALT)) {
-            if (ehci->isoch_pause <= 0) {
-                ehci->frindex += 8;
-            }
+            ehci->frindex += 8;
 
             if (ehci->frindex > 0x00001fff) {
                 ehci->frindex = 0;
@@ -2361,15 +2317,14 @@ static int usb_ehci_initfn(PCIDevice *dev)
     }
 
     s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
-    QTAILQ_INIT(&s->queues);
+    QTAILQ_INIT(&s->aqueues);
+    QTAILQ_INIT(&s->pqueues);
 
     qemu_register_reset(ehci_reset, s);
 
     memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
 
-    fprintf(stderr, "*** EHCI support is under development ***\n");
-
     return 0;
 }
 
diff --git a/hw/usb-musb.c b/hw/usb/hcd-musb.c
index 820907a9a9..fa9385ee49 100644
--- a/hw/usb-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -22,9 +22,9 @@
  */
 #include "qemu-common.h"
 #include "qemu-timer.h"
-#include "usb.h"
-#include "irq.h"
-#include "hw.h"
+#include "hw/usb.h"
+#include "hw/irq.h"
+#include "hw/hw.h"
 
 /* Common USB registers */
 #define MUSB_HDRC_FADDR		0x00	/* 8-bit */
diff --git a/hw/usb-ohci.c b/hw/usb/hcd-ohci.c
index 7aa19fe781..1a1cc88b1f 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -26,13 +26,12 @@
  *  o BIOS work to boot from USB storage
 */
 
-#include "hw.h"
+#include "hw/hw.h"
 #include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
-#include "usb-ohci.h"
-#include "sysbus.h"
-#include "qdev-addr.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-addr.h"
 
 //#define DEBUG_OHCI
 /* Dump packet contents.  */
@@ -122,6 +121,11 @@ struct ohci_hcca {
     uint16_t frame, pad;
     uint32_t done;
 };
+#define HCCA_WRITEBACK_OFFSET   offsetof(struct ohci_hcca, frame)
+#define HCCA_WRITEBACK_SIZE     8 /* frame, pad, done */
+
+#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head)
+#define ED_WBACK_SIZE   4
 
 static void ohci_bus_stop(OHCIState *ohci);
 static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
@@ -569,7 +573,13 @@ static inline int ohci_read_hcca(OHCIState *ohci,
 static inline int ohci_put_ed(OHCIState *ohci,
                               uint32_t addr, struct ohci_ed *ed)
 {
-    return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+    /* ed->tail is under control of the HCD.
+     * Since just ed->head is changed by HC, just write back this
+     */
+
+    return put_dwords(ohci, addr + ED_WBACK_OFFSET,
+                      (uint32_t *)((char *)ed + ED_WBACK_OFFSET),
+                      ED_WBACK_SIZE >> 2);
 }
 
 static inline int ohci_put_td(OHCIState *ohci,
@@ -588,7 +598,9 @@ static inline int ohci_put_iso_td(OHCIState *ohci,
 static inline int ohci_put_hcca(OHCIState *ohci,
                                 uint32_t addr, struct ohci_hcca *hcca)
 {
-    cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca));
+    cpu_physical_memory_write(addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET,
+                              (char *)hcca + HCCA_WRITEBACK_OFFSET,
+                              HCCA_WRITEBACK_SIZE);
     return 1;
 }
 
@@ -837,6 +849,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
                         OHCI_CC_DATAUNDERRUN);
         } else {
             switch (ret) {
+            case USB_RET_IOERROR:
             case USB_RET_NODEV:
                 OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
                             OHCI_CC_DEVICENOTRESPONDING);
@@ -1052,6 +1065,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
             OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
         } else {
             switch (ret) {
+            case USB_RET_IOERROR:
             case USB_RET_NODEV:
                 OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
             case USB_RET_NAK:
@@ -1813,11 +1827,6 @@ static int usb_ohci_initfn_pci(struct PCIDevice *dev)
     return 0;
 }
 
-void usb_ohci_init_pci(struct PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "pci-ohci");
-}
-
 typedef struct {
     SysBusDevice busdev;
     OHCIState ohci;
diff --git a/hw/usb-uhci.c b/hw/usb/hcd-uhci.c
index 2280dc792d..e55dad914c 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -25,13 +25,13 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "hw.h"
-#include "usb.h"
-#include "pci.h"
+#include "hw/hw.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
 #include "qemu-timer.h"
-#include "usb-uhci.h"
 #include "iov.h"
 #include "dma.h"
+#include "trace.h"
 
 //#define DEBUG
 //#define DEBUG_DUMP_DATA
@@ -77,41 +77,41 @@
 
 #define NB_PORTS 2
 
-#ifdef DEBUG
-#define DPRINTF printf
-
-static const char *pid2str(int pid)
-{
-    switch (pid) {
-    case USB_TOKEN_SETUP: return "SETUP";
-    case USB_TOKEN_IN:    return "IN";
-    case USB_TOKEN_OUT:   return "OUT";
-    }
-    return "?";
-}
-
-#else
-#define DPRINTF(...)
-#endif
+enum {
+    TD_RESULT_STOP_FRAME = 10,
+    TD_RESULT_COMPLETE,
+    TD_RESULT_NEXT_QH,
+    TD_RESULT_ASYNC_START,
+    TD_RESULT_ASYNC_CONT,
+};
 
 typedef struct UHCIState UHCIState;
+typedef struct UHCIAsync UHCIAsync;
+typedef struct UHCIQueue UHCIQueue;
 
 /* 
  * Pending async transaction.
  * 'packet' must be the first field because completion
  * handler does "(UHCIAsync *) pkt" cast.
  */
-typedef struct UHCIAsync {
+
+struct UHCIAsync {
     USBPacket packet;
     QEMUSGList sgl;
-    UHCIState *uhci;
+    UHCIQueue *queue;
     QTAILQ_ENTRY(UHCIAsync) next;
     uint32_t  td;
-    uint32_t  token;
-    int8_t    valid;
     uint8_t   isoc;
     uint8_t   done;
-} UHCIAsync;
+};
+
+struct UHCIQueue {
+    uint32_t  token;
+    UHCIState *uhci;
+    QTAILQ_ENTRY(UHCIQueue) next;
+    QTAILQ_HEAD(, UHCIAsync) asyncs;
+    int8_t    valid;
+};
 
 typedef struct UHCIPort {
     USBPort port;
@@ -137,7 +137,7 @@ struct UHCIState {
     uint32_t pending_int_mask;
 
     /* Active packets */
-    QTAILQ_HEAD(,UHCIAsync) async_pending;
+    QTAILQ_HEAD(, UHCIQueue) queues;
     uint8_t num_ports_vmstate;
 
     /* Properties */
@@ -157,62 +157,95 @@ typedef struct UHCI_QH {
     uint32_t el_link;
 } UHCI_QH;
 
-static UHCIAsync *uhci_async_alloc(UHCIState *s)
+static inline int32_t uhci_queue_token(UHCI_TD *td)
+{
+    /* covers ep, dev, pid -> identifies the endpoint */
+    return td->token & 0x7ffff;
+}
+
+static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            return queue;
+        }
+    }
+
+    queue = g_new0(UHCIQueue, 1);
+    queue->uhci = s;
+    queue->token = token;
+    QTAILQ_INIT(&queue->asyncs);
+    QTAILQ_INSERT_HEAD(&s->queues, queue, next);
+    trace_usb_uhci_queue_add(queue->token);
+    return queue;
+}
+
+static void uhci_queue_free(UHCIQueue *queue)
 {
-    UHCIAsync *async = g_malloc(sizeof(UHCIAsync));
-
-    memset(&async->packet, 0, sizeof(async->packet));
-    async->uhci  = s;
-    async->valid = 0;
-    async->td    = 0;
-    async->token = 0;
-    async->done  = 0;
-    async->isoc  = 0;
+    UHCIState *s = queue->uhci;
+
+    trace_usb_uhci_queue_del(queue->token);
+    QTAILQ_REMOVE(&s->queues, queue, next);
+    g_free(queue);
+}
+
+static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t addr)
+{
+    UHCIAsync *async = g_new0(UHCIAsync, 1);
+
+    async->queue = queue;
+    async->td = addr;
     usb_packet_init(&async->packet);
-    pci_dma_sglist_init(&async->sgl, &s->dev, 1);
+    pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
+    trace_usb_uhci_packet_add(async->queue->token, async->td);
 
     return async;
 }
 
-static void uhci_async_free(UHCIState *s, UHCIAsync *async)
+static void uhci_async_free(UHCIAsync *async)
 {
+    trace_usb_uhci_packet_del(async->queue->token, async->td);
     usb_packet_cleanup(&async->packet);
     qemu_sglist_destroy(&async->sgl);
     g_free(async);
 }
 
-static void uhci_async_link(UHCIState *s, UHCIAsync *async)
+static void uhci_async_link(UHCIAsync *async)
 {
-    QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
+    UHCIQueue *queue = async->queue;
+    QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
+    trace_usb_uhci_packet_link_async(async->queue->token, async->td);
 }
 
-static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
+static void uhci_async_unlink(UHCIAsync *async)
 {
-    QTAILQ_REMOVE(&s->async_pending, async, next);
+    UHCIQueue *queue = async->queue;
+    QTAILQ_REMOVE(&queue->asyncs, async, next);
+    trace_usb_uhci_packet_unlink_async(async->queue->token, async->td);
 }
 
-static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
+static void uhci_async_cancel(UHCIAsync *async)
 {
-    DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
-           async->td, async->token, async->done);
-
+    trace_usb_uhci_packet_cancel(async->queue->token, async->td, async->done);
     if (!async->done)
         usb_cancel_packet(&async->packet);
-    uhci_async_free(s, async);
+    uhci_async_free(async);
 }
 
 /*
  * Mark all outstanding async packets as invalid.
  * This is used for canceling them when TDs are removed by the HCD.
  */
-static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
+static void uhci_async_validate_begin(UHCIState *s)
 {
-    UHCIAsync *async;
+    UHCIQueue *queue;
 
-    QTAILQ_FOREACH(async, &s->async_pending, next) {
-        async->valid--;
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        queue->valid--;
     }
-    return NULL;
 }
 
 /*
@@ -220,77 +253,75 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
  */
 static void uhci_async_validate_end(UHCIState *s)
 {
-    UHCIAsync *curr, *n;
+    UHCIQueue *queue, *n;
+    UHCIAsync *async;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        if (curr->valid > 0) {
+    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
+        if (queue->valid > 0) {
             continue;
         }
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
+        while (!QTAILQ_EMPTY(&queue->asyncs)) {
+            async = QTAILQ_FIRST(&queue->asyncs);
+            uhci_async_unlink(async);
+            uhci_async_cancel(async);
+        }
+        uhci_queue_free(queue);
     }
 }
 
 static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
 {
+    UHCIQueue *queue;
     UHCIAsync *curr, *n;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        if (!usb_packet_is_inflight(&curr->packet) ||
-            curr->packet.ep->dev != dev) {
-            continue;
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            if (!usb_packet_is_inflight(&curr->packet) ||
+                curr->packet.ep->dev != dev) {
+                continue;
+            }
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
         }
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
     }
 }
 
 static void uhci_async_cancel_all(UHCIState *s)
 {
+    UHCIQueue *queue;
     UHCIAsync *curr, *n;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
+        }
+        uhci_queue_free(queue);
     }
 }
 
-static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
 {
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
     UHCIAsync *async;
-    UHCIAsync *match = NULL;
-    int count = 0;
-
-    /*
-     * We're looking for the best match here. ie both td addr and token.
-     * Otherwise we return last good match. ie just token.
-     * It's ok to match just token because it identifies the transaction
-     * rather well, token includes: device addr, endpoint, size, etc.
-     *
-     * Also since we queue async transactions in reverse order by returning
-     * last good match we restores the order.
-     *
-     * It's expected that we wont have a ton of outstanding transactions.
-     * If we ever do we'd want to optimize this algorithm.
-     */
 
-    QTAILQ_FOREACH(async, &s->async_pending, next) {
-        if (async->token == token) {
-            /* Good match */
-            match = async;
-
-            if (async->td == addr) {
-                /* Best match */
-                break;
-            }
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            break;
         }
-        count++;
+    }
+    if (queue == NULL) {
+        return NULL;
     }
 
-    if (count > 64)
-	fprintf(stderr, "uhci: warning lots of async transactions\n");
+    QTAILQ_FOREACH(async, &queue->asyncs, next) {
+        if (async->td == addr) {
+            return async;
+        }
+    }
 
-    return match;
+    return NULL;
 }
 
 static void uhci_update_irq(UHCIState *s)
@@ -316,7 +347,7 @@ static void uhci_reset(void *opaque)
     int i;
     UHCIPort *port;
 
-    DPRINTF("uhci: full reset\n");
+    trace_usb_uhci_reset();
 
     pci_conf = s->dev.config;
 
@@ -416,12 +447,13 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
     UHCIState *s = opaque;
 
     addr &= 0x1f;
-    DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
+    trace_usb_uhci_mmio_writew(addr, val);
 
     switch(addr) {
     case 0x00:
         if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
             /* start frame processing */
+            trace_usb_uhci_schedule_start();
             s->expire_time = qemu_get_clock_ns(vm_clock) +
                 (get_ticks_per_sec() / FRAME_TIMER_FREQ);
             qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
@@ -526,7 +558,7 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
         break;
     }
 
-    DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
+    trace_usb_uhci_mmio_readw(addr, val);
 
     return val;
 }
@@ -536,7 +568,7 @@ static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
     UHCIState *s = opaque;
 
     addr &= 0x1f;
-    DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
+    trace_usb_uhci_mmio_writel(addr, val);
 
     switch(addr) {
     case 0x08:
@@ -559,6 +591,7 @@ static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr)
         val = 0xffffffff;
         break;
     }
+    trace_usb_uhci_mmio_readl(addr, val);
     return val;
 }
 
@@ -694,13 +727,15 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
         if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
             *int_mask |= 0x02;
             /* short packet: do not update QH */
-            DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
-            return 1;
+            trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
+                                                    async->td);
+            return TD_RESULT_NEXT_QH;
         }
     }
 
     /* success */
-    return 0;
+    trace_usb_uhci_packet_complete_success(async->queue->token, async->td);
+    return TD_RESULT_COMPLETE;
 
 out:
     switch(ret) {
@@ -712,7 +747,8 @@ out:
             *int_mask |= 0x01;
         }
         uhci_update_irq(s);
-        return 1;
+        trace_usb_uhci_packet_complete_stall(async->queue->token, async->td);
+        return TD_RESULT_NEXT_QH;
 
     case USB_RET_BABBLE:
         td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
@@ -723,14 +759,16 @@ out:
         }
         uhci_update_irq(s);
         /* frame interrupted */
-        return -1;
+        trace_usb_uhci_packet_complete_babble(async->queue->token, async->td);
+        return TD_RESULT_STOP_FRAME;
 
     case USB_RET_NAK:
         td->ctrl |= TD_CTRL_NAK;
         if (pid == USB_TOKEN_SETUP)
             break;
-	return 1;
+        return TD_RESULT_NEXT_QH;
 
+    case USB_RET_IOERROR:
     case USB_RET_NODEV:
     default:
 	break;
@@ -748,61 +786,47 @@ out:
             if (td->ctrl & TD_CTRL_IOC)
                 *int_mask |= 0x01;
             uhci_update_irq(s);
+            trace_usb_uhci_packet_complete_error(async->queue->token,
+                                                 async->td);
         }
     }
     td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
         (err << TD_CTRL_ERROR_SHIFT);
-    return 1;
+    return TD_RESULT_NEXT_QH;
 }
 
 static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
 {
     UHCIAsync *async;
     int len = 0, max_len;
-    uint8_t pid, isoc;
-    uint32_t token;
+    uint8_t pid;
     USBDevice *dev;
     USBEndpoint *ep;
 
     /* Is active ? */
     if (!(td->ctrl & TD_CTRL_ACTIVE))
-        return 1;
+        return TD_RESULT_NEXT_QH;
 
-    /* token field is not unique for isochronous requests,
-     * so use the destination buffer 
-     */
-    if (td->ctrl & TD_CTRL_IOS) {
-        token = td->buffer;
-        isoc = 1;
-    } else {
-        token = td->token;
-        isoc = 0;
-    }
-
-    async = uhci_async_find_td(s, addr, token);
+    async = uhci_async_find_td(s, addr, td);
     if (async) {
         /* Already submitted */
-        async->valid = 32;
+        async->queue->valid = 32;
 
         if (!async->done)
-            return 1;
+            return TD_RESULT_ASYNC_CONT;
 
-        uhci_async_unlink(s, async);
+        uhci_async_unlink(async);
         goto done;
     }
 
     /* Allocate new packet */
-    async = uhci_async_alloc(s);
-    if (!async)
-        return 1;
+    async = uhci_async_alloc(uhci_queue_get(s, td), addr);
 
     /* valid needs to be large enough to handle 10 frame delay
      * for initial isochronous requests
      */
-    async->valid = 32;
-    async->td    = addr;
-    async->token = token;
-    async->isoc  = isoc;
+    async->queue->valid = 32;
+    async->isoc  = td->ctrl & TD_CTRL_IOS;
 
     max_len = ((td->token >> 21) + 1) & 0x7ff;
     pid = td->token & 0xff;
@@ -827,15 +851,15 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 
     default:
         /* invalid pid : frame interrupted */
-        uhci_async_free(s, async);
+        uhci_async_free(async);
         s->status |= UHCI_STS_HCPERR;
         uhci_update_irq(s);
-        return -1;
+        return TD_RESULT_STOP_FRAME;
     }
  
     if (len == USB_RET_ASYNC) {
-        uhci_async_link(s, async);
-        return 2;
+        uhci_async_link(async);
+        return TD_RESULT_ASYNC_START;
     }
 
     async->packet.result = len;
@@ -843,16 +867,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 done:
     len = uhci_complete_td(s, td, async, int_mask);
     usb_packet_unmap(&async->packet);
-    uhci_async_free(s, async);
+    uhci_async_free(async);
     return len;
 }
 
 static void uhci_async_complete(USBPort *port, USBPacket *packet)
 {
     UHCIAsync *async = container_of(packet, UHCIAsync, packet);
-    UHCIState *s = async->uhci;
-
-    DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
+    UHCIState *s = async->queue->uhci;
 
     if (async->isoc) {
         UHCI_TD td;
@@ -865,14 +887,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
         le32_to_cpus(&td.token);
         le32_to_cpus(&td.buffer);
 
-        uhci_async_unlink(s, async);
+        uhci_async_unlink(async);
         uhci_complete_td(s, &td, async, &int_mask);
         s->pending_int_mask |= int_mask;
 
         /* update the status bits of the TD */
         val = cpu_to_le32(td.ctrl);
         pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
-        uhci_async_free(s, async);
+        uhci_async_free(async);
     } else {
         async->done = 1;
         uhci_process_frame(s);
@@ -921,6 +943,34 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
     return 0;
 }
 
+static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t int_mask = 0;
+    uint32_t plink = td->link;
+    uint32_t token = uhci_queue_token(td);
+    UHCI_TD ptd;
+    int ret;
+
+    while (is_valid(plink)) {
+        pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
+        le32_to_cpus(&ptd.link);
+        le32_to_cpus(&ptd.ctrl);
+        le32_to_cpus(&ptd.token);
+        le32_to_cpus(&ptd.buffer);
+        if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
+            break;
+        }
+        if (uhci_queue_token(&ptd) != token) {
+            break;
+        }
+        trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
+        ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+        assert(ret == TD_RESULT_ASYNC_START);
+        assert(int_mask == 0);
+        plink = ptd.link;
+    }
+}
+
 static void uhci_process_frame(UHCIState *s)
 {
     uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
@@ -932,8 +982,6 @@ static void uhci_process_frame(UHCIState *s)
 
     frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
 
-    DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
-
     pci_dma_read(&s->dev, frame_addr, &link, 4);
     le32_to_cpus(&link);
 
@@ -945,6 +993,7 @@ static void uhci_process_frame(UHCIState *s)
     for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
         if (is_qh(link)) {
             /* QH */
+            trace_usb_uhci_qh_load(link & ~0xf);
 
             if (qhdb_insert(&qhdb, link)) {
                 /*
@@ -957,14 +1006,14 @@ static void uhci_process_frame(UHCIState *s)
                  *  (b) we've reached the usb 1.1 bandwidth, which is
                  *      1280 bytes/frame.
                  */
-                DPRINTF("uhci: detected loop. qh 0x%x\n", link);
                 if (td_count == 0) {
-                    DPRINTF("uhci: no transaction last round, stop\n");
+                    trace_usb_uhci_frame_loop_stop_idle();
                     break;
                 } else if (bytes_count >= 1280) {
-                    DPRINTF("uhci: bandwidth limit reached, stop\n");
+                    trace_usb_uhci_frame_loop_stop_bandwidth();
                     break;
                 } else {
+                    trace_usb_uhci_frame_loop_continue();
                     td_count = 0;
                     qhdb_reset(&qhdb);
                     qhdb_insert(&qhdb, link);
@@ -975,9 +1024,6 @@ static void uhci_process_frame(UHCIState *s)
             le32_to_cpus(&qh.link);
             le32_to_cpus(&qh.el_link);
 
-            DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
-                    link, qh.link, qh.el_link);
-
             if (!is_valid(qh.el_link)) {
                 /* QH w/o elements */
                 curr_qh = 0;
@@ -996,9 +1042,7 @@ static void uhci_process_frame(UHCIState *s)
         le32_to_cpus(&td.ctrl);
         le32_to_cpus(&td.token);
         le32_to_cpus(&td.buffer);
-
-        DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
-                link, td.link, td.ctrl, td.token, curr_qh);
+        trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
 
         old_td_ctrl = td.ctrl;
         ret = uhci_handle_td(s, link, &td, &int_mask);
@@ -1008,49 +1052,52 @@ static void uhci_process_frame(UHCIState *s)
             pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
         }
 
-        if (ret < 0) {
-            /* interrupted frame */
-            break;
-        }
-
-        if (ret == 2 || ret == 1) {
-            DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
-                    link, ret == 2 ? "pend" : "skip",
-                    td.link, td.ctrl, td.token, curr_qh);
+        switch (ret) {
+        case TD_RESULT_STOP_FRAME: /* interrupted frame */
+            goto out;
 
+        case TD_RESULT_NEXT_QH:
+        case TD_RESULT_ASYNC_CONT:
+            trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf);
             link = curr_qh ? qh.link : td.link;
             continue;
-        }
-
-        /* completed TD */
-
-        DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
-                link, td.link, td.ctrl, td.token, curr_qh);
-
-        link = td.link;
-        td_count++;
-        bytes_count += (td.ctrl & 0x7ff) + 1;
-
-        if (curr_qh) {
-	    /* update QH element link */
-            qh.el_link = link;
-            val = cpu_to_le32(qh.el_link);
-            pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
 
-            if (!depth_first(link)) {
-               /* done with this QH */
-
-               DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
-                       curr_qh, qh.link, qh.el_link);
+        case TD_RESULT_ASYNC_START:
+            trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
+            if (is_valid(td.link)) {
+                uhci_fill_queue(s, &td);
+            }
+            link = curr_qh ? qh.link : td.link;
+            continue;
 
-               curr_qh = 0;
-               link    = qh.link;
+        case TD_RESULT_COMPLETE:
+            trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf);
+            link = td.link;
+            td_count++;
+            bytes_count += (td.ctrl & 0x7ff) + 1;
+
+            if (curr_qh) {
+                /* update QH element link */
+                qh.el_link = link;
+                val = cpu_to_le32(qh.el_link);
+                pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
+
+                if (!depth_first(link)) {
+                    /* done with this QH */
+                    curr_qh = 0;
+                    link    = qh.link;
+                }
             }
+            break;
+
+        default:
+            assert(!"unknown return code");
         }
 
         /* go to the next entry */
     }
 
+out:
     s->pending_int_mask |= int_mask;
 }
 
@@ -1063,11 +1110,11 @@ static void uhci_frame_timer(void *opaque)
 
     if (!(s->cmd & UHCI_CMD_RS)) {
         /* Full stop */
+        trace_usb_uhci_schedule_stop();
         qemu_del_timer(s->frame_timer);
+        uhci_async_cancel_all(s);
         /* set hchalted bit in status - UHCI11D 2.1.2 */
         s->status |= UHCI_STS_HCHALTED;
-
-        DPRINTF("uhci: halted\n");
         return;
     }
 
@@ -1082,7 +1129,7 @@ static void uhci_frame_timer(void *opaque)
     /* Start new frame */
     s->frnum = (s->frnum + 1) & 0x7ff;
 
-    DPRINTF("uhci: new frame #%u\n" , s->frnum);
+    trace_usb_uhci_frame_start(s->frnum);
 
     uhci_async_validate_begin(s);
 
@@ -1148,7 +1195,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
     }
     s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
     s->num_ports_vmstate = NB_PORTS;
-    QTAILQ_INIT(&s->async_pending);
+    QTAILQ_INIT(&s->queues);
 
     qemu_register_reset(uhci_reset, s);
 
@@ -1329,18 +1376,3 @@ static void uhci_register_types(void)
 }
 
 type_init(uhci_register_types)
-
-void usb_uhci_piix3_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "piix3-usb-uhci");
-}
-
-void usb_uhci_piix4_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "piix4-usb-uhci");
-}
-
-void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn)
-{
-    pci_create_simple(bus, devfn, "vt82c686b-usb-uhci");
-}
diff --git a/hw/usb-xhci.c b/hw/usb/hcd-xhci.c
index 008b0b5718..73b0c7f5e5 100644
--- a/hw/usb-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -18,12 +18,12 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
-#include "hw.h"
+#include "hw/hw.h"
 #include "qemu-timer.h"
-#include "usb.h"
-#include "pci.h"
-#include "qdev-addr.h"
-#include "msi.h"
+#include "hw/usb.h"
+#include "hw/pci.h"
+#include "hw/qdev-addr.h"
+#include "hw/msi.h"
 
 //#define DEBUG_XHCI
 //#define DEBUG_DATA
@@ -1470,8 +1470,8 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
 static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
 {
     XHCITRB *trb_setup, *trb_status;
-    uint8_t bmRequestType, bRequest;
-    uint16_t wValue, wLength, wIndex;
+    uint8_t bmRequestType;
+    uint16_t wLength;
     XHCIPort *port;
     USBDevice *dev;
     int ret;
@@ -1508,9 +1508,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
     }
 
     bmRequestType = trb_setup->parameter;
-    bRequest = trb_setup->parameter >> 8;
-    wValue = trb_setup->parameter >> 16;
-    wIndex = trb_setup->parameter >> 32;
     wLength = trb_setup->parameter >> 48;
 
     if (xfer->data && xfer->data_alloced < wLength) {
@@ -1537,12 +1534,12 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
     xfer->iso_xfer = false;
 
     xhci_setup_packet(xfer, dev);
+    xfer->packet.parameter = trb_setup->parameter;
     if (!xfer->in_xfer) {
         xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
     }
-    ret = usb_device_handle_control(dev, &xfer->packet,
-                                    (bmRequestType << 8) | bRequest,
-                                    wValue, wIndex, wLength, xfer->data);
+
+    ret = usb_handle_packet(dev, &xfer->packet);
 
     xhci_complete_packet(xfer, ret);
     if (!xfer->running_async && !xfer->running_retry) {
@@ -1769,12 +1766,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
             epctx->retry = xfer;
             break;
         }
-
-        /*
-         * Qemu usb can't handle multiple in-flight xfers.
-         * Stop here for now.
-         */
-        break;
     }
 }
 
@@ -2288,7 +2279,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
     int nr = port->port.index + 1;
 
     port->portsc = PORTSC_PP;
-    if (port->port.dev && !is_detach) {
+    if (port->port.dev && port->port.dev->attached && !is_detach) {
         port->portsc |= PORTSC_CCS;
         switch (port->port.dev->speed) {
         case USB_SPEED_LOW:
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
new file mode 100644
index 0000000000..ec26266620
--- /dev/null
+++ b/hw/usb/host-bsd.c
@@ -0,0 +1,647 @@
+/*
+ * BSD host USB redirector
+ *
+ * Copyright (c) 2006 Lonnie Mendez
+ * Portions of code and concepts borrowed from
+ * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "monitor.h"
+#include "hw/usb.h"
+
+/* usb.h declares these */
+#undef USB_SPEED_HIGH
+#undef USB_SPEED_FULL
+#undef USB_SPEED_LOW
+
+#include <sys/ioctl.h>
+#ifndef __DragonFly__
+#include <dev/usb/usb.h>
+#else
+#include <bus/usb/usb.h>
+#endif
+
+/* This value has maximum potential at 16.
+ * You should also set hw.usb.debug to gain
+ * more detailed view.
+ */
+//#define DEBUG
+#define UGEN_DEBUG_LEVEL 0
+
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+                        int vendor_id, int product_id,
+                        const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr,
+                                const char *devname);
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int ep_fd[USB_MAX_ENDPOINTS];
+    int devfd;
+    char devpath[32];
+} USBHostDevice;
+
+
+static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
+{
+    char buf[32];
+    int fd;
+
+    /* Get the address for this endpoint */
+    ep = UE_GET_ADDR(ep);
+
+    if (dev->ep_fd[ep] < 0) {
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
+#else
+        snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
+#endif
+        /* Try to open it O_RDWR first for those devices which have in and out
+         * endpoints with the same address (eg 0x02 and 0x82)
+         */
+        fd = open(buf, O_RDWR);
+        if (fd < 0 && errno == ENXIO)
+            fd = open(buf, mode);
+        if (fd < 0) {
+#ifdef DEBUG
+            printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
+                   buf, strerror(errno));
+#endif
+        }
+        dev->ep_fd[ep] = fd;
+    }
+
+    return dev->ep_fd[ep];
+}
+
+static void ensure_eps_closed(USBHostDevice *dev)
+{
+    int epnum = 1;
+
+    if (!dev)
+        return;
+
+    while (epnum < USB_MAX_ENDPOINTS) {
+        if (dev->ep_fd[epnum] >= 0) {
+            close(dev->ep_fd[epnum]);
+            dev->ep_fd[epnum] = -1;
+        }
+        epnum++;
+    }
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+#if 0
+    USBHostDevice *s = (USBHostDevice *)dev;
+#endif
+}
+
+/* XXX:
+ * -check device states against transfer requests
+ *  and return appropriate response
+ */
+static int usb_host_handle_control(USBDevice *dev,
+                                   USBPacket *p,
+                                   int request,
+                                   int value,
+                                   int index,
+                                   int length,
+                                   uint8_t *data)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    struct usb_ctl_request req;
+    struct usb_alt_interface aiface;
+    int ret, timeout = 50;
+
+    if ((request >> 8) == UT_WRITE_DEVICE &&
+        (request & 0xff) == UR_SET_ADDRESS) {
+
+        /* specific SET_ADDRESS support */
+        dev->addr = value;
+        return 0;
+    } else if ((request >> 8) == UT_WRITE_DEVICE &&
+               (request & 0xff) == UR_SET_CONFIG) {
+
+        ensure_eps_closed(s); /* can't do this without all eps closed */
+
+        ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: failed to set configuration - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_STALL;
+        }
+
+        return 0;
+    } else if ((request >> 8) == UT_WRITE_INTERFACE &&
+               (request & 0xff) == UR_SET_INTERFACE) {
+
+        aiface.uai_interface_index = index;
+        aiface.uai_alt_no = value;
+
+        ensure_eps_closed(s); /* can't do this without all eps closed */
+        ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: failed to set alternate interface - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_STALL;
+        }
+
+        return 0;
+    } else {
+        req.ucr_request.bmRequestType = request >> 8;
+        req.ucr_request.bRequest = request & 0xff;
+        USETW(req.ucr_request.wValue, value);
+        USETW(req.ucr_request.wIndex, index);
+        USETW(req.ucr_request.wLength, length);
+        req.ucr_data = data;
+        req.ucr_flags = USBD_SHORT_XFER_OK;
+
+        ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+        if (ret < 0 && errno != EINVAL) {
+#else
+        if (ret < 0) {
+#endif
+#ifdef DEBUG
+            printf("handle_control: setting timeout failed - %s\n",
+                   strerror(errno));
+#endif
+        }
+
+        ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
+        /* ugen returns EIO for usbd_do_request_ no matter what
+         * happens with the transfer */
+        if (ret < 0) {
+#ifdef DEBUG
+            printf("handle_control: error after request - %s\n",
+                   strerror(errno));
+#endif
+            return USB_RET_NAK; // STALL
+        } else {
+            return req.ucr_actlen;
+        }
+    }
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+    int ret, fd, mode;
+    int one = 1, shortpacket = 0, timeout = 50;
+    sigset_t new_mask, old_mask;
+    uint8_t devep = p->ep->nr;
+
+    /* protect data transfers from SIGALRM signal */
+    sigemptyset(&new_mask);
+    sigaddset(&new_mask, SIGALRM);
+    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
+
+    if (p->pid == USB_TOKEN_IN) {
+        devep |= 0x80;
+        mode = O_RDONLY;
+        shortpacket = 1;
+    } else {
+        mode = O_WRONLY;
+    }
+
+    fd = ensure_ep_open(s, devep, mode);
+    if (fd < 0) {
+        sigprocmask(SIG_SETMASK, &old_mask, NULL);
+        return USB_RET_NODEV;
+    }
+
+    if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
+#ifdef DEBUG
+        printf("handle_data: failed to set timeout - %s\n",
+               strerror(errno));
+#endif
+    }
+
+    if (shortpacket) {
+        if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
+#ifdef DEBUG
+            printf("handle_data: failed to set short xfer mode - %s\n",
+                   strerror(errno));
+#endif
+            sigprocmask(SIG_SETMASK, &old_mask, NULL);
+        }
+    }
+
+    if (p->pid == USB_TOKEN_IN)
+        ret = readv(fd, p->iov.iov, p->iov.niov);
+    else
+        ret = writev(fd, p->iov.iov, p->iov.niov);
+
+    sigprocmask(SIG_SETMASK, &old_mask, NULL);
+
+    if (ret < 0) {
+#ifdef DEBUG
+        printf("handle_data: error after %s data - %s\n",
+               pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
+#endif
+        switch(errno) {
+        case ETIMEDOUT:
+        case EINTR:
+            return USB_RET_NAK;
+        default:
+            return USB_RET_STALL;
+        }
+    } else {
+        return ret;
+    }
+}
+
+static void usb_host_handle_destroy(USBDevice *opaque)
+{
+    USBHostDevice *s = (USBHostDevice *)opaque;
+    int i;
+
+    for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+        if (s->ep_fd[i] >= 0)
+            close(s->ep_fd[i]);
+
+    if (s->devfd < 0)
+        return;
+
+    close(s->devfd);
+
+    g_free(s);
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+    return 0;
+}
+
+USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
+{
+    struct usb_device_info bus_info, dev_info;
+    USBDevice *d = NULL, *ret = NULL;
+    USBHostDevice *dev;
+    char ctlpath[PATH_MAX + 1];
+    char buspath[PATH_MAX + 1];
+    int bfd, dfd, bus, address, i;
+    int ugendebug = UGEN_DEBUG_LEVEL;
+
+    if (usb_host_find_device(&bus, &address, devname) < 0) {
+        goto fail;
+    }
+
+    snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
+
+    bfd = open(buspath, O_RDWR);
+    if (bfd < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to open usb bus - %s\n",
+               strerror(errno));
+#endif
+        goto fail;
+    }
+
+    bus_info.udi_addr = address;
+    if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to grab bus information - %s\n",
+               strerror(errno));
+#endif
+        goto fail_bfd;
+    }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+    snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+    dfd  = open(ctlpath, O_RDWR);
+    if (dfd < 0) {
+        dfd = open(ctlpath, O_RDONLY);
+        if (dfd < 0) {
+#ifdef DEBUG
+            printf("usb_host_device_open: failed to open usb device %s - %s\n",
+                   ctlpath, strerror(errno));
+#endif
+        }
+        goto fail_dfd;
+    }
+
+    if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
+#ifdef DEBUG
+        printf("usb_host_device_open: failed to grab device info - %s\n",
+               strerror(errno));
+#endif
+        goto fail_dfd;
+    }
+
+    d = usb_create(guest_bus, "usb-host");
+    dev = DO_UPCAST(USBHostDevice, dev, d);
+
+    if (dev_info.udi_speed == 1) {
+        dev->dev.speed = USB_SPEED_LOW - 1;
+        dev->dev.speedmask = USB_SPEED_MASK_LOW;
+    } else {
+        dev->dev.speed = USB_SPEED_FULL - 1;
+        dev->dev.speedmask = USB_SPEED_MASK_FULL;
+    }
+
+    if (strncmp(dev_info.udi_product, "product", 7) != 0) {
+        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                dev_info.udi_product);
+    } else {
+        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                 "host:%s", devname);
+    }
+
+    pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
+    pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
+
+    /* Mark the endpoints as not yet open */
+    for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+        dev->ep_fd[i] = -1;
+    }
+
+    ioctl(dfd, USB_SETDEBUG, &ugendebug);
+
+    ret = (USBDevice *)dev;
+
+fail_dfd:
+    close(dfd);
+fail_bfd:
+    close(bfd);
+fail:
+    return ret;
+}
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->product_desc   = "USB Host Device";
+    uc->init           = usb_host_initfn;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_destroy = usb_host_handle_destroy;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = "usb-host",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+}
+
+type_init(usb_host_register_types)
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    struct usb_device_info bus_info;
+    struct usb_device_info dev_info;
+    uint16_t vendor_id, product_id, class_id, speed;
+    int bfd, dfd, bus, address;
+    char busbuf[20], devbuf[20], product_name[256];
+    int ret = 0;
+
+    for (bus = 0; bus < 10; bus++) {
+
+        snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
+        bfd = open(busbuf, O_RDWR);
+        if (bfd < 0)
+	    continue;
+
+        for (address = 1; address < 127; address++) {
+
+            bus_info.udi_addr = address;
+            if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
+                continue;
+
+            /* only list devices that can be used by generic layer */
+            if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
+                continue;
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+            dfd = open(devbuf, O_RDONLY);
+            if (dfd < 0) {
+#ifdef DEBUG
+                printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
+                       strerror(errno));
+#endif
+                continue;
+            }
+
+            if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
+                printf("usb_host_scan: couldn't get device information for %s - %s\n",
+                       devbuf, strerror(errno));
+
+            /* XXX: might need to fixup endianness of word values before copying over */
+
+            vendor_id = dev_info.udi_vendorNo;
+            product_id = dev_info.udi_productNo;
+            class_id = dev_info.udi_class;
+            speed = dev_info.udi_speed;
+
+            if (strncmp(dev_info.udi_product, "product", 7) != 0)
+                pstrcpy(product_name, sizeof(product_name),
+                        dev_info.udi_product);
+            else
+                product_name[0] = '\0';
+
+            ret = func(opaque, bus, address, class_id, vendor_id,
+                       product_id, product_name, speed);
+
+            close(dfd);
+
+            if (ret)
+                goto the_end;
+        }
+
+        close(bfd);
+    }
+
+the_end:
+    return ret;
+}
+
+typedef struct FindDeviceState {
+    int vendor_id;
+    int product_id;
+    int bus_num;
+    int addr;
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
+                                     int class_id,
+                                     int vendor_id, int product_id,
+                                     const char *product_name, int speed)
+{
+    FindDeviceState *s = opaque;
+    if (vendor_id == s->vendor_id &&
+        product_id == s->product_id) {
+        s->bus_num = bus_num;
+        s->addr = addr;
+        return 1;
+     } else {
+        return 0;
+     }
+}
+
+
+/* the syntax is :
+   'bus.addr' (decimal numbers) or
+   'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr,
+                                const char *devname)
+{
+    const char *p;
+    int ret;
+    FindDeviceState fs;
+
+    p = strchr(devname, '.');
+    if (p) {
+        *pbus_num = strtoul(devname, NULL, 0);
+        *paddr = strtoul(p + 1, NULL, 0);
+        return 0;
+    }
+    p = strchr(devname, ':');
+    if (p) {
+        fs.vendor_id = strtoul(devname, NULL, 16);
+        fs.product_id = strtoul(p + 1, NULL, 16);
+        ret = usb_host_scan(&fs, usb_host_find_device_scan);
+        if (ret) {
+            *pbus_num = fs.bus_num;
+            *paddr = fs.addr;
+            return 0;
+        }
+     }
+     return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID, "Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+    const struct usb_class_info *p;
+    for (p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class)
+            break;
+    }
+    return p->class_name;
+}
+
+static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
+                            int vendor_id, int product_id,
+                            const char *product_name,
+                            int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW:
+        speed_str = "1.5";
+        break;
+    case USB_SPEED_FULL:
+        speed_str = "12";
+        break;
+    case USB_SPEED_HIGH:
+        speed_str = "480";
+        break;
+    default:
+        speed_str = "?";
+        break;
+    }
+
+    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
+                   bus_num, addr, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str)
+        monitor_printf(mon, "    %s:", class_str);
+    else
+        monitor_printf(mon, "    Class %02x:", class_id);
+    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0')
+        monitor_printf(mon, ", %s", product_name);
+    monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque,
+                                int bus_num, int addr,
+                                int class_id,
+                                int vendor_id, int product_id,
+                                const char *product_name,
+                                int speed)
+{
+    Monitor *mon = opaque;
+
+    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+void usb_host_info(Monitor *mon)
+{
+    usb_host_scan(mon, usb_host_info_device);
+}
+
+/* XXX add this */
+int usb_host_device_close(const char *devname)
+{
+    return 0;
+}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
new file mode 100644
index 0000000000..90919c242a
--- /dev/null
+++ b/hw/usb/host-linux.c
@@ -0,0 +1,1913 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "trace.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include "hw/usb.h"
+
+/* We redefine it to avoid version problems */
+struct usb_ctrltransfer {
+    uint8_t  bRequestType;
+    uint8_t  bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+    uint32_t timeout;
+    void *data;
+};
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
+                        int class_id, int vendor_id, int product_id,
+                        const char *product_name, int speed);
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#define PRODUCT_NAME_SZ 32
+#define MAX_PORTLEN 16
+
+/* endpoint association data */
+#define ISO_FRAME_DESC_PER_URB 32
+
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
+typedef struct AsyncURB AsyncURB;
+
+struct endp_data {
+    uint8_t halted;
+    uint8_t iso_started;
+    AsyncURB *iso_urb;
+    int iso_urb_idx;
+    int iso_buffer_used;
+    int inflight;
+};
+
+struct USBAutoFilter {
+    uint32_t bus_num;
+    uint32_t addr;
+    char     *port;
+    uint32_t vendor_id;
+    uint32_t product_id;
+};
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int       fd;
+    int       hub_fd;
+    int       hub_port;
+
+    uint8_t   descr[8192];
+    int       descr_len;
+    int       closing;
+    uint32_t  iso_urb_count;
+    Notifier  exit;
+
+    struct endp_data ep_in[USB_MAX_ENDPOINTS];
+    struct endp_data ep_out[USB_MAX_ENDPOINTS];
+    QLIST_HEAD(, AsyncURB) aurbs;
+
+    /* Host side address */
+    int bus_num;
+    int addr;
+    char port[MAX_PORTLEN];
+    struct USBAutoFilter match;
+    int seen, errcount;
+
+    QTAILQ_ENTRY(USBHostDevice) next;
+} USBHostDevice;
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+static int usb_host_close(USBHostDevice *dev);
+static int parse_filter(const char *spec, struct USBAutoFilter *f);
+static void usb_host_auto_check(void *unused);
+static int usb_host_read_file(char *line, size_t line_size,
+                            const char *device_file, const char *device_name);
+static int usb_linux_update_endp_table(USBHostDevice *s);
+
+static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
+{
+    static const int usbfs[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
+        [USB_ENDPOINT_XFER_ISOC]    = USBDEVFS_URB_TYPE_ISO,
+        [USB_ENDPOINT_XFER_BULK]    = USBDEVFS_URB_TYPE_BULK,
+        [USB_ENDPOINT_XFER_INT]     = USBDEVFS_URB_TYPE_INTERRUPT,
+    };
+    uint8_t type = p->ep->type;
+    assert(type < ARRAY_SIZE(usbfs));
+    return usbfs[type];
+}
+
+static int usb_host_do_reset(USBHostDevice *dev)
+{
+    struct timeval s, e;
+    uint32_t usecs;
+    int ret;
+
+    gettimeofday(&s, NULL);
+    ret = ioctl(dev->fd, USBDEVFS_RESET);
+    gettimeofday(&e, NULL);
+    usecs = (e.tv_sec  - s.tv_sec) * 1000000;
+    usecs += e.tv_usec - s.tv_usec;
+    if (usecs > 1000000) {
+        /* more than a second, something is fishy, broken usb device? */
+        fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
+                dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
+    }
+    return ret;
+}
+
+static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
+    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
+    return eps + ep - 1;
+}
+
+static int is_isoc(USBHostDevice *s, int pid, int ep)
+{
+    return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
+}
+
+static int is_valid(USBHostDevice *s, int pid, int ep)
+{
+    return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
+}
+
+static int is_halted(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->halted;
+}
+
+static void clear_halt(USBHostDevice *s, int pid, int ep)
+{
+    trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
+    get_endp(s, pid, ep)->halted = 0;
+}
+
+static void set_halt(USBHostDevice *s, int pid, int ep)
+{
+    if (ep != 0) {
+        trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
+        get_endp(s, pid, ep)->halted = 1;
+    }
+}
+
+static int is_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_started;
+}
+
+static void clear_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
+    get_endp(s, pid, ep)->iso_started = 0;
+}
+
+static void set_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+
+    trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
+    if (!e->iso_started) {
+        e->iso_started = 1;
+        e->inflight = 0;
+    }
+}
+
+static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+
+    e->inflight += value;
+    return e->inflight;
+}
+
+static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
+{
+    get_endp(s, pid, ep)->iso_urb = iso_urb;
+}
+
+static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb;
+}
+
+static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_urb_idx = i;
+}
+
+static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb_idx;
+}
+
+static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_buffer_used = i;
+}
+
+static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_buffer_used;
+}
+
+/*
+ * Async URB state.
+ * We always allocate iso packet descriptors even for bulk transfers
+ * to simplify allocation and casts.
+ */
+struct AsyncURB
+{
+    struct usbdevfs_urb urb;
+    struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
+    USBHostDevice *hdev;
+    QLIST_ENTRY(AsyncURB) next;
+
+    /* For regular async urbs */
+    USBPacket     *packet;
+    int more; /* large transfer, more urbs follow */
+
+    /* For buffered iso handling */
+    int iso_frame_idx; /* -1 means in flight */
+};
+
+static AsyncURB *async_alloc(USBHostDevice *s)
+{
+    AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
+    aurb->hdev = s;
+    QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
+    return aurb;
+}
+
+static void async_free(AsyncURB *aurb)
+{
+    QLIST_REMOVE(aurb, next);
+    g_free(aurb);
+}
+
+static void do_disconnect(USBHostDevice *s)
+{
+    usb_host_close(s);
+    usb_host_auto_check(NULL);
+}
+
+static void async_complete(void *opaque)
+{
+    USBHostDevice *s = opaque;
+    AsyncURB *aurb;
+    int urbs = 0;
+
+    while (1) {
+        USBPacket *p;
+
+        int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+        if (r < 0) {
+            if (errno == EAGAIN) {
+                if (urbs > 2) {
+                    fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
+                }
+                return;
+            }
+            if (errno == ENODEV) {
+                if (!s->closing) {
+                    trace_usb_host_disconnect(s->bus_num, s->addr);
+                    do_disconnect(s);
+                }
+                return;
+            }
+
+            perror("USBDEVFS_REAPURBNDELAY");
+            return;
+        }
+
+        DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
+                aurb, aurb->urb.status, aurb->urb.actual_length);
+
+        /* If this is a buffered iso urb mark it as complete and don't do
+           anything else (it is handled further in usb_host_handle_iso_data) */
+        if (aurb->iso_frame_idx == -1) {
+            int inflight;
+            int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
+                USB_TOKEN_IN : USB_TOKEN_OUT;
+            int ep = aurb->urb.endpoint & 0xf;
+            if (aurb->urb.status == -EPIPE) {
+                set_halt(s, pid, ep);
+            }
+            aurb->iso_frame_idx = 0;
+            urbs++;
+            inflight = change_iso_inflight(s, pid, ep, -1);
+            if (inflight == 0 && is_iso_started(s, pid, ep)) {
+                fprintf(stderr, "husb: out of buffers for iso stream\n");
+            }
+            continue;
+        }
+
+        p = aurb->packet;
+        trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
+                                    aurb->urb.actual_length, aurb->more);
+
+        if (p) {
+            switch (aurb->urb.status) {
+            case 0:
+                p->result += aurb->urb.actual_length;
+                break;
+
+            case -EPIPE:
+                set_halt(s, p->pid, p->ep->nr);
+                p->result = USB_RET_STALL;
+                break;
+
+            case -EOVERFLOW:
+                p->result = USB_RET_BABBLE;
+                break;
+
+            default:
+                p->result = USB_RET_IOERROR;
+                break;
+            }
+
+            if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
+                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                usb_generic_async_ctrl_complete(&s->dev, p);
+            } else if (!aurb->more) {
+                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                usb_packet_complete(&s->dev, p);
+            }
+        }
+
+        async_free(aurb);
+    }
+}
+
+static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    AsyncURB *aurb;
+
+    QLIST_FOREACH(aurb, &s->aurbs, next) {
+        if (p != aurb->packet) {
+            continue;
+        }
+
+        DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
+
+        /* Mark it as dead (see async_complete above) */
+        aurb->packet = NULL;
+
+        int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+        if (r < 0) {
+            DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+        }
+    }
+}
+
+static int usb_host_open_device(int bus, int addr)
+{
+    const char *usbfs = NULL;
+    char filename[32];
+    struct stat st;
+    int fd, rc;
+
+    rc = stat("/dev/bus/usb", &st);
+    if (rc == 0 && S_ISDIR(st.st_mode)) {
+        /* udev-created device nodes available */
+        usbfs = "/dev/bus/usb";
+    } else {
+        /* fallback: usbfs mounted below /proc */
+        usbfs = "/proc/bus/usb";
+    }
+
+    snprintf(filename, sizeof(filename), "%s/%03d/%03d",
+             usbfs, bus, addr);
+    fd = open(filename, O_RDWR | O_NONBLOCK);
+    if (fd < 0) {
+        fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
+    }
+    return fd;
+}
+
+static int usb_host_claim_port(USBHostDevice *s)
+{
+#ifdef USBDEVFS_CLAIM_PORT
+    char *h, hub_name[64], line[1024];
+    int hub_addr, ret;
+
+    snprintf(hub_name, sizeof(hub_name), "%d-%s",
+             s->match.bus_num, s->match.port);
+
+    /* try strip off last ".$portnr" to get hub */
+    h = strrchr(hub_name, '.');
+    if (h != NULL) {
+        s->hub_port = atoi(h+1);
+        *h = '\0';
+    } else {
+        /* no dot in there -> it is the root hub */
+        snprintf(hub_name, sizeof(hub_name), "usb%d",
+                 s->match.bus_num);
+        s->hub_port = atoi(s->match.port);
+    }
+
+    if (!usb_host_read_file(line, sizeof(line), "devnum",
+                            hub_name)) {
+        return -1;
+    }
+    if (sscanf(line, "%d", &hub_addr) != 1) {
+        return -1;
+    }
+
+    s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
+    if (s->hub_fd < 0) {
+        return -1;
+    }
+
+    ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
+    if (ret < 0) {
+        close(s->hub_fd);
+        s->hub_fd = -1;
+        return -1;
+    }
+
+    trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+static void usb_host_release_port(USBHostDevice *s)
+{
+    if (s->hub_fd == -1) {
+        return;
+    }
+#ifdef USBDEVFS_RELEASE_PORT
+    ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
+#endif
+    close(s->hub_fd);
+    s->hub_fd = -1;
+}
+
+static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
+{
+    /* earlier Linux 2.4 do not support that */
+#ifdef USBDEVFS_DISCONNECT
+    struct usbdevfs_ioctl ctrl;
+    int ret, interface;
+
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+        ctrl.ifno = interface;
+        ctrl.data = 0;
+        ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+        if (ret < 0 && errno != ENODATA) {
+            perror("USBDEVFS_DISCONNECT");
+            return -1;
+        }
+    }
+#endif
+    return 0;
+}
+
+static int usb_linux_get_num_interfaces(USBHostDevice *s)
+{
+    char device_name[64], line[1024];
+    int num_interfaces = 0;
+
+    sprintf(device_name, "%d-%s", s->bus_num, s->port);
+    if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
+                            device_name)) {
+        return -1;
+    }
+    if (sscanf(line, "%d", &num_interfaces) != 1) {
+        return -1;
+    }
+    return num_interfaces;
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
+{
+    const char *op = NULL;
+    int dev_descr_len, config_descr_len;
+    int interface, nb_interfaces;
+    int ret, i;
+
+    for (i = 0; i < USB_MAX_INTERFACES; i++) {
+        dev->dev.altsetting[i] = 0;
+    }
+
+    if (configuration == 0) { /* address state - ignore */
+        dev->dev.ninterfaces   = 0;
+        dev->dev.configuration = 0;
+        return 1;
+    }
+
+    DPRINTF("husb: claiming interfaces. config %d\n", configuration);
+
+    i = 0;
+    dev_descr_len = dev->descr[0];
+    if (dev_descr_len > dev->descr_len) {
+        fprintf(stderr, "husb: update iface failed. descr too short\n");
+        return 0;
+    }
+
+    i += dev_descr_len;
+    while (i < dev->descr_len) {
+        DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
+                i, dev->descr_len,
+               dev->descr[i], dev->descr[i+1]);
+
+        if (dev->descr[i+1] != USB_DT_CONFIG) {
+            i += dev->descr[i];
+            continue;
+        }
+        config_descr_len = dev->descr[i];
+
+        DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
+
+        if (configuration == dev->descr[i + 5]) {
+            configuration = dev->descr[i + 5];
+            break;
+        }
+
+        i += config_descr_len;
+    }
+
+    if (i >= dev->descr_len) {
+        fprintf(stderr,
+                "husb: update iface failed. no matching configuration\n");
+        return 0;
+    }
+    nb_interfaces = dev->descr[i + 4];
+
+    if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
+        goto fail;
+    }
+
+    /* XXX: only grab if all interfaces are free */
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        op = "USBDEVFS_CLAIMINTERFACE";
+        ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
+                                    nb_interfaces, configuration);
+
+    dev->dev.ninterfaces   = nb_interfaces;
+    dev->dev.configuration = configuration;
+    return 1;
+
+fail:
+    if (errno == ENODEV) {
+        do_disconnect(dev);
+    }
+    perror(op);
+    return 0;
+}
+
+static int usb_host_release_interfaces(USBHostDevice *s)
+{
+    int ret, i;
+
+    trace_usb_host_release_interfaces(s->bus_num, s->addr);
+
+    for (i = 0; i < s->dev.ninterfaces; i++) {
+        ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+        if (ret < 0) {
+            perror("USBDEVFS_RELEASEINTERFACE");
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+    trace_usb_host_reset(s->bus_num, s->addr);
+
+    usb_host_do_reset(s);;
+
+    usb_host_claim_interfaces(s, 0);
+    usb_linux_update_endp_table(s);
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+
+    usb_host_release_port(s);
+    usb_host_close(s);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    qemu_remove_exit_notifier(&s->exit);
+}
+
+/* iso data is special, we need to keep enough urbs in flight to make sure
+   that the controller never runs out of them, otherwise the device will
+   likely suffer a buffer underrun / overrun. */
+static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
+{
+    AsyncURB *aurb;
+    int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
+
+    aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
+    for (i = 0; i < s->iso_urb_count; i++) {
+        aurb[i].urb.endpoint      = ep;
+        aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
+        aurb[i].urb.buffer        = g_malloc(aurb[i].urb.buffer_length);
+        aurb[i].urb.type          = USBDEVFS_URB_TYPE_ISO;
+        aurb[i].urb.flags         = USBDEVFS_URB_ISO_ASAP;
+        aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
+        for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
+            aurb[i].urb.iso_frame_desc[j].length = len;
+        if (pid == USB_TOKEN_IN) {
+            aurb[i].urb.endpoint |= 0x80;
+            /* Mark as fully consumed (idle) */
+            aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
+        }
+    }
+    set_iso_urb(s, pid, ep, aurb);
+
+    return aurb;
+}
+
+static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
+{
+    AsyncURB *aurb;
+    int i, ret, killed = 0, free = 1;
+
+    aurb = get_iso_urb(s, pid, ep);
+    if (!aurb) {
+        return;
+    }
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        /* in flight? */
+        if (aurb[i].iso_frame_idx == -1) {
+            ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
+            if (ret < 0) {
+                perror("USBDEVFS_DISCARDURB");
+                free = 0;
+                continue;
+            }
+            killed++;
+        }
+    }
+
+    /* Make sure any urbs we've killed are reaped before we free them */
+    if (killed) {
+        async_complete(s);
+    }
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        g_free(aurb[i].urb.buffer);
+    }
+
+    if (free)
+        g_free(aurb);
+    else
+        printf("husb: leaking iso urbs because of discard failure\n");
+    set_iso_urb(s, pid, ep, NULL);
+    set_iso_urb_idx(s, pid, ep, 0);
+    clear_iso_started(s, pid, ep);
+}
+
+static int urb_status_to_usb_ret(int status)
+{
+    switch (status) {
+    case -EPIPE:
+        return USB_RET_STALL;
+    case -EOVERFLOW:
+        return USB_RET_BABBLE;
+    default:
+        return USB_RET_IOERROR;
+    }
+}
+
+static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
+{
+    AsyncURB *aurb;
+    int i, j, ret, max_packet_size, offset, len = 0;
+    uint8_t *buf;
+
+    max_packet_size = p->ep->max_packet_size;
+    if (max_packet_size == 0)
+        return USB_RET_NAK;
+
+    aurb = get_iso_urb(s, p->pid, p->ep->nr);
+    if (!aurb) {
+        aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
+    }
+
+    i = get_iso_urb_idx(s, p->pid, p->ep->nr);
+    j = aurb[i].iso_frame_idx;
+    if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
+        if (in) {
+            /* Check urb status  */
+            if (aurb[i].urb.status) {
+                len = urb_status_to_usb_ret(aurb[i].urb.status);
+                /* Move to the next urb */
+                aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
+            /* Check frame status */
+            } else if (aurb[i].urb.iso_frame_desc[j].status) {
+                len = urb_status_to_usb_ret(
+                                        aurb[i].urb.iso_frame_desc[j].status);
+            /* Check the frame fits */
+            } else if (aurb[i].urb.iso_frame_desc[j].actual_length
+                       > p->iov.size) {
+                printf("husb: received iso data is larger then packet\n");
+                len = USB_RET_BABBLE;
+            /* All good copy data over */
+            } else {
+                len = aurb[i].urb.iso_frame_desc[j].actual_length;
+                buf  = aurb[i].urb.buffer +
+                    j * aurb[i].urb.iso_frame_desc[0].length;
+                usb_packet_copy(p, buf, len);
+            }
+        } else {
+            len = p->iov.size;
+            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
+
+            /* Check the frame fits */
+            if (len > max_packet_size) {
+                printf("husb: send iso data is larger then max packet size\n");
+                return USB_RET_NAK;
+            }
+
+            /* All good copy data over */
+            usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
+            aurb[i].urb.iso_frame_desc[j].length = len;
+            offset += len;
+            set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
+
+            /* Start the stream once we have buffered enough data */
+            if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
+                set_iso_started(s, p->pid, p->ep->nr);
+            }
+        }
+        aurb[i].iso_frame_idx++;
+        if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+            i = (i + 1) % s->iso_urb_count;
+            set_iso_urb_idx(s, p->pid, p->ep->nr, i);
+        }
+    } else {
+        if (in) {
+            set_iso_started(s, p->pid, p->ep->nr);
+        } else {
+            DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
+        }
+    }
+
+    if (is_iso_started(s, p->pid, p->ep->nr)) {
+        /* (Re)-submit all fully consumed / filled urbs */
+        for (i = 0; i < s->iso_urb_count; i++) {
+            if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+                ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
+                if (ret < 0) {
+                    perror("USBDEVFS_SUBMITURB");
+                    if (!in || len == 0) {
+                        switch(errno) {
+                        case ETIMEDOUT:
+                            len = USB_RET_NAK;
+                            break;
+                        case EPIPE:
+                        default:
+                            len = USB_RET_STALL;
+                        }
+                    }
+                    break;
+                }
+                aurb[i].iso_frame_idx = -1;
+                change_iso_inflight(s, p->pid, p->ep->nr, 1);
+            }
+        }
+    }
+
+    return len;
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret, rem, prem, v;
+    uint8_t *pbuf;
+    uint8_t ep;
+
+    trace_usb_host_req_data(s->bus_num, s->addr,
+                            p->pid == USB_TOKEN_IN,
+                            p->ep->nr, p->iov.size);
+
+    if (!is_valid(s, p->pid, p->ep->nr)) {
+        trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+        return USB_RET_NAK;
+    }
+
+    if (p->pid == USB_TOKEN_IN) {
+        ep = p->ep->nr | 0x80;
+    } else {
+        ep = p->ep->nr;
+    }
+
+    if (is_halted(s, p->pid, p->ep->nr)) {
+        unsigned int arg = ep;
+        ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
+        if (ret < 0) {
+            perror("USBDEVFS_CLEAR_HALT");
+            trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+            return USB_RET_NAK;
+        }
+        clear_halt(s, p->pid, p->ep->nr);
+    }
+
+    if (is_isoc(s, p->pid, p->ep->nr)) {
+        return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+    }
+
+    v = 0;
+    prem = p->iov.iov[v].iov_len;
+    pbuf = p->iov.iov[v].iov_base;
+    rem = p->iov.size;
+    while (rem) {
+        if (prem == 0) {
+            v++;
+            assert(v < p->iov.niov);
+            prem = p->iov.iov[v].iov_len;
+            pbuf = p->iov.iov[v].iov_base;
+            assert(prem <= rem);
+        }
+        aurb = async_alloc(s);
+        aurb->packet = p;
+
+        urb = &aurb->urb;
+        urb->endpoint      = ep;
+        urb->type          = usb_host_usbfs_type(s, p);
+        urb->usercontext   = s;
+        urb->buffer        = pbuf;
+        urb->buffer_length = prem;
+
+        if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
+            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+        }
+        pbuf += urb->buffer_length;
+        prem -= urb->buffer_length;
+        rem  -= urb->buffer_length;
+        if (rem) {
+            aurb->more         = 1;
+        }
+
+        trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
+                                  urb->buffer_length, aurb->more);
+        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
+
+        if (ret < 0) {
+            perror("USBDEVFS_SUBMITURB");
+            async_free(aurb);
+
+            switch(errno) {
+            case ETIMEDOUT:
+                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+                return USB_RET_NAK;
+            case EPIPE:
+            default:
+                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
+                return USB_RET_STALL;
+            }
+        }
+    }
+
+    return USB_RET_ASYNC;
+}
+
+static int ctrl_error(void)
+{
+    if (errno == ETIMEDOUT) {
+        return USB_RET_NAK;
+    } else {
+        return USB_RET_STALL;
+    }
+}
+
+static int usb_host_set_address(USBHostDevice *s, int addr)
+{
+    trace_usb_host_set_address(s->bus_num, s->addr, addr);
+    s->dev.addr = addr;
+    return 0;
+}
+
+static int usb_host_set_config(USBHostDevice *s, int config)
+{
+    int ret, first = 1;
+
+    trace_usb_host_set_config(s->bus_num, s->addr, config);
+
+    usb_host_release_interfaces(s);
+
+again:
+    ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+
+    DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+
+    if (ret < 0 && errno == EBUSY && first) {
+        /* happens if usb device is in use by host drivers */
+        int count = usb_linux_get_num_interfaces(s);
+        if (count > 0) {
+            DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
+            usb_host_disconnect_ifaces(s, count);
+            first = 0;
+            goto again;
+        }
+    }
+
+    if (ret < 0) {
+        return ctrl_error();
+    }
+    usb_host_claim_interfaces(s, config);
+    usb_linux_update_endp_table(s);
+    return 0;
+}
+
+static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
+{
+    struct usbdevfs_setinterface si;
+    int i, ret;
+
+    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
+
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
+        if (is_isoc(s, USB_TOKEN_IN, i)) {
+            usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
+        }
+        if (is_isoc(s, USB_TOKEN_OUT, i)) {
+            usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
+        }
+    }
+
+    if (iface >= USB_MAX_INTERFACES) {
+        return USB_RET_STALL;
+    }
+
+    si.interface  = iface;
+    si.altsetting = alt;
+    ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+
+    DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
+            iface, alt, ret, errno);
+
+    if (ret < 0) {
+        return ctrl_error();
+    }
+
+    s->dev.altsetting[iface] = alt;
+    usb_linux_update_endp_table(s);
+    return 0;
+}
+
+static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret;
+
+    /*
+     * Process certain standard device requests.
+     * These are infrequent and are processed synchronously.
+     */
+
+    /* Note request is (bRequestType << 8) | bRequest */
+    trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
+
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        return usb_host_set_address(s, value);
+
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        return usb_host_set_config(s, value & 0xff);
+
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        return usb_host_set_interface(s, index, value);
+    }
+
+    /* The rest are asynchronous */
+
+    if (length > sizeof(dev->data_buf)) {
+        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+                length, sizeof(dev->data_buf));
+        return USB_RET_STALL;
+    }
+
+    aurb = async_alloc(s);
+    aurb->packet = p;
+
+    /*
+     * Setup ctrl transfer.
+     *
+     * s->ctrl is laid out such that data buffer immediately follows
+     * 'req' struct which is exactly what usbdevfs expects.
+     */
+    urb = &aurb->urb;
+
+    urb->type     = USBDEVFS_URB_TYPE_CONTROL;
+    urb->endpoint = p->ep->nr;
+
+    urb->buffer        = &dev->setup_buf;
+    urb->buffer_length = length + 8;
+
+    urb->usercontext = s;
+
+    trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
+                              urb->buffer_length, aurb->more);
+    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+    DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
+
+    if (ret < 0) {
+        DPRINTF("husb: submit failed. errno %d\n", errno);
+        async_free(aurb);
+
+        switch(errno) {
+        case ETIMEDOUT:
+            return USB_RET_NAK;
+        case EPIPE:
+        default:
+            return USB_RET_STALL;
+        }
+    }
+
+    return USB_RET_ASYNC;
+}
+
+static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
+    uint8_t configuration, uint8_t interface)
+{
+    char device_name[64], line[1024];
+    int alt_setting;
+
+    sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
+            (int)configuration, (int)interface);
+
+    if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
+                            device_name)) {
+        /* Assume alt 0 on error */
+        return 0;
+    }
+    if (sscanf(line, "%d", &alt_setting) != 1) {
+        /* Assume alt 0 on error */
+        return 0;
+    }
+    return alt_setting;
+}
+
+/* returns 1 on problem encountered or 0 for success */
+static int usb_linux_update_endp_table(USBHostDevice *s)
+{
+    uint8_t *descriptors;
+    uint8_t devep, type, alt_interface;
+    uint16_t raw;
+    int interface, length, i, ep, pid;
+    struct endp_data *epd;
+
+    usb_ep_init(&s->dev);
+
+    if (s->dev.configuration == 0) {
+        /* not configured yet -- leave all endpoints disabled */
+        return 0;
+    }
+
+    /* get the desired configuration, interface, and endpoint descriptors
+     * from device description */
+    descriptors = &s->descr[18];
+    length = s->descr_len - 18;
+    i = 0;
+
+    while (i < length) {
+        if (descriptors[i + 1] != USB_DT_CONFIG) {
+            fprintf(stderr, "invalid descriptor data\n");
+            return 1;
+        } else if (descriptors[i + 5] != s->dev.configuration) {
+            DPRINTF("not requested configuration %d\n", s->dev.configuration);
+            i += (descriptors[i + 3] << 8) + descriptors[i + 2];
+            continue;
+        }
+        i += descriptors[i];
+
+        if (descriptors[i + 1] != USB_DT_INTERFACE ||
+            (descriptors[i + 1] == USB_DT_INTERFACE &&
+             descriptors[i + 4] == 0)) {
+            i += descriptors[i];
+            continue;
+        }
+
+        interface = descriptors[i + 2];
+        alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
+                                                  interface);
+
+        /* the current interface descriptor is the active interface
+         * and has endpoints */
+        if (descriptors[i + 3] != alt_interface) {
+            i += descriptors[i];
+            continue;
+        }
+
+        /* advance to the endpoints */
+        while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
+            i += descriptors[i];
+        }
+
+        if (i >= length)
+            break;
+
+        while (i < length) {
+            if (descriptors[i + 1] != USB_DT_ENDPOINT) {
+                break;
+            }
+
+            devep = descriptors[i + 2];
+            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = devep & 0xf;
+            if (ep == 0) {
+                fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
+                return 1;
+            }
+
+            type = descriptors[i + 3] & 0x3;
+            raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
+            usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
+            assert(usb_ep_get_type(&s->dev, pid, ep) ==
+                   USB_ENDPOINT_XFER_INVALID);
+            usb_ep_set_type(&s->dev, pid, ep, type);
+            usb_ep_set_ifnum(&s->dev, pid, ep, interface);
+            if (type == USB_ENDPOINT_XFER_BULK) {
+                usb_ep_set_pipeline(&s->dev, pid, ep, true);
+            }
+
+            epd = get_endp(s, pid, ep);
+            epd->halted = 0;
+
+            i += descriptors[i];
+        }
+    }
+#ifdef DEBUG
+    usb_ep_dump(&s->dev);
+#endif
+    return 0;
+}
+
+/*
+ * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
+ * this function assumes this is safe, if:
+ * 1) There are no isoc endpoints
+ * 2) There are no interrupt endpoints with a max_packet_size > 64
+ * Note bulk endpoints with a max_packet_size > 64 in theory also are not
+ * usb1 compatible, but in practice this seems to work fine.
+ */
+static int usb_linux_full_speed_compat(USBHostDevice *dev)
+{
+    int i, packet_size;
+
+    /*
+     * usb_linux_update_endp_table only registers info about ep in the current
+     * interface altsettings, so we need to parse the descriptors again.
+     */
+    for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
+        if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
+            switch (dev->descr[i + 3] & 0x3) {
+            case 0x00: /* CONTROL */
+                break;
+            case 0x01: /* ISO */
+                return 0;
+            case 0x02: /* BULK */
+                break;
+            case 0x03: /* INTERRUPT */
+                packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
+                if (packet_size > 64)
+                    return 0;
+                break;
+            }
+        }
+    }
+    return 1;
+}
+
+static int usb_host_open(USBHostDevice *dev, int bus_num,
+                         int addr, const char *port,
+                         const char *prod_name, int speed)
+{
+    int fd = -1, ret;
+
+    trace_usb_host_open_started(bus_num, addr);
+
+    if (dev->fd != -1) {
+        goto fail;
+    }
+
+    fd = usb_host_open_device(bus_num, addr);
+    if (fd < 0) {
+        goto fail;
+    }
+    DPRINTF("husb: opened %s\n", buf);
+
+    dev->bus_num = bus_num;
+    dev->addr = addr;
+    strcpy(dev->port, port);
+    dev->fd = fd;
+
+    /* read the device description */
+    dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
+    if (dev->descr_len <= 0) {
+        perror("husb: reading device data failed");
+        goto fail;
+    }
+
+#ifdef DEBUG
+    {
+        int x;
+        printf("=== begin dumping device descriptor data ===\n");
+        for (x = 0; x < dev->descr_len; x++) {
+            printf("%02x ", dev->descr[x]);
+        }
+        printf("\n=== end dumping device descriptor data ===\n");
+    }
+#endif
+
+
+    /* start unconfigured -- we'll wait for the guest to set a configuration */
+    if (!usb_host_claim_interfaces(dev, 0)) {
+        goto fail;
+    }
+
+    ret = usb_linux_update_endp_table(dev);
+    if (ret) {
+        goto fail;
+    }
+
+    if (speed == -1) {
+        struct usbdevfs_connectinfo ci;
+
+        ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+        if (ret < 0) {
+            perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+            goto fail;
+        }
+
+        if (ci.slow) {
+            speed = USB_SPEED_LOW;
+        } else {
+            speed = USB_SPEED_HIGH;
+        }
+    }
+    dev->dev.speed = speed;
+    dev->dev.speedmask = (1 << speed);
+    if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
+        dev->dev.speedmask |= USB_SPEED_MASK_FULL;
+    }
+
+    trace_usb_host_open_success(bus_num, addr);
+
+    if (!prod_name || prod_name[0] == '\0') {
+        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                 "host:%d.%d", bus_num, addr);
+    } else {
+        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+                prod_name);
+    }
+
+    ret = usb_device_attach(&dev->dev);
+    if (ret) {
+        goto fail;
+    }
+
+    /* USB devio uses 'write' flag to check for async completions */
+    qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
+
+    return 0;
+
+fail:
+    trace_usb_host_open_failure(bus_num, addr);
+    if (dev->fd != -1) {
+        close(dev->fd);
+        dev->fd = -1;
+    }
+    return -1;
+}
+
+static int usb_host_close(USBHostDevice *dev)
+{
+    int i;
+
+    if (dev->fd == -1) {
+        return -1;
+    }
+
+    trace_usb_host_close(dev->bus_num, dev->addr);
+
+    qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
+    dev->closing = 1;
+    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
+        if (is_isoc(dev, USB_TOKEN_IN, i)) {
+            usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
+        }
+        if (is_isoc(dev, USB_TOKEN_OUT, i)) {
+            usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
+        }
+    }
+    async_complete(dev);
+    dev->closing = 0;
+    if (dev->dev.attached) {
+        usb_device_detach(&dev->dev);
+    }
+    usb_host_do_reset(dev);
+    close(dev->fd);
+    dev->fd = -1;
+    return 0;
+}
+
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
+{
+    USBHostDevice *s = container_of(n, USBHostDevice, exit);
+
+    usb_host_release_port(s);
+    if (s->fd != -1) {
+        usb_host_do_reset(s);;
+    }
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+    dev->auto_attach = 0;
+    s->fd = -1;
+    s->hub_fd = -1;
+
+    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+    s->exit.notify = usb_host_exit_notifier;
+    qemu_add_exit_notifier(&s->exit);
+    usb_host_auto_check(NULL);
+
+    if (s->match.bus_num != 0 && s->match.port != NULL) {
+        usb_host_claim_port(s);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_host = {
+    .name = "usb-host",
+    .unmigratable = 1,
+};
+
+static Property usb_host_dev_properties[] = {
+    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
+    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
+    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
+    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
+    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
+    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_host_initfn;
+    uc->product_desc   = "USB Host Device";
+    uc->cancel_packet  = usb_host_async_cancel;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_destroy = usb_host_handle_destroy;
+    dc->vmsd = &vmstate_usb_host;
+    dc->props = usb_host_dev_properties;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = "usb-host",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+    usb_legacy_register("usb-host", "host", usb_host_device_open);
+}
+
+type_init(usb_host_register_types)
+
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
+{
+    struct USBAutoFilter filter;
+    USBDevice *dev;
+    char *p;
+
+    dev = usb_create(bus, "usb-host");
+
+    if (strstr(devname, "auto:")) {
+        if (parse_filter(devname, &filter) < 0) {
+            goto fail;
+        }
+    } else {
+        if ((p = strchr(devname, '.'))) {
+            filter.bus_num    = strtoul(devname, NULL, 0);
+            filter.addr       = strtoul(p + 1, NULL, 0);
+            filter.vendor_id  = 0;
+            filter.product_id = 0;
+        } else if ((p = strchr(devname, ':'))) {
+            filter.bus_num    = 0;
+            filter.addr       = 0;
+            filter.vendor_id  = strtoul(devname, NULL, 16);
+            filter.product_id = strtoul(p + 1, NULL, 16);
+        } else {
+            goto fail;
+        }
+    }
+
+    qdev_prop_set_uint32(&dev->qdev, "hostbus",   filter.bus_num);
+    qdev_prop_set_uint32(&dev->qdev, "hostaddr",  filter.addr);
+    qdev_prop_set_uint32(&dev->qdev, "vendorid",  filter.vendor_id);
+    qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
+    qdev_init_nofail(&dev->qdev);
+    return dev;
+
+fail:
+    qdev_free(&dev->qdev);
+    return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+#if 0
+    char product_name[PRODUCT_NAME_SZ];
+    int bus_num, addr;
+    USBHostDevice *s;
+
+    if (strstr(devname, "auto:")) {
+        return usb_host_auto_del(devname);
+    }
+    if (usb_host_find_device(&bus_num, &addr, product_name,
+                                    sizeof(product_name), devname) < 0) {
+        return -1;
+    }
+    s = hostdev_find(bus_num, addr);
+    if (s) {
+        usb_device_delete_addr(s->bus_num, s->dev.addr);
+        return 0;
+    }
+#endif
+
+    return -1;
+}
+
+/*
+ * Read sys file-system device file
+ *
+ * @line address of buffer to put file contents in
+ * @line_size size of line
+ * @device_file path to device file (printf format string)
+ * @device_name device being opened (inserted into device_file)
+ *
+ * @return 0 failed, 1 succeeded ('line' contains data)
+ */
+static int usb_host_read_file(char *line, size_t line_size,
+                              const char *device_file, const char *device_name)
+{
+    FILE *f;
+    int ret = 0;
+    char filename[PATH_MAX];
+
+    snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
+             device_file);
+    f = fopen(filename, "r");
+    if (f) {
+        ret = fgets(line, line_size, f) != NULL;
+        fclose(f);
+    }
+
+    return ret;
+}
+
+/*
+ * Use /sys/bus/usb/devices/ directory to determine host's USB
+ * devices.
+ *
+ * This code is based on Robert Schiele's original patches posted to
+ * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
+ */
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    DIR *dir = NULL;
+    char line[1024];
+    int bus_num, addr, speed, class_id, product_id, vendor_id;
+    int ret = 0;
+    char port[MAX_PORTLEN];
+    char product_name[512];
+    struct dirent *de;
+
+    dir = opendir("/sys/bus/usb/devices");
+    if (!dir) {
+        perror("husb: opendir /sys/bus/usb/devices");
+        fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
+        goto the_end;
+    }
+
+    while ((de = readdir(dir))) {
+        if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
+            if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
+                continue;
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%d", &addr) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &class_id) != 1) {
+                goto the_end;
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "idVendor",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &vendor_id) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "idProduct",
+                                    de->d_name)) {
+                goto the_end;
+            }
+            if (sscanf(line, "%x", &product_id) != 1) {
+                goto the_end;
+            }
+            if (!usb_host_read_file(line, sizeof(line), "product",
+                                    de->d_name)) {
+                *product_name = 0;
+            } else {
+                if (strlen(line) > 0) {
+                    line[strlen(line) - 1] = '\0';
+                }
+                pstrcpy(product_name, sizeof(product_name), line);
+            }
+
+            if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
+                goto the_end;
+            }
+            if (!strcmp(line, "5000\n")) {
+                speed = USB_SPEED_SUPER;
+            } else if (!strcmp(line, "480\n")) {
+                speed = USB_SPEED_HIGH;
+            } else if (!strcmp(line, "1.5\n")) {
+                speed = USB_SPEED_LOW;
+            } else {
+                speed = USB_SPEED_FULL;
+            }
+
+            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
+                       product_id, product_name, speed);
+            if (ret) {
+                goto the_end;
+            }
+        }
+    }
+ the_end:
+    if (dir) {
+        closedir(dir);
+    }
+    return ret;
+}
+
+static QEMUTimer *usb_auto_timer;
+
+static int usb_host_auto_scan(void *opaque, int bus_num,
+                              int addr, const char *port,
+                              int class_id, int vendor_id, int product_id,
+                              const char *product_name, int speed)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    /* Ignore hubs */
+    if (class_id == 9)
+        return 0;
+
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        f = &s->match;
+
+        if (f->bus_num > 0 && f->bus_num != bus_num) {
+            continue;
+        }
+        if (f->addr > 0 && f->addr != addr) {
+            continue;
+        }
+        if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
+            continue;
+        }
+
+        if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
+            continue;
+        }
+
+        if (f->product_id > 0 && f->product_id != product_id) {
+            continue;
+        }
+        /* We got a match */
+        s->seen++;
+        if (s->errcount >= 3) {
+            return 0;
+        }
+
+        /* Already attached ? */
+        if (s->fd != -1) {
+            return 0;
+        }
+        DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
+
+        if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
+            s->errcount++;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+static void usb_host_auto_check(void *unused)
+{
+    struct USBHostDevice *s;
+    int unconnected = 0;
+
+    usb_host_scan(NULL, usb_host_auto_scan);
+
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        if (s->fd == -1) {
+            unconnected++;
+        }
+        if (s->seen == 0) {
+            s->errcount = 0;
+        }
+        s->seen = 0;
+    }
+
+    if (unconnected == 0) {
+        /* nothing to watch */
+        if (usb_auto_timer) {
+            qemu_del_timer(usb_auto_timer);
+            trace_usb_host_auto_scan_disabled();
+        }
+        return;
+    }
+
+    if (!usb_auto_timer) {
+        usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
+        if (!usb_auto_timer) {
+            return;
+        }
+        trace_usb_host_auto_scan_enabled();
+    }
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
+}
+
+/*
+ * Autoconnect filter
+ * Format:
+ *    auto:bus:dev[:vid:pid]
+ *    auto:bus.dev[:vid:pid]
+ *
+ *    bus  - bus number    (dec, * means any)
+ *    dev  - device number (dec, * means any)
+ *    vid  - vendor id     (hex, * means any)
+ *    pid  - product id    (hex, * means any)
+ *
+ *    See 'lsusb' output.
+ */
+static int parse_filter(const char *spec, struct USBAutoFilter *f)
+{
+    enum { BUS, DEV, VID, PID, DONE };
+    const char *p = spec;
+    int i;
+
+    f->bus_num    = 0;
+    f->addr       = 0;
+    f->vendor_id  = 0;
+    f->product_id = 0;
+
+    for (i = BUS; i < DONE; i++) {
+        p = strpbrk(p, ":.");
+        if (!p) {
+            break;
+        }
+        p++;
+
+        if (*p == '*') {
+            continue;
+        }
+        switch(i) {
+        case BUS: f->bus_num = strtol(p, NULL, 10);    break;
+        case DEV: f->addr    = strtol(p, NULL, 10);    break;
+        case VID: f->vendor_id  = strtol(p, NULL, 16); break;
+        case PID: f->product_id = strtol(p, NULL, 16); break;
+        }
+    }
+
+    if (i < DEV) {
+        fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID, "Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+    const struct usb_class_info *p;
+    for(p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class) {
+            break;
+        }
+    }
+    return p->class_name;
+}
+
+static void usb_info_device(Monitor *mon, int bus_num,
+                            int addr, const char *port,
+                            int class_id, int vendor_id, int product_id,
+                            const char *product_name,
+                            int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW:
+        speed_str = "1.5";
+        break;
+    case USB_SPEED_FULL:
+        speed_str = "12";
+        break;
+    case USB_SPEED_HIGH:
+        speed_str = "480";
+        break;
+    case USB_SPEED_SUPER:
+        speed_str = "5000";
+        break;
+    default:
+        speed_str = "?";
+        break;
+    }
+
+    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                   bus_num, addr, port, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str) {
+        monitor_printf(mon, "    %s:", class_str);
+    } else {
+        monitor_printf(mon, "    Class %02x:", class_id);
+    }
+    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0') {
+        monitor_printf(mon, ", %s", product_name);
+    }
+    monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+                                const char *path, int class_id,
+                                int vendor_id, int product_id,
+                                const char *product_name,
+                                int speed)
+{
+    Monitor *mon = opaque;
+
+    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+static void dec2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%d", val);
+    }
+}
+
+static void hex2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%04x", val);
+    }
+}
+
+void usb_host_info(Monitor *mon)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    usb_host_scan(mon, usb_host_info_device);
+
+    if (QTAILQ_EMPTY(&hostdevs)) {
+        return;
+    }
+
+    monitor_printf(mon, "  Auto filters:\n");
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        char bus[10], addr[10], vid[10], pid[10];
+        f = &s->match;
+        dec2str(f->bus_num, bus, sizeof(bus));
+        dec2str(f->addr, addr, sizeof(addr));
+        hex2str(f->vendor_id, vid, sizeof(vid));
+        hex2str(f->product_id, pid, sizeof(pid));
+        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
+                       bus, addr, f->port ? f->port : "*", vid, pid);
+    }
+}
diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c
new file mode 100644
index 0000000000..b4e10c12ca
--- /dev/null
+++ b/hw/usb/host-stub.c
@@ -0,0 +1,52 @@
+/*
+ * Stub host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "console.h"
+#include "hw/usb.h"
+#include "monitor.h"
+
+void usb_host_info(Monitor *mon)
+{
+    monitor_printf(mon, "USB host devices not supported\n");
+}
+
+/* XXX: modify configure to compile the right host driver */
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
+{
+    return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+    return 0;
+}
diff --git a/hw/usb-libhw.c b/hw/usb/libhw.c
index 162b42bd5b..2462351389 100644
--- a/hw/usb-libhw.c
+++ b/hw/usb/libhw.c
@@ -21,7 +21,7 @@
  */
 #include "qemu-common.h"
 #include "cpu-common.h"
-#include "usb.h"
+#include "hw/usb.h"
 #include "dma.h"
 
 int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
new file mode 100644
index 0000000000..8e9f175dbb
--- /dev/null
+++ b/hw/usb/redirect.c
@@ -0,0 +1,1485 @@
+/*
+ * USB redirector usb-guest
+ *
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <usbredirparser.h>
+#include <usbredirfilter.h>
+
+#include "hw/usb.h"
+
+#define MAX_ENDPOINTS 32
+#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+
+typedef struct AsyncURB AsyncURB;
+typedef struct USBRedirDevice USBRedirDevice;
+
+/* Struct to hold buffered packets (iso or int input packets) */
+struct buf_packet {
+    uint8_t *data;
+    int len;
+    int status;
+    QTAILQ_ENTRY(buf_packet)next;
+};
+
+struct endp_data {
+    uint8_t type;
+    uint8_t interval;
+    uint8_t interface; /* bInterfaceNumber this ep belongs to */
+    uint8_t iso_started;
+    uint8_t iso_error; /* For reporting iso errors to the HC */
+    uint8_t interrupt_started;
+    uint8_t interrupt_error;
+    uint8_t bufpq_prefilled;
+    uint8_t bufpq_dropping_packets;
+    QTAILQ_HEAD(, buf_packet) bufpq;
+    int bufpq_size;
+    int bufpq_target_size;
+};
+
+struct USBRedirDevice {
+    USBDevice dev;
+    /* Properties */
+    CharDriverState *cs;
+    uint8_t debug;
+    char *filter_str;
+    /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
+    const uint8_t *read_buf;
+    int read_buf_size;
+    /* For async handling of open/close */
+    QEMUBH *open_close_bh;
+    /* To delay the usb attach in case of quick chardev close + open */
+    QEMUTimer *attach_timer;
+    int64_t next_attach_time;
+    struct usbredirparser *parser;
+    struct endp_data endpoint[MAX_ENDPOINTS];
+    uint32_t packet_id;
+    QTAILQ_HEAD(, AsyncURB) asyncq;
+    /* Data for device filtering */
+    struct usb_redir_device_connect_header device_info;
+    struct usb_redir_interface_info_header interface_info;
+    struct usbredirfilter_rule *filter_rules;
+    int filter_rules_count;
+};
+
+struct AsyncURB {
+    USBRedirDevice *dev;
+    USBPacket *packet;
+    uint32_t packet_id;
+    int get;
+    union {
+        struct usb_redir_control_packet_header control_packet;
+        struct usb_redir_bulk_packet_header bulk_packet;
+        struct usb_redir_interrupt_packet_header interrupt_packet;
+    };
+    QTAILQ_ENTRY(AsyncURB)next;
+};
+
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
+static void usbredir_device_connect(void *priv,
+    struct usb_redir_device_connect_header *device_connect);
+static void usbredir_device_disconnect(void *priv);
+static void usbredir_interface_info(void *priv,
+    struct usb_redir_interface_info_header *interface_info);
+static void usbredir_ep_info(void *priv,
+    struct usb_redir_ep_info_header *ep_info);
+static void usbredir_configuration_status(void *priv, uint32_t id,
+    struct usb_redir_configuration_status_header *configuration_status);
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status);
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status);
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+    struct usb_redir_interrupt_receiving_status_header
+    *interrupt_receiving_status);
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+static void usbredir_control_packet(void *priv, uint32_t id,
+    struct usb_redir_control_packet_header *control_packet,
+    uint8_t *data, int data_len);
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+    struct usb_redir_bulk_packet_header *bulk_packet,
+    uint8_t *data, int data_len);
+static void usbredir_iso_packet(void *priv, uint32_t id,
+    struct usb_redir_iso_packet_header *iso_packet,
+    uint8_t *data, int data_len);
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_header,
+    uint8_t *data, int data_len);
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+                                       int status, int actual_len);
+
+#define VERSION "qemu usb-redir guest " QEMU_VERSION
+
+/*
+ * Logging stuff
+ */
+
+#define ERROR(...) \
+    do { \
+        if (dev->debug >= usbredirparser_error) { \
+            error_report("usb-redir error: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define WARNING(...) \
+    do { \
+        if (dev->debug >= usbredirparser_warning) { \
+            error_report("usb-redir warning: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define INFO(...) \
+    do { \
+        if (dev->debug >= usbredirparser_info) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define DPRINTF(...) \
+    do { \
+        if (dev->debug >= usbredirparser_debug) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+#define DPRINTF2(...) \
+    do { \
+        if (dev->debug >= usbredirparser_debug_data) { \
+            error_report("usb-redir: " __VA_ARGS__); \
+        } \
+    } while (0)
+
+static void usbredir_log(void *priv, int level, const char *msg)
+{
+    USBRedirDevice *dev = priv;
+
+    if (dev->debug < level) {
+        return;
+    }
+
+    error_report("%s", msg);
+}
+
+static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
+    const uint8_t *data, int len)
+{
+    int i, j, n;
+
+    if (dev->debug < usbredirparser_debug_data) {
+        return;
+    }
+
+    for (i = 0; i < len; i += j) {
+        char buf[128];
+
+        n = sprintf(buf, "%s", desc);
+        for (j = 0; j < 8 && i + j < len; j++) {
+            n += sprintf(buf + n, " %02X", data[i + j]);
+        }
+        error_report("%s", buf);
+    }
+}
+
+/*
+ * usbredirparser io functions
+ */
+
+static int usbredir_read(void *priv, uint8_t *data, int count)
+{
+    USBRedirDevice *dev = priv;
+
+    if (dev->read_buf_size < count) {
+        count = dev->read_buf_size;
+    }
+
+    memcpy(data, dev->read_buf, count);
+
+    dev->read_buf_size -= count;
+    if (dev->read_buf_size) {
+        dev->read_buf += count;
+    } else {
+        dev->read_buf = NULL;
+    }
+
+    return count;
+}
+
+static int usbredir_write(void *priv, uint8_t *data, int count)
+{
+    USBRedirDevice *dev = priv;
+
+    if (!dev->cs->opened) {
+        return 0;
+    }
+
+    return qemu_chr_fe_write(dev->cs, data, count);
+}
+
+/*
+ * Async and buffered packets helpers
+ */
+
+static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+{
+    AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
+    aurb->dev = dev;
+    aurb->packet = p;
+    aurb->packet_id = dev->packet_id;
+    QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+    dev->packet_id++;
+
+    return aurb;
+}
+
+static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+{
+    QTAILQ_REMOVE(&dev->asyncq, aurb, next);
+    g_free(aurb);
+}
+
+static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
+{
+    AsyncURB *aurb;
+
+    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+        if (aurb->packet_id == packet_id) {
+            return aurb;
+        }
+    }
+    ERROR("could not find async urb for packet_id %u\n", packet_id);
+    return NULL;
+}
+
+static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    AsyncURB *aurb;
+
+    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+        if (p != aurb->packet) {
+            continue;
+        }
+
+        DPRINTF("async cancel id %u\n", aurb->packet_id);
+        usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
+        usbredirparser_do_write(dev->parser);
+
+        /* Mark it as dead */
+        aurb->packet = NULL;
+        break;
+    }
+}
+
+static void bufp_alloc(USBRedirDevice *dev,
+    uint8_t *data, int len, int status, uint8_t ep)
+{
+    struct buf_packet *bufp;
+
+    if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets &&
+        dev->endpoint[EP2I(ep)].bufpq_size >
+            2 * dev->endpoint[EP2I(ep)].bufpq_target_size) {
+        DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1;
+    }
+    /* Since we're interupting the stream anyways, drop enough packets to get
+       back to our target buffer size */
+    if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) {
+        if (dev->endpoint[EP2I(ep)].bufpq_size >
+                dev->endpoint[EP2I(ep)].bufpq_target_size) {
+            free(data);
+            return;
+        }
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+    }
+
+    bufp = g_malloc(sizeof(struct buf_packet));
+    bufp->data   = data;
+    bufp->len    = len;
+    bufp->status = status;
+    QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size++;
+}
+
+static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
+    uint8_t ep)
+{
+    QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+    dev->endpoint[EP2I(ep)].bufpq_size--;
+    free(bufp->data);
+    g_free(bufp);
+}
+
+static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
+{
+    struct buf_packet *buf, *buf_next;
+
+    QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
+        bufp_free(dev, buf, ep);
+    }
+}
+
+/*
+ * USBDevice callbacks
+ */
+
+static void usbredir_handle_reset(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+    DPRINTF("reset device\n");
+    usbredirparser_send_reset(dev->parser);
+    usbredirparser_do_write(dev->parser);
+}
+
+static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
+                                     uint8_t ep)
+{
+    int status, len;
+    if (!dev->endpoint[EP2I(ep)].iso_started &&
+            !dev->endpoint[EP2I(ep)].iso_error) {
+        struct usb_redir_start_iso_stream_header start_iso = {
+            .endpoint = ep,
+        };
+        int pkts_per_sec;
+
+        if (dev->dev.speed == USB_SPEED_HIGH) {
+            pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval;
+        } else {
+            pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval;
+        }
+        /* Testing has shown that we need circa 60 ms buffer */
+        dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000;
+
+        /* Aim for approx 100 interrupts / second on the client to
+           balance latency and interrupt load */
+        start_iso.pkts_per_urb = pkts_per_sec / 100;
+        if (start_iso.pkts_per_urb < 1) {
+            start_iso.pkts_per_urb = 1;
+        } else if (start_iso.pkts_per_urb > 32) {
+            start_iso.pkts_per_urb = 32;
+        }
+
+        start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
+                             start_iso.pkts_per_urb - 1) /
+                            start_iso.pkts_per_urb;
+        /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
+           as overflow buffer. Also see the usbredir protocol documentation */
+        if (!(ep & USB_DIR_IN)) {
+            start_iso.no_urbs *= 2;
+        }
+        if (start_iso.no_urbs > 16) {
+            start_iso.no_urbs = 16;
+        }
+
+        /* No id, we look at the ep when receiving a status back */
+        usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
+        usbredirparser_do_write(dev->parser);
+        DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n",
+                pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep);
+        dev->endpoint[EP2I(ep)].iso_started = 1;
+        dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
+        dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+    }
+
+    if (ep & USB_DIR_IN) {
+        struct buf_packet *isop;
+
+        if (dev->endpoint[EP2I(ep)].iso_started &&
+                !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
+            if (dev->endpoint[EP2I(ep)].bufpq_size <
+                    dev->endpoint[EP2I(ep)].bufpq_target_size) {
+                return usbredir_handle_status(dev, 0, 0);
+            }
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
+        }
+
+        isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+        if (isop == NULL) {
+            DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n",
+                    ep, dev->endpoint[EP2I(ep)].iso_error);
+            /* Re-fill the buffer */
+            dev->endpoint[EP2I(ep)].bufpq_prefilled = 0;
+            /* Check iso_error for stream errors, otherwise its an underrun */
+            status = dev->endpoint[EP2I(ep)].iso_error;
+            dev->endpoint[EP2I(ep)].iso_error = 0;
+            return status ? USB_RET_IOERROR : 0;
+        }
+        DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
+                 isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
+
+        status = isop->status;
+        if (status != usb_redir_success) {
+            bufp_free(dev, isop, ep);
+            return USB_RET_IOERROR;
+        }
+
+        len = isop->len;
+        if (len > p->iov.size) {
+            ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
+                  ep, len, (int)p->iov.size);
+            bufp_free(dev, isop, ep);
+            return USB_RET_BABBLE;
+        }
+        usb_packet_copy(p, isop->data, len);
+        bufp_free(dev, isop, ep);
+        return len;
+    } else {
+        /* If the stream was not started because of a pending error don't
+           send the packet to the usb-host */
+        if (dev->endpoint[EP2I(ep)].iso_started) {
+            struct usb_redir_iso_packet_header iso_packet = {
+                .endpoint = ep,
+                .length = p->iov.size
+            };
+            uint8_t buf[p->iov.size];
+            /* No id, we look at the ep when receiving a status back */
+            usb_packet_copy(p, buf, p->iov.size);
+            usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
+                                           buf, p->iov.size);
+            usbredirparser_do_write(dev->parser);
+        }
+        status = dev->endpoint[EP2I(ep)].iso_error;
+        dev->endpoint[EP2I(ep)].iso_error = 0;
+        DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
+                 p->iov.size);
+        return usbredir_handle_status(dev, status, p->iov.size);
+    }
+}
+
+static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
+{
+    struct usb_redir_stop_iso_stream_header stop_iso_stream = {
+        .endpoint = ep
+    };
+    if (dev->endpoint[EP2I(ep)].iso_started) {
+        usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
+        DPRINTF("iso stream stopped ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].iso_started = 0;
+    }
+    dev->endpoint[EP2I(ep)].iso_error = 0;
+    usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+                                      uint8_t ep)
+{
+    AsyncURB *aurb = async_alloc(dev, p);
+    struct usb_redir_bulk_packet_header bulk_packet;
+
+    DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
+            p->iov.size, aurb->packet_id);
+
+    bulk_packet.endpoint  = ep;
+    bulk_packet.length    = p->iov.size;
+    bulk_packet.stream_id = 0;
+    aurb->bulk_packet = bulk_packet;
+
+    if (ep & USB_DIR_IN) {
+        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+                                        &bulk_packet, NULL, 0);
+    } else {
+        uint8_t buf[p->iov.size];
+        usb_packet_copy(p, buf, p->iov.size);
+        usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
+        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+                                        &bulk_packet, buf, p->iov.size);
+    }
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+                                           USBPacket *p, uint8_t ep)
+{
+    if (ep & USB_DIR_IN) {
+        /* Input interrupt endpoint, buffered packet input */
+        struct buf_packet *intp;
+        int status, len;
+
+        if (!dev->endpoint[EP2I(ep)].interrupt_started &&
+                !dev->endpoint[EP2I(ep)].interrupt_error) {
+            struct usb_redir_start_interrupt_receiving_header start_int = {
+                .endpoint = ep,
+            };
+            /* No id, we look at the ep when receiving a status back */
+            usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
+                                                          &start_int);
+            usbredirparser_do_write(dev->parser);
+            DPRINTF("interrupt recv started ep %02X\n", ep);
+            dev->endpoint[EP2I(ep)].interrupt_started = 1;
+            /* We don't really want to drop interrupt packets ever, but
+               having some upper limit to how much we buffer is good. */
+            dev->endpoint[EP2I(ep)].bufpq_target_size = 1000;
+            dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
+        }
+
+        intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+        if (intp == NULL) {
+            DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
+            /* Check interrupt_error for stream errors */
+            status = dev->endpoint[EP2I(ep)].interrupt_error;
+            dev->endpoint[EP2I(ep)].interrupt_error = 0;
+            if (status) {
+                return usbredir_handle_status(dev, status, 0);
+            }
+            return USB_RET_NAK;
+        }
+        DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
+                intp->status, intp->len);
+
+        status = intp->status;
+        if (status != usb_redir_success) {
+            bufp_free(dev, intp, ep);
+            return usbredir_handle_status(dev, status, 0);
+        }
+
+        len = intp->len;
+        if (len > p->iov.size) {
+            ERROR("received int data is larger then packet ep %02X\n", ep);
+            bufp_free(dev, intp, ep);
+            return USB_RET_BABBLE;
+        }
+        usb_packet_copy(p, intp->data, len);
+        bufp_free(dev, intp, ep);
+        return len;
+    } else {
+        /* Output interrupt endpoint, normal async operation */
+        AsyncURB *aurb = async_alloc(dev, p);
+        struct usb_redir_interrupt_packet_header interrupt_packet;
+        uint8_t buf[p->iov.size];
+
+        DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
+                aurb->packet_id);
+
+        interrupt_packet.endpoint  = ep;
+        interrupt_packet.length    = p->iov.size;
+        aurb->interrupt_packet     = interrupt_packet;
+
+        usb_packet_copy(p, buf, p->iov.size);
+        usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
+        usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
+                                        &interrupt_packet, buf, p->iov.size);
+        usbredirparser_do_write(dev->parser);
+        return USB_RET_ASYNC;
+    }
+}
+
+static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
+    uint8_t ep)
+{
+    struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
+        .endpoint = ep
+    };
+    if (dev->endpoint[EP2I(ep)].interrupt_started) {
+        usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
+                                                     &stop_interrupt_recv);
+        DPRINTF("interrupt recv stopped ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].interrupt_started = 0;
+    }
+    dev->endpoint[EP2I(ep)].interrupt_error = 0;
+    usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    uint8_t ep;
+
+    ep = p->ep->nr;
+    if (p->pid == USB_TOKEN_IN) {
+        ep |= USB_DIR_IN;
+    }
+
+    switch (dev->endpoint[EP2I(ep)].type) {
+    case USB_ENDPOINT_XFER_CONTROL:
+        ERROR("handle_data called for control transfer on ep %02X\n", ep);
+        return USB_RET_NAK;
+    case USB_ENDPOINT_XFER_ISOC:
+        return usbredir_handle_iso_data(dev, p, ep);
+    case USB_ENDPOINT_XFER_BULK:
+        return usbredir_handle_bulk_data(dev, p, ep);
+    case USB_ENDPOINT_XFER_INT:
+        return usbredir_handle_interrupt_data(dev, p, ep);
+    default:
+        ERROR("handle_data ep %02X has unknown type %d\n", ep,
+              dev->endpoint[EP2I(ep)].type);
+        return USB_RET_NAK;
+    }
+}
+
+static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+                                int config)
+{
+    struct usb_redir_set_configuration_header set_config;
+    AsyncURB *aurb = async_alloc(dev, p);
+    int i;
+
+    DPRINTF("set config %d id %u\n", config, aurb->packet_id);
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        switch (dev->endpoint[i].type) {
+        case USB_ENDPOINT_XFER_ISOC:
+            usbredir_stop_iso_stream(dev, I2EP(i));
+            break;
+        case USB_ENDPOINT_XFER_INT:
+            if (i & 0x10) {
+                usbredir_stop_interrupt_receiving(dev, I2EP(i));
+            }
+            break;
+        }
+        usbredir_free_bufpq(dev, I2EP(i));
+    }
+
+    set_config.configuration = config;
+    usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
+                                          &set_config);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+{
+    AsyncURB *aurb = async_alloc(dev, p);
+
+    DPRINTF("get config id %u\n", aurb->packet_id);
+
+    aurb->get = 1;
+    usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+                                   int interface, int alt)
+{
+    struct usb_redir_set_alt_setting_header set_alt;
+    AsyncURB *aurb = async_alloc(dev, p);
+    int i;
+
+    DPRINTF("set interface %d alt %d id %u\n", interface, alt,
+            aurb->packet_id);
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        if (dev->endpoint[i].interface == interface) {
+            switch (dev->endpoint[i].type) {
+            case USB_ENDPOINT_XFER_ISOC:
+                usbredir_stop_iso_stream(dev, I2EP(i));
+                break;
+            case USB_ENDPOINT_XFER_INT:
+                if (i & 0x10) {
+                    usbredir_stop_interrupt_receiving(dev, I2EP(i));
+                }
+                break;
+            }
+            usbredir_free_bufpq(dev, I2EP(i));
+        }
+    }
+
+    set_alt.interface = interface;
+    set_alt.alt = alt;
+    usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
+                                        &set_alt);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+                                   int interface)
+{
+    struct usb_redir_get_alt_setting_header get_alt;
+    AsyncURB *aurb = async_alloc(dev, p);
+
+    DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+
+    get_alt.interface = interface;
+    aurb->get = 1;
+    usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+                                        &get_alt);
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+        int request, int value, int index, int length, uint8_t *data)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    struct usb_redir_control_packet_header control_packet;
+    AsyncURB *aurb;
+
+    /* Special cases for certain standard device requests */
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        DPRINTF("set address %d\n", value);
+        dev->dev.addr = value;
+        return 0;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        return usbredir_set_config(dev, p, value & 0xff);
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        return usbredir_get_config(dev, p);
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        return usbredir_set_interface(dev, p, index, value);
+    case InterfaceRequest | USB_REQ_GET_INTERFACE:
+        return usbredir_get_interface(dev, p, index);
+    }
+
+    /* "Normal" ctrl requests */
+    aurb = async_alloc(dev, p);
+
+    /* Note request is (bRequestType << 8) | bRequest */
+    DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
+            request >> 8, request & 0xff, value, index, length,
+            aurb->packet_id);
+
+    control_packet.request     = request & 0xFF;
+    control_packet.requesttype = request >> 8;
+    control_packet.endpoint    = control_packet.requesttype & USB_DIR_IN;
+    control_packet.value       = value;
+    control_packet.index       = index;
+    control_packet.length      = length;
+    aurb->control_packet       = control_packet;
+
+    if (control_packet.requesttype & USB_DIR_IN) {
+        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+                                           &control_packet, NULL, 0);
+    } else {
+        usbredir_log_data(dev, "ctrl data out:", data, length);
+        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+                                           &control_packet, data, length);
+    }
+    usbredirparser_do_write(dev->parser);
+    return USB_RET_ASYNC;
+}
+
+/*
+ * Close events can be triggered by usbredirparser_do_write which gets called
+ * from within the USBDevice data / control packet callbacks and doing a
+ * usb_detach from within these callbacks is not a good idea.
+ *
+ * So we use a bh handler to take care of close events. We also handle
+ * open events from this callback to make sure that a close directly followed
+ * by an open gets handled in the right order.
+ */
+static void usbredir_open_close_bh(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+
+    usbredir_device_disconnect(dev);
+
+    if (dev->parser) {
+        usbredirparser_destroy(dev->parser);
+        dev->parser = NULL;
+    }
+
+    if (dev->cs->opened) {
+        dev->parser = qemu_oom_check(usbredirparser_create());
+        dev->parser->priv = dev;
+        dev->parser->log_func = usbredir_log;
+        dev->parser->read_func = usbredir_read;
+        dev->parser->write_func = usbredir_write;
+        dev->parser->hello_func = usbredir_hello;
+        dev->parser->device_connect_func = usbredir_device_connect;
+        dev->parser->device_disconnect_func = usbredir_device_disconnect;
+        dev->parser->interface_info_func = usbredir_interface_info;
+        dev->parser->ep_info_func = usbredir_ep_info;
+        dev->parser->configuration_status_func = usbredir_configuration_status;
+        dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+        dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+        dev->parser->interrupt_receiving_status_func =
+            usbredir_interrupt_receiving_status;
+        dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+        dev->parser->control_packet_func = usbredir_control_packet;
+        dev->parser->bulk_packet_func = usbredir_bulk_packet;
+        dev->parser->iso_packet_func = usbredir_iso_packet;
+        dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+        dev->read_buf = NULL;
+        dev->read_buf_size = 0;
+
+        usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+        usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+        usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
+        usbredirparser_do_write(dev->parser);
+    }
+}
+
+static void usbredir_do_attach(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+
+    usb_device_attach(&dev->dev);
+}
+
+/*
+ * chardev callbacks
+ */
+
+static int usbredir_chardev_can_read(void *opaque)
+{
+    USBRedirDevice *dev = opaque;
+
+    if (dev->parser) {
+        /* usbredir_parser_do_read will consume *all* data we give it */
+        return 1024 * 1024;
+    } else {
+        /* usbredir_open_close_bh hasn't handled the open event yet */
+        return 0;
+    }
+}
+
+static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
+{
+    USBRedirDevice *dev = opaque;
+
+    /* No recursion allowed! */
+    assert(dev->read_buf == NULL);
+
+    dev->read_buf = buf;
+    dev->read_buf_size = size;
+
+    usbredirparser_do_read(dev->parser);
+    /* Send any acks, etc. which may be queued now */
+    usbredirparser_do_write(dev->parser);
+}
+
+static void usbredir_chardev_event(void *opaque, int event)
+{
+    USBRedirDevice *dev = opaque;
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+    case CHR_EVENT_CLOSED:
+        qemu_bh_schedule(dev->open_close_bh);
+        break;
+    }
+}
+
+/*
+ * init + destroy
+ */
+
+static int usbredir_initfn(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+    int i;
+
+    if (dev->cs == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "chardev");
+        return -1;
+    }
+
+    if (dev->filter_str) {
+        i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|",
+                                           &dev->filter_rules,
+                                           &dev->filter_rules_count);
+        if (i) {
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter",
+                          "a usb device filter string");
+            return -1;
+        }
+    }
+
+    dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
+    dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+
+    QTAILQ_INIT(&dev->asyncq);
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        QTAILQ_INIT(&dev->endpoint[i].bufpq);
+    }
+
+    /* We'll do the attach once we receive the speed from the usb-host */
+    udev->auto_attach = 0;
+
+    /* Let the backend know we are ready */
+    qemu_chr_fe_open(dev->cs);
+    qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
+                          usbredir_chardev_read, usbredir_chardev_event, dev);
+
+    return 0;
+}
+
+static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+{
+    AsyncURB *aurb, *next_aurb;
+    int i;
+
+    QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
+        async_free(dev, aurb);
+    }
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        usbredir_free_bufpq(dev, I2EP(i));
+    }
+}
+
+static void usbredir_handle_destroy(USBDevice *udev)
+{
+    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+    qemu_chr_fe_close(dev->cs);
+    qemu_chr_delete(dev->cs);
+    /* Note must be done after qemu_chr_close, as that causes a close event */
+    qemu_bh_delete(dev->open_close_bh);
+
+    qemu_del_timer(dev->attach_timer);
+    qemu_free_timer(dev->attach_timer);
+
+    usbredir_cleanup_device_queues(dev);
+
+    if (dev->parser) {
+        usbredirparser_destroy(dev->parser);
+    }
+
+    free(dev->filter_rules);
+}
+
+static int usbredir_check_filter(USBRedirDevice *dev)
+{
+    if (dev->interface_info.interface_count == 0) {
+        ERROR("No interface info for device\n");
+        goto error;
+    }
+
+    if (dev->filter_rules) {
+        if (!usbredirparser_peer_has_cap(dev->parser,
+                                    usb_redir_cap_connect_device_version)) {
+            ERROR("Device filter specified and peer does not have the "
+                  "connect_device_version capability\n");
+            goto error;
+        }
+
+        if (usbredirfilter_check(
+                dev->filter_rules,
+                dev->filter_rules_count,
+                dev->device_info.device_class,
+                dev->device_info.device_subclass,
+                dev->device_info.device_protocol,
+                dev->interface_info.interface_class,
+                dev->interface_info.interface_subclass,
+                dev->interface_info.interface_protocol,
+                dev->interface_info.interface_count,
+                dev->device_info.vendor_id,
+                dev->device_info.product_id,
+                dev->device_info.device_version_bcd,
+                0) != 0) {
+            goto error;
+        }
+    }
+
+    return 0;
+
+error:
+    usbredir_device_disconnect(dev);
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+        usbredirparser_send_filter_reject(dev->parser);
+        usbredirparser_do_write(dev->parser);
+    }
+    return -1;
+}
+
+/*
+ * usbredirparser packet complete callbacks
+ */
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+                                       int status, int actual_len)
+{
+    switch (status) {
+    case usb_redir_success:
+        return actual_len;
+    case usb_redir_stall:
+        return USB_RET_STALL;
+    case usb_redir_cancelled:
+        WARNING("returning cancelled packet to HC?\n");
+        return USB_RET_NAK;
+    case usb_redir_inval:
+        WARNING("got invalid param error from usb-host?\n");
+        return USB_RET_NAK;
+    case usb_redir_ioerror:
+    case usb_redir_timeout:
+    default:
+        return USB_RET_IOERROR;
+    }
+}
+
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
+{
+    USBRedirDevice *dev = priv;
+
+    /* Try to send the filter info now that we've the usb-host's caps */
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
+            dev->filter_rules) {
+        usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
+                                          dev->filter_rules_count);
+        usbredirparser_do_write(dev->parser);
+    }
+}
+
+static void usbredir_device_connect(void *priv,
+    struct usb_redir_device_connect_header *device_connect)
+{
+    USBRedirDevice *dev = priv;
+    const char *speed;
+
+    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
+        ERROR("Received device connect while already connected\n");
+        return;
+    }
+
+    switch (device_connect->speed) {
+    case usb_redir_speed_low:
+        speed = "low speed";
+        dev->dev.speed = USB_SPEED_LOW;
+        break;
+    case usb_redir_speed_full:
+        speed = "full speed";
+        dev->dev.speed = USB_SPEED_FULL;
+        break;
+    case usb_redir_speed_high:
+        speed = "high speed";
+        dev->dev.speed = USB_SPEED_HIGH;
+        break;
+    case usb_redir_speed_super:
+        speed = "super speed";
+        dev->dev.speed = USB_SPEED_SUPER;
+        break;
+    default:
+        speed = "unknown speed";
+        dev->dev.speed = USB_SPEED_FULL;
+    }
+
+    if (usbredirparser_peer_has_cap(dev->parser,
+                                    usb_redir_cap_connect_device_version)) {
+        INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
+             speed, device_connect->vendor_id, device_connect->product_id,
+             ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
+             ((device_connect->device_version_bcd & 0x0f00) >>  8),
+             ((device_connect->device_version_bcd & 0x00f0) >>  4) * 10 +
+             ((device_connect->device_version_bcd & 0x000f) >>  0),
+             device_connect->device_class);
+    } else {
+        INFO("attaching %s device %04x:%04x class %02x\n", speed,
+             device_connect->vendor_id, device_connect->product_id,
+             device_connect->device_class);
+    }
+
+    dev->dev.speedmask = (1 << dev->dev.speed);
+    dev->device_info = *device_connect;
+
+    if (usbredir_check_filter(dev)) {
+        WARNING("Device %04x:%04x rejected by device filter, not attaching\n",
+                device_connect->vendor_id, device_connect->product_id);
+        return;
+    }
+
+    qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
+}
+
+static void usbredir_device_disconnect(void *priv)
+{
+    USBRedirDevice *dev = priv;
+    int i;
+
+    /* Stop any pending attaches */
+    qemu_del_timer(dev->attach_timer);
+
+    if (dev->dev.attached) {
+        usb_device_detach(&dev->dev);
+        /*
+         * Delay next usb device attach to give the guest a chance to see
+         * see the detach / attach in case of quick close / open succession
+         */
+        dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200;
+    }
+
+    /* Reset state so that the next dev connected starts with a clean slate */
+    usbredir_cleanup_device_queues(dev);
+    memset(dev->endpoint, 0, sizeof(dev->endpoint));
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        QTAILQ_INIT(&dev->endpoint[i].bufpq);
+    }
+    usb_ep_init(&dev->dev);
+    dev->interface_info.interface_count = 0;
+}
+
+static void usbredir_interface_info(void *priv,
+    struct usb_redir_interface_info_header *interface_info)
+{
+    USBRedirDevice *dev = priv;
+
+    dev->interface_info = *interface_info;
+
+    /*
+     * If we receive interface info after the device has already been
+     * connected (ie on a set_config), re-check the filter.
+     */
+    if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
+        if (usbredir_check_filter(dev)) {
+            ERROR("Device no longer matches filter after interface info "
+                  "change, disconnecting!\n");
+        }
+    }
+}
+
+static void usbredir_ep_info(void *priv,
+    struct usb_redir_ep_info_header *ep_info)
+{
+    USBRedirDevice *dev = priv;
+    struct USBEndpoint *usb_ep;
+    int i;
+
+    for (i = 0; i < MAX_ENDPOINTS; i++) {
+        dev->endpoint[i].type = ep_info->type[i];
+        dev->endpoint[i].interval = ep_info->interval[i];
+        dev->endpoint[i].interface = ep_info->interface[i];
+        switch (dev->endpoint[i].type) {
+        case usb_redir_type_invalid:
+            break;
+        case usb_redir_type_iso:
+        case usb_redir_type_interrupt:
+            if (dev->endpoint[i].interval == 0) {
+                ERROR("Received 0 interval for isoc or irq endpoint\n");
+                usbredir_device_disconnect(dev);
+            }
+            /* Fall through */
+        case usb_redir_type_control:
+        case usb_redir_type_bulk:
+            DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
+                    dev->endpoint[i].type, dev->endpoint[i].interface);
+            break;
+        default:
+            ERROR("Received invalid endpoint type\n");
+            usbredir_device_disconnect(dev);
+            return;
+        }
+        usb_ep = usb_ep_get(&dev->dev,
+                            (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
+                            i & 0x0f);
+        usb_ep->type = dev->endpoint[i].type;
+        usb_ep->ifnum = dev->endpoint[i].interface;
+    }
+}
+
+static void usbredir_configuration_status(void *priv, uint32_t id,
+    struct usb_redir_configuration_status_header *config_status)
+{
+    USBRedirDevice *dev = priv;
+    AsyncURB *aurb;
+    int len = 0;
+
+    DPRINTF("set config status %d config %d id %u\n", config_status->status,
+            config_status->configuration, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        return;
+    }
+    if (aurb->packet) {
+        if (aurb->get) {
+            dev->dev.data_buf[0] = config_status->configuration;
+            len = 1;
+        }
+        aurb->packet->result =
+            usbredir_handle_status(dev, config_status->status, len);
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+}
+
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+    USBRedirDevice *dev = priv;
+    AsyncURB *aurb;
+    int len = 0;
+
+    DPRINTF("alt status %d intf %d alt %d id: %u\n",
+            alt_setting_status->status,
+            alt_setting_status->interface,
+            alt_setting_status->alt, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        return;
+    }
+    if (aurb->packet) {
+        if (aurb->get) {
+            dev->dev.data_buf[0] = alt_setting_status->alt;
+            len = 1;
+        }
+        aurb->packet->result =
+            usbredir_handle_status(dev, alt_setting_status->status, len);
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+}
+
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = iso_stream_status->endpoint;
+
+    DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
+            ep, id);
+
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
+        return;
+    }
+
+    dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
+    if (iso_stream_status->status == usb_redir_stall) {
+        DPRINTF("iso stream stopped by peer ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].iso_started = 0;
+    }
+}
+
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+    struct usb_redir_interrupt_receiving_status_header
+    *interrupt_receiving_status)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = interrupt_receiving_status->endpoint;
+
+    DPRINTF("interrupt recv status %d ep %02X id %u\n",
+            interrupt_receiving_status->status, ep, id);
+
+    if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
+        return;
+    }
+
+    dev->endpoint[EP2I(ep)].interrupt_error =
+        interrupt_receiving_status->status;
+    if (interrupt_receiving_status->status == usb_redir_stall) {
+        DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
+        dev->endpoint[EP2I(ep)].interrupt_started = 0;
+    }
+}
+
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+}
+
+static void usbredir_control_packet(void *priv, uint32_t id,
+    struct usb_redir_control_packet_header *control_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    int len = control_packet->length;
+    AsyncURB *aurb;
+
+    DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+            len, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        free(data);
+        return;
+    }
+
+    aurb->control_packet.status = control_packet->status;
+    aurb->control_packet.length = control_packet->length;
+    if (memcmp(&aurb->control_packet, control_packet,
+               sizeof(*control_packet))) {
+        ERROR("return control packet mismatch, please report this!\n");
+        len = USB_RET_NAK;
+    }
+
+    if (aurb->packet) {
+        len = usbredir_handle_status(dev, control_packet->status, len);
+        if (len > 0) {
+            usbredir_log_data(dev, "ctrl data in:", data, data_len);
+            if (data_len <= sizeof(dev->dev.data_buf)) {
+                memcpy(dev->dev.data_buf, data, data_len);
+            } else {
+                ERROR("ctrl buffer too small (%d > %zu)\n",
+                      data_len, sizeof(dev->dev.data_buf));
+                len = USB_RET_STALL;
+            }
+        }
+        aurb->packet->result = len;
+        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+    free(data);
+}
+
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+    struct usb_redir_bulk_packet_header *bulk_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = bulk_packet->endpoint;
+    int len = bulk_packet->length;
+    AsyncURB *aurb;
+
+    DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+            ep, len, id);
+
+    aurb = async_find(dev, id);
+    if (!aurb) {
+        free(data);
+        return;
+    }
+
+    if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
+            aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
+        ERROR("return bulk packet mismatch, please report this!\n");
+        len = USB_RET_NAK;
+    }
+
+    if (aurb->packet) {
+        len = usbredir_handle_status(dev, bulk_packet->status, len);
+        if (len > 0) {
+            usbredir_log_data(dev, "bulk data in:", data, data_len);
+            if (data_len <= aurb->packet->iov.size) {
+                usb_packet_copy(aurb->packet, data, data_len);
+            } else {
+                ERROR("bulk buffer too small (%d > %zd)\n", data_len,
+                      aurb->packet->iov.size);
+                len = USB_RET_STALL;
+            }
+        }
+        aurb->packet->result = len;
+        usb_packet_complete(&dev->dev, aurb->packet);
+    }
+    async_free(dev, aurb);
+    free(data);
+}
+
+static void usbredir_iso_packet(void *priv, uint32_t id,
+    struct usb_redir_iso_packet_header *iso_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = iso_packet->endpoint;
+
+    DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
+             data_len, id);
+
+    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
+        ERROR("received iso packet for non iso endpoint %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    if (dev->endpoint[EP2I(ep)].iso_started == 0) {
+        DPRINTF("received iso packet for non started stream ep %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    /* bufp_alloc also adds the packet to the ep queue */
+    bufp_alloc(dev, data, data_len, iso_packet->status, ep);
+}
+
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_packet,
+    uint8_t *data, int data_len)
+{
+    USBRedirDevice *dev = priv;
+    uint8_t ep = interrupt_packet->endpoint;
+
+    DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
+            interrupt_packet->status, ep, data_len, id);
+
+    if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
+        ERROR("received int packet for non interrupt endpoint %02X\n", ep);
+        free(data);
+        return;
+    }
+
+    if (ep & USB_DIR_IN) {
+        if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
+            DPRINTF("received int packet while not started ep %02X\n", ep);
+            free(data);
+            return;
+        }
+
+        /* bufp_alloc also adds the packet to the ep queue */
+        bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+    } else {
+        int len = interrupt_packet->length;
+
+        AsyncURB *aurb = async_find(dev, id);
+        if (!aurb) {
+            return;
+        }
+
+        if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
+            ERROR("return int packet mismatch, please report this!\n");
+            len = USB_RET_NAK;
+        }
+
+        if (aurb->packet) {
+            aurb->packet->result = usbredir_handle_status(dev,
+                                               interrupt_packet->status, len);
+            usb_packet_complete(&dev->dev, aurb->packet);
+        }
+        async_free(dev, aurb);
+    }
+}
+
+static Property usbredir_properties[] = {
+    DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
+    DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
+    DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usbredir_class_initfn(ObjectClass *klass, void *data)
+{
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    uc->init           = usbredir_initfn;
+    uc->product_desc   = "USB Redirection Device";
+    uc->handle_destroy = usbredir_handle_destroy;
+    uc->cancel_packet  = usbredir_cancel_packet;
+    uc->handle_reset   = usbredir_handle_reset;
+    uc->handle_data    = usbredir_handle_data;
+    uc->handle_control = usbredir_handle_control;
+    dc->props          = usbredir_properties;
+}
+
+static TypeInfo usbredir_dev_info = {
+    .name          = "usb-redir",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBRedirDevice),
+    .class_init    = usbredir_class_initfn,
+};
+
+static void usbredir_register_types(void)
+{
+    type_register_static(&usbredir_dev_info);
+}
+
+type_init(usbredir_register_types)
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index b9102f4a54..25afb1eb31 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -13,7 +13,6 @@
 #include "net.h"
 #include "sysemu.h"
 #include "pci.h"
-#include "usb-ohci.h"
 #include "boards.h"
 #include "blockdev.h"
 #include "exec-memory.h"
@@ -168,7 +167,7 @@ static void versatile_init(ram_addr_t ram_size,
                      const char *initrd_filename, const char *cpu_model,
                      int board_id)
 {
-    CPUState *env;
+    CPUARMState *env;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     qemu_irq *cpu_pic;
@@ -240,7 +239,7 @@ static void versatile_init(ram_addr_t ram_size,
         }
     }
     if (usb_enabled) {
-        usb_ohci_init_pci(pci_bus, -1);
+        pci_create_simple(pci_bus, -1, "pci-ohci");
     }
     n = drive_get_max_bus(IF_SCSI);
     while (n >= 0) {
diff --git a/hw/vexpress.c b/hw/vexpress.c
index b9aafec4cc..18d87ac378 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -159,7 +159,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
                                   const char *cpu_model,
                                   qemu_irq *pic, uint32_t *proc_id)
 {
-    CPUState *env = NULL;
+    CPUARMState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *lowram = g_new(MemoryRegion, 1);
@@ -259,7 +259,7 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard,
                                    qemu_irq *pic, uint32_t *proc_id)
 {
     int n;
-    CPUState *env = NULL;
+    CPUARMState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *sram = g_new(MemoryRegion, 1);
diff --git a/hw/vga.c b/hw/vga.c
index 5994f43b75..6dc98f6c2a 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -30,6 +30,7 @@
 #include "pixel_ops.h"
 #include "qemu-timer.h"
 #include "xen.h"
+#include "trace.h"
 
 //#define DEBUG_VGA
 //#define DEBUG_VGA_MEM
@@ -2372,6 +2373,7 @@ int ppm_save(const char *filename, struct DisplaySurface *ds)
     int ret;
     char *linebuf, *pbuf;
 
+    trace_ppm_save(filename, ds);
     f = fopen(filename, "wb");
     if (!f)
         return -1;
diff --git a/hw/vhost.c b/hw/vhost.c
index 5ece659f4a..8d3ba5b608 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -436,6 +436,14 @@ static bool vhost_section(MemoryRegionSection *section)
         && memory_region_is_ram(section->mr);
 }
 
+static void vhost_begin(MemoryListener *listener)
+{
+}
+
+static void vhost_commit(MemoryListener *listener)
+{
+}
+
 static void vhost_region_add(MemoryListener *listener,
                              MemoryRegionSection *section)
 {
@@ -476,6 +484,11 @@ static void vhost_region_del(MemoryListener *listener,
     }
 }
 
+static void vhost_region_nop(MemoryListener *listener,
+                             MemoryRegionSection *section)
+{
+}
+
 static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
                                     struct vhost_virtqueue *vq,
                                     unsigned idx, bool enable_log)
@@ -720,6 +733,18 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
                               0, virtio_queue_get_desc_size(vdev, idx));
 }
 
+static void vhost_eventfd_add(MemoryListener *listener,
+                              MemoryRegionSection *section,
+                              bool match_data, uint64_t data, int fd)
+{
+}
+
+static void vhost_eventfd_del(MemoryListener *listener,
+                              MemoryRegionSection *section,
+                              bool match_data, uint64_t data, int fd)
+{
+}
+
 int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
 {
     uint64_t features;
@@ -744,13 +769,19 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
     hdev->features = features;
 
     hdev->memory_listener = (MemoryListener) {
+        .begin = vhost_begin,
+        .commit = vhost_commit,
         .region_add = vhost_region_add,
         .region_del = vhost_region_del,
+        .region_nop = vhost_region_nop,
         .log_start = vhost_log_start,
         .log_stop = vhost_log_stop,
         .log_sync = vhost_log_sync,
         .log_global_start = vhost_log_global_start,
         .log_global_stop = vhost_log_global_stop,
+        .eventfd_add = vhost_eventfd_add,
+        .eventfd_del = vhost_eventfd_del,
+        .priority = 10
     };
     hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
     hdev->n_mem_sections = 0;
@@ -759,7 +790,7 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
     hdev->log_size = 0;
     hdev->log_enabled = false;
     hdev->started = false;
-    memory_listener_register(&hdev->memory_listener);
+    memory_listener_register(&hdev->memory_listener, NULL);
     hdev->force = force;
     return 0;
 fail:
diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c
index f8d2b1be04..4a133b5d1e 100644
--- a/hw/virtex_ml507.c
+++ b/hw/virtex_ml507.c
@@ -56,7 +56,7 @@ static struct boot_info
 } boot_info;
 
 /* Create reset TLB entries for BookE, spanning the 32bit addr space.  */
-static void mmubooke_create_initial_mapping(CPUState *env,
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
                                      target_ulong va,
                                      target_phys_addr_t pa)
 {
@@ -78,12 +78,12 @@ static void mmubooke_create_initial_mapping(CPUState *env,
     tlb->PID = 0;
 }
 
-static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
+static CPUPPCState *ppc440_init_xilinx(ram_addr_t *ram_size,
                                     int do_init,
                                     const char *cpu_model,
                                     uint32_t sysclk)
 {
-    CPUState *env;
+    CPUPPCState *env;
     qemu_irq *irqs;
 
     env = cpu_init(cpu_model);
@@ -106,10 +106,10 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
 
 static void main_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUPPCState *env = opaque;
     struct boot_info *bi = env->load_info;
 
-    cpu_reset(env);
+    cpu_state_reset(env);
     /* Linux Kernel Parameters (passing device tree):
        *   r3: pointer to the fdt
        *   r4: 0
@@ -188,7 +188,7 @@ static void virtex_init(ram_addr_t ram_size,
 {
     MemoryRegion *address_space_mem = get_system_memory();
     DeviceState *dev;
-    CPUState *env;
+    CPUPPCState *env;
     target_phys_addr_t ram_base = 0;
     DriveInfo *dinfo;
     MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index e607edc915..45d54faeb5 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -613,5 +613,7 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
 
 void virtio_scsi_exit(VirtIODevice *vdev)
 {
+    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+    unregister_savevm(s->qdev, "virtio-scsi", s);
     virtio_cleanup(vdev);
 }
diff --git a/hw/vmport.c b/hw/vmport.c
index 9373be9775..a4f52ee5bb 100644
--- a/hw/vmport.c
+++ b/hw/vmport.c
@@ -57,7 +57,7 @@ void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque)
 static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
 {
     VMPortState *s = opaque;
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
     unsigned char command;
     uint32_t eax;
 
@@ -83,21 +83,21 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
 
 static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
 
     env->regs[R_EAX] = vmport_ioport_read(opaque, addr);
 }
 
 static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
     env->regs[R_EBX] = VMPORT_MAGIC;
     return 6;
 }
 
 static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
     env->regs[R_EBX] = 0x1177;
     return ram_size;
 }
@@ -105,7 +105,7 @@ static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr)
 /* vmmouse helpers */
 void vmmouse_get_data(uint32_t *data)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
 
     data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX];
     data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX];
@@ -114,7 +114,7 @@ void vmmouse_get_data(uint32_t *data)
 
 void vmmouse_set_data(const uint32_t *data)
 {
-    CPUState *env = cpu_single_env;
+    CPUX86State *env = cpu_single_env;
 
     env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1];
     env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3];
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
index 7985d11d5a..7eee770eea 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -36,7 +36,7 @@ static void xen_init_pv(ram_addr_t ram_size,
 			const char *initrd_filename,
 			const char *cpu_model)
 {
-    CPUState *env;
+    CPUX86State *env;
     DriveInfo *dinfo;
     int i;
 
diff --git a/hw/xics.c b/hw/xics.c
index 1c5eaa4135..668a0d6484 100644
--- a/hw/xics.c
+++ b/hw/xics.c
@@ -132,9 +132,9 @@ static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr)
 {
     struct icp_server_state *ss = icp->ss + server;
 
-    ics_eoi(icp->ics, xirr & XISR_MASK);
     /* Send EOI -> ICS */
     ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
+    ics_eoi(icp->ics, xirr & XISR_MASK);
     if (!XISR(ss)) {
         icp_resend(icp, server);
     }
@@ -165,8 +165,9 @@ struct ics_irq_state {
     int server;
     uint8_t priority;
     uint8_t saved_priority;
-    /* int pending:1; */
-    /* int presented:1; */
+    enum xics_irq_type type;
+    int asserted:1;
+    int sent:1;
     int rejected:1;
     int masked_pending:1;
 };
@@ -185,9 +186,32 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
         && (nr < (ics->offset + ics->nr_irqs));
 }
 
-static void ics_set_irq_msi(void *opaque, int srcno, int val)
+static void resend_msi(struct ics_state *ics, int srcno)
+{
+    struct ics_irq_state *irq = ics->irqs + srcno;
+
+    /* FIXME: filter by server#? */
+    if (irq->rejected) {
+        irq->rejected = 0;
+        if (irq->priority != 0xff) {
+            icp_irq(ics->icp, irq->server, srcno + ics->offset,
+                    irq->priority);
+        }
+    }
+}
+
+static void resend_lsi(struct ics_state *ics, int srcno)
+{
+    struct ics_irq_state *irq = ics->irqs + srcno;
+
+    if ((irq->priority != 0xff) && irq->asserted && !irq->sent) {
+        irq->sent = 1;
+        icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+    }
+}
+
+static void set_irq_msi(struct ics_state *ics, int srcno, int val)
 {
-    struct ics_state *ics = (struct ics_state *)opaque;
     struct ics_irq_state *irq = ics->irqs + srcno;
 
     if (val) {
@@ -200,75 +224,112 @@ static void ics_set_irq_msi(void *opaque, int srcno, int val)
     }
 }
 
-static void ics_reject_msi(struct ics_state *ics, int nr)
+static void set_irq_lsi(struct ics_state *ics, int srcno, int val)
 {
-    struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
+    struct ics_irq_state *irq = ics->irqs + srcno;
 
-    irq->rejected = 1;
+    irq->asserted = val;
+    resend_lsi(ics, srcno);
 }
 
-static void ics_resend_msi(struct ics_state *ics)
+static void ics_set_irq(void *opaque, int srcno, int val)
 {
-    int i;
+    struct ics_state *ics = (struct ics_state *)opaque;
+    struct ics_irq_state *irq = ics->irqs + srcno;
 
-    for (i = 0; i < ics->nr_irqs; i++) {
-        struct ics_irq_state *irq = ics->irqs + i;
+    if (irq->type == XICS_LSI) {
+        set_irq_lsi(ics, srcno, val);
+    } else {
+        set_irq_msi(ics, srcno, val);
+    }
+}
 
-        /* FIXME: filter by server#? */
-        if (irq->rejected) {
-            irq->rejected = 0;
-            if (irq->priority != 0xff) {
-                icp_irq(ics->icp, irq->server, i + ics->offset, irq->priority);
-            }
-        }
+static void write_xive_msi(struct ics_state *ics, int srcno)
+{
+    struct ics_irq_state *irq = ics->irqs + srcno;
+
+    if (!irq->masked_pending || (irq->priority == 0xff)) {
+        return;
     }
+
+    irq->masked_pending = 0;
+    icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
 }
 
-static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
-                               uint8_t priority)
+static void write_xive_lsi(struct ics_state *ics, int srcno)
 {
-    struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
+    resend_lsi(ics, srcno);
+}
+
+static void ics_write_xive(struct ics_state *ics, int nr, int server,
+                           uint8_t priority)
+{
+    int srcno = nr - ics->offset;
+    struct ics_irq_state *irq = ics->irqs + srcno;
 
     irq->server = server;
     irq->priority = priority;
 
-    if (!irq->masked_pending || (priority == 0xff)) {
-        return;
+    if (irq->type == XICS_LSI) {
+        write_xive_lsi(ics, srcno);
+    } else {
+        write_xive_msi(ics, srcno);
     }
-
-    irq->masked_pending = 0;
-    icp_irq(ics->icp, server, nr, priority);
 }
 
 static void ics_reject(struct ics_state *ics, int nr)
 {
-    ics_reject_msi(ics, nr);
+    struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
+
+    irq->rejected = 1; /* Irrelevant but harmless for LSI */
+    irq->sent = 0; /* Irrelevant but harmless for MSI */
 }
 
 static void ics_resend(struct ics_state *ics)
 {
-    ics_resend_msi(ics);
+    int i;
+
+    for (i = 0; i < ics->nr_irqs; i++) {
+        struct ics_irq_state *irq = ics->irqs + i;
+
+        /* FIXME: filter by server#? */
+        if (irq->type == XICS_LSI) {
+            resend_lsi(ics, i);
+        } else {
+            resend_msi(ics, i);
+        }
+    }
 }
 
 static void ics_eoi(struct ics_state *ics, int nr)
 {
+    int srcno = nr - ics->offset;
+    struct ics_irq_state *irq = ics->irqs + srcno;
+
+    if (irq->type == XICS_LSI) {
+        irq->sent = 0;
+    }
 }
 
 /*
  * Exported functions
  */
 
-qemu_irq xics_find_qirq(struct icp_state *icp, int irq)
+qemu_irq xics_assign_irq(struct icp_state *icp, int irq,
+                         enum xics_irq_type type)
 {
     if ((irq < icp->ics->offset)
         || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) {
         return NULL;
     }
 
+    assert((type == XICS_MSI) || (type == XICS_LSI));
+
+    icp->ics->irqs[irq - icp->ics->offset].type = type;
     return icp->ics->qirqs[irq - icp->ics->offset];
 }
 
-static target_ulong h_cppr(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_cppr(CPUPPCState *env, sPAPREnvironment *spapr,
                            target_ulong opcode, target_ulong *args)
 {
     target_ulong cppr = args[0];
@@ -277,7 +338,7 @@ static target_ulong h_cppr(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_ipi(CPUPPCState *env, sPAPREnvironment *spapr,
                           target_ulong opcode, target_ulong *args)
 {
     target_ulong server = args[0];
@@ -292,7 +353,7 @@ static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr,
 
 }
 
-static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_xirr(CPUPPCState *env, sPAPREnvironment *spapr,
                            target_ulong opcode, target_ulong *args)
 {
     uint32_t xirr = icp_accept(spapr->icp->ss + env->cpu_index);
@@ -301,7 +362,7 @@ static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-static target_ulong h_eoi(CPUState *env, sPAPREnvironment *spapr,
+static target_ulong h_eoi(CPUPPCState *env, sPAPREnvironment *spapr,
                           target_ulong opcode, target_ulong *args)
 {
     target_ulong xirr = args[0];
@@ -332,7 +393,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
         return;
     }
 
-    ics_write_xive_msi(ics, nr, server, priority);
+    ics_write_xive(ics, nr, server, priority);
 
     rtas_st(rets, 0, 0); /* Success */
 }
@@ -424,7 +485,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
 
 struct icp_state *xics_system_init(int nr_irqs)
 {
-    CPUState *env;
+    CPUPPCState *env;
     int max_server_num;
     int i;
     struct icp_state *icp;
@@ -477,7 +538,7 @@ struct icp_state *xics_system_init(int nr_irqs)
         ics->irqs[i].saved_priority = 0xff;
     }
 
-    ics->qirqs = qemu_allocate_irqs(ics_set_irq_msi, ics, nr_irqs);
+    ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs);
 
     spapr_register_hypercall(H_CPPR, h_cppr);
     spapr_register_hypercall(H_IPI, h_ipi);
diff --git a/hw/xics.h b/hw/xics.h
index 83c1182598..208015939c 100644
--- a/hw/xics.h
+++ b/hw/xics.h
@@ -31,7 +31,13 @@
 
 struct icp_state;
 
-qemu_irq xics_find_qirq(struct icp_state *icp, int irq);
+enum xics_irq_type {
+    XICS_MSI,        /* Message-signalled (edge) interrupt */
+    XICS_LSI,        /* Level-signalled interrupt */
+};
+
+qemu_irq xics_assign_irq(struct icp_state *icp, int irq,
+                         enum xics_irq_type type);
 
 struct icp_state *xics_system_init(int nr_irqs);
 
diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c
new file mode 100644
index 0000000000..7290c64a4c
--- /dev/null
+++ b/hw/xilinx_zynq.c
@@ -0,0 +1,157 @@
+/*
+ * Xilinx Zynq Baseboard System emulation.
+ *
+ * Copyright (c) 2010 Xilinx.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com)
+ * Copyright (c) 2012 Petalogix Pty Ltd.
+ * Written by Haibing Ma
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "net.h"
+#include "exec-memory.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "flash.h"
+#include "blockdev.h"
+#include "loader.h"
+
+#define FLASH_SIZE (64 * 1024 * 1024)
+#define FLASH_SECTOR_SIZE (128 * 1024)
+
+#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
+
+static struct arm_boot_info zynq_binfo = {};
+
+static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "cadence_gem");
+    dev = qdev_create(NULL, "cadence_gem");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+static void zynq_init(ram_addr_t ram_size, const char *boot_device,
+                        const char *kernel_filename, const char *kernel_cmdline,
+                        const char *initrd_filename, const char *cpu_model)
+{
+    CPUARMState *env = NULL;
+    MemoryRegion *address_space_mem = get_system_memory();
+    MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
+    MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
+    DeviceState *dev;
+    SysBusDevice *busdev;
+    qemu_irq *irqp;
+    qemu_irq pic[64];
+    NICInfo *nd;
+    int n;
+    qemu_irq cpu_irq;
+
+    if (!cpu_model) {
+        cpu_model = "cortex-a9";
+    }
+
+    env = cpu_init(cpu_model);
+    if (!env) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+    irqp = arm_pic_init_cpu(env);
+    cpu_irq = irqp[ARM_PIC_CPU_IRQ];
+
+    /* max 2GB ram */
+    if (ram_size > 0x80000000) {
+        ram_size = 0x80000000;
+    }
+
+    /* DDR remapped to address zero.  */
+    memory_region_init_ram(ext_ram, "zynq.ext_ram", ram_size);
+    vmstate_register_ram_global(ext_ram);
+    memory_region_add_subregion(address_space_mem, 0, ext_ram);
+
+    /* 256K of on-chip memory */
+    memory_region_init_ram(ocm_ram, "zynq.ocm_ram", 256 << 10);
+    vmstate_register_ram_global(ocm_ram);
+    memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram);
+
+    DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
+
+    /* AMD */
+    pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE,
+                          dinfo ? dinfo->bdrv : NULL, FLASH_SECTOR_SIZE,
+                          FLASH_SIZE/FLASH_SECTOR_SIZE, 1,
+                          1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa,
+                              0);
+
+    dev = qdev_create(NULL, "xilinx,zynq_slcr");
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xF8000000);
+
+    dev = qdev_create(NULL, "a9mpcore_priv");
+    qdev_prop_set_uint32(dev, "num-cpu", 1);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    sysbus_mmio_map(busdev, 0, 0xF8F00000);
+    sysbus_connect_irq(busdev, 0, cpu_irq);
+
+    for (n = 0; n < 64; n++) {
+        pic[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
+    sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
+
+    sysbus_create_varargs("cadence_ttc", 0xF8001000,
+            pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
+    sysbus_create_varargs("cadence_ttc", 0xF8002000,
+            pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL);
+
+    for (n = 0; n < nb_nics; n++) {
+        nd = &nd_table[n];
+        if (n == 0) {
+            gem_init(nd, 0xE000B000, pic[54-IRQ_OFFSET]);
+        } else if (n == 1) {
+            gem_init(nd, 0xE000C000, pic[77-IRQ_OFFSET]);
+        }
+    }
+
+    zynq_binfo.ram_size = ram_size;
+    zynq_binfo.kernel_filename = kernel_filename;
+    zynq_binfo.kernel_cmdline = kernel_cmdline;
+    zynq_binfo.initrd_filename = initrd_filename;
+    zynq_binfo.nb_cpus = 1;
+    zynq_binfo.board_id = 0xd32;
+    zynq_binfo.loader_start = 0;
+    arm_load_kernel(first_cpu, &zynq_binfo);
+}
+
+static QEMUMachine zynq_machine = {
+    .name = "xilinx-zynq-a9",
+    .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
+    .init = zynq_init,
+    .use_scsi = 1,
+    .max_cpus = 1,
+    .no_sdcard = 1
+};
+
+static void zynq_machine_init(void)
+{
+    qemu_register_machine(&zynq_machine);
+}
+
+machine_init(zynq_machine_init);
diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c
index 26112c3eb0..afdef494b2 100644
--- a/hw/xtensa_lx60.c
+++ b/hw/xtensa_lx60.c
@@ -146,9 +146,11 @@ static uint64_t translate_phys_addr(void *env, uint64_t addr)
     return cpu_get_phys_page_debug(env, addr);
 }
 
-static void lx60_reset(void *env)
+static void lx60_reset(void *opaque)
 {
-    cpu_reset(env);
+    CPUXtensaState *env = opaque;
+
+    cpu_state_reset(env);
 }
 
 static void lx_init(const LxBoardDesc *board,
@@ -162,7 +164,7 @@ static void lx_init(const LxBoardDesc *board,
     int be = 0;
 #endif
     MemoryRegion *system_memory = get_system_memory();
-    CPUState *env = NULL;
+    CPUXtensaState *env = NULL;
     MemoryRegion *ram, *rom, *system_io;
     DriveInfo *dinfo;
     pflash_t *flash = NULL;
@@ -183,7 +185,7 @@ static void lx_init(const LxBoardDesc *board,
         /* Need MMU initialized prior to ELF loading,
          * so that ELF gets loaded into virtual addresses
          */
-        cpu_reset(env);
+        cpu_state_reset(env);
     }
 
     ram = g_malloc(sizeof(*ram));
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
index 71d5fc89d4..653ded6820 100644
--- a/hw/xtensa_pic.c
+++ b/hw/xtensa_pic.c
@@ -29,7 +29,7 @@
 #include "qemu-log.h"
 #include "qemu-timer.h"
 
-void xtensa_advance_ccount(CPUState *env, uint32_t d)
+void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d)
 {
     uint32_t old_ccount = env->sregs[CCOUNT];
 
@@ -45,7 +45,7 @@ void xtensa_advance_ccount(CPUState *env, uint32_t d)
     }
 }
 
-void check_interrupts(CPUState *env)
+void check_interrupts(CPUXtensaState *env)
 {
     int minlevel = xtensa_get_cintlevel(env);
     uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE];
@@ -84,7 +84,7 @@ void check_interrupts(CPUState *env)
 
 static void xtensa_set_irq(void *opaque, int irq, int active)
 {
-    CPUState *env = opaque;
+    CPUXtensaState *env = opaque;
 
     if (irq >= env->config->ninterrupt) {
         qemu_log("%s: bad IRQ %d\n", __func__, irq);
@@ -101,12 +101,12 @@ static void xtensa_set_irq(void *opaque, int irq, int active)
     }
 }
 
-void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active)
+void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active)
 {
     qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
 }
 
-void xtensa_rearm_ccompare_timer(CPUState *env)
+void xtensa_rearm_ccompare_timer(CPUXtensaState *env)
 {
     int i;
     uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
@@ -125,7 +125,7 @@ void xtensa_rearm_ccompare_timer(CPUState *env)
 
 static void xtensa_ccompare_cb(void *opaque)
 {
-    CPUState *env = opaque;
+    CPUXtensaState *env = opaque;
 
     if (env->halted) {
         env->halt_clock = qemu_get_clock_ns(vm_clock);
@@ -137,7 +137,7 @@ static void xtensa_ccompare_cb(void *opaque)
     }
 }
 
-void xtensa_irq_init(CPUState *env)
+void xtensa_irq_init(CPUXtensaState *env)
 {
     env->irq_inputs = (void **)qemu_allocate_irqs(
             xtensa_set_irq, env, env->config->ninterrupt);
@@ -148,7 +148,7 @@ void xtensa_irq_init(CPUState *env)
     }
 }
 
-void *xtensa_get_extint(CPUState *env, unsigned extint)
+void *xtensa_get_extint(CPUXtensaState *env, unsigned extint)
 {
     if (extint < env->config->nextint) {
         unsigned irq = env->config->extint[extint];
diff --git a/hw/xtensa_sim.c b/hw/xtensa_sim.c
index 104e5dc619..c7e05dcf4e 100644
--- a/hw/xtensa_sim.c
+++ b/hw/xtensa_sim.c
@@ -39,7 +39,7 @@ static uint64_t translate_phys_addr(void *env, uint64_t addr)
 
 static void sim_reset(void *env)
 {
-    cpu_reset(env);
+    cpu_state_reset(env);
 }
 
 static void sim_init(ram_addr_t ram_size,
@@ -47,7 +47,7 @@ static void sim_init(ram_addr_t ram_size,
         const char *kernel_filename, const char *kernel_cmdline,
         const char *initrd_filename, const char *cpu_model)
 {
-    CPUState *env = NULL;
+    CPUXtensaState *env = NULL;
     MemoryRegion *ram, *rom;
     int n;
 
diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c
new file mode 100644
index 0000000000..4f97575770
--- /dev/null
+++ b/hw/zynq_slcr.c
@@ -0,0 +1,535 @@
+/*
+ * Status and system control registers for Xilinx Zynq Platform
+ *
+ * Copyright (c) 2011 Michal Simek <monstr@monstr.eu>
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Based on hw/arm_sysctl.c, written by Paul Brook
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "sysemu.h"
+
+#ifdef ZYNQ_ARM_SLCR_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define XILINX_LOCK_KEY 0x767b
+#define XILINX_UNLOCK_KEY 0xdf0d
+
+typedef enum {
+  ARM_PLL_CTRL,
+  DDR_PLL_CTRL,
+  IO_PLL_CTRL,
+  PLL_STATUS,
+  ARM_PPL_CFG,
+  DDR_PLL_CFG,
+  IO_PLL_CFG,
+  PLL_BG_CTRL,
+  PLL_MAX
+} PLLValues;
+
+typedef enum {
+  ARM_CLK_CTRL,
+  DDR_CLK_CTRL,
+  DCI_CLK_CTRL,
+  APER_CLK_CTRL,
+  USB0_CLK_CTRL,
+  USB1_CLK_CTRL,
+  GEM0_RCLK_CTRL,
+  GEM1_RCLK_CTRL,
+  GEM0_CLK_CTRL,
+  GEM1_CLK_CTRL,
+  SMC_CLK_CTRL,
+  LQSPI_CLK_CTRL,
+  SDIO_CLK_CTRL,
+  UART_CLK_CTRL,
+  SPI_CLK_CTRL,
+  CAN_CLK_CTRL,
+  CAN_MIOCLK_CTRL,
+  DBG_CLK_CTRL,
+  PCAP_CLK_CTRL,
+  TOPSW_CLK_CTRL,
+  CLK_MAX
+} ClkValues;
+
+typedef enum {
+  CLK_CTRL,
+  THR_CTRL,
+  THR_CNT,
+  THR_STA,
+  FPGA_MAX
+} FPGAValues;
+
+typedef enum {
+  SYNC_CTRL,
+  SYNC_STATUS,
+  BANDGAP_TRIP,
+  CC_TEST,
+  PLL_PREDIVISOR,
+  CLK_621_TRUE,
+  PICTURE_DBG,
+  PICTURE_DBG_UCNT,
+  PICTURE_DBG_LCNT,
+  MISC_MAX
+} MiscValues;
+
+typedef enum {
+  PSS,
+  DDDR,
+  DMAC,
+  USB,
+  GEM,
+  SDIO,
+  SPI,
+  CAN,
+  I2C,
+  UART,
+  GPIO,
+  LQSPI,
+  SMC,
+  OCM,
+  DEVCI,
+  FPGA,
+  A9_CPU,
+  RS_AWDT,
+  RST_REASON,
+  RST_REASON_CLR,
+  REBOOT_STATUS,
+  BOOT_MODE,
+  RESET_MAX
+} ResetValues;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    union {
+        struct {
+            uint16_t scl;
+            uint16_t lockval;
+            uint32_t pll[PLL_MAX]; /* 0x100 - 0x11C */
+            uint32_t clk[CLK_MAX]; /* 0x120 - 0x16C */
+            uint32_t fpga[4][FPGA_MAX]; /* 0x170 - 0x1AC */
+            uint32_t misc[MISC_MAX]; /* 0x1B0 - 0x1D8 */
+            uint32_t reset[RESET_MAX]; /* 0x200 - 0x25C */
+            uint32_t apu_ctrl; /* 0x300 */
+            uint32_t wdt_clk_sel; /* 0x304 */
+            uint32_t tz_ocm[3]; /* 0x400 - 0x408 */
+            uint32_t tz_ddr; /* 0x430 */
+            uint32_t tz_dma[3]; /* 0x440 - 0x448 */
+            uint32_t tz_misc[3]; /* 0x450 - 0x458 */
+            uint32_t tz_fpga[2]; /* 0x484 - 0x488 */
+            uint32_t dbg_ctrl; /* 0x500 */
+            uint32_t pss_idcode; /* 0x530 */
+            uint32_t ddr[8]; /* 0x600 - 0x620 - 0x604-missing */
+            uint32_t mio[54]; /* 0x700 - 0x7D4 */
+            uint32_t mio_func[4]; /* 0x800 - 0x810 */
+            uint32_t sd[2]; /* 0x830 - 0x834 */
+            uint32_t lvl_shftr_en; /* 0x900 */
+            uint32_t ocm_cfg; /* 0x910 */
+            uint32_t cpu_ram[8]; /* 0xA00 - 0xA1C */
+            uint32_t iou[7]; /* 0xA30 - 0xA48 */
+            uint32_t dmac_ram; /* 0xA50 */
+            uint32_t afi[4][3]; /* 0xA60 - 0xA8C */
+            uint32_t ocm[3]; /* 0xA90 - 0xA98 */
+            uint32_t devci_ram; /* 0xAA0 */
+            uint32_t csg_ram; /* 0xAB0 */
+            uint32_t gpiob[12]; /* 0xB00 - 0xB2C */
+            uint32_t ddriob[14]; /* 0xB40 - 0xB74 */
+        };
+        uint8_t data[0x1000];
+    };
+} ZynqSLCRState;
+
+static void zynq_slcr_reset(DeviceState *d)
+{
+    int i;
+    ZynqSLCRState *s =
+            FROM_SYSBUS(ZynqSLCRState, sysbus_from_qdev(d));
+
+    DB_PRINT("RESET\n");
+
+    s->lockval = 1;
+    /* 0x100 - 0x11C */
+    s->pll[ARM_PLL_CTRL] = 0x0001A008;
+    s->pll[DDR_PLL_CTRL] = 0x0001A008;
+    s->pll[IO_PLL_CTRL] = 0x0001A008;
+    s->pll[PLL_STATUS] = 0x0000003F;
+    s->pll[ARM_PPL_CFG] = 0x00014000;
+    s->pll[DDR_PLL_CFG] = 0x00014000;
+    s->pll[IO_PLL_CFG] = 0x00014000;
+
+    /* 0x120 - 0x16C */
+    s->clk[ARM_CLK_CTRL] = 0x1F000400;
+    s->clk[DDR_CLK_CTRL] = 0x18400003;
+    s->clk[DCI_CLK_CTRL] = 0x01E03201;
+    s->clk[APER_CLK_CTRL] = 0x01FFCCCD;
+    s->clk[USB0_CLK_CTRL] = s->clk[USB1_CLK_CTRL] = 0x00101941;
+    s->clk[GEM0_RCLK_CTRL] = s->clk[GEM1_RCLK_CTRL] = 0x00000001;
+    s->clk[GEM0_CLK_CTRL] = s->clk[GEM1_CLK_CTRL] = 0x00003C01;
+    s->clk[SMC_CLK_CTRL] = 0x00003C01;
+    s->clk[LQSPI_CLK_CTRL] = 0x00002821;
+    s->clk[SDIO_CLK_CTRL] = 0x00001E03;
+    s->clk[UART_CLK_CTRL] = 0x00003F03;
+    s->clk[SPI_CLK_CTRL] = 0x00003F03;
+    s->clk[CAN_CLK_CTRL] = 0x00501903;
+    s->clk[DBG_CLK_CTRL] = 0x00000F03;
+    s->clk[PCAP_CLK_CTRL] = 0x00000F01;
+
+    /* 0x170 - 0x1AC */
+    s->fpga[0][CLK_CTRL] = s->fpga[1][CLK_CTRL] = s->fpga[2][CLK_CTRL] =
+            s->fpga[3][CLK_CTRL] = 0x00101800;
+    s->fpga[0][THR_STA] = s->fpga[1][THR_STA] = s->fpga[2][THR_STA] =
+            s->fpga[3][THR_STA] = 0x00010000;
+
+    /* 0x1B0 - 0x1D8 */
+    s->misc[BANDGAP_TRIP] = 0x0000001F;
+    s->misc[PLL_PREDIVISOR] = 0x00000001;
+    s->misc[CLK_621_TRUE] = 0x00000001;
+
+    /* 0x200 - 0x25C */
+    s->reset[FPGA] = 0x01F33F0F;
+    s->reset[RST_REASON] = 0x00000040;
+
+    /* 0x700 - 0x7D4 */
+    for (i = 0; i < 54; i++) {
+        s->mio[i] = 0x00001601;
+    }
+    for (i = 2; i <= 8; i++) {
+        s->mio[i] = 0x00000601;
+    }
+
+    /* MIO_MST_TRI0, MIO_MST_TRI1 */
+    s->mio_func[2] = s->mio_func[3] = 0xFFFFFFFF;
+
+    s->cpu_ram[0] = s->cpu_ram[1] = s->cpu_ram[3] =
+            s->cpu_ram[4] = s->cpu_ram[7] = 0x00010101;
+    s->cpu_ram[2] = s->cpu_ram[5] = 0x01010101;
+    s->cpu_ram[6] = 0x00000001;
+
+    s->iou[0] = s->iou[1] = s->iou[2] = s->iou[3] = 0x09090909;
+    s->iou[4] = s->iou[5] = 0x00090909;
+    s->iou[6] = 0x00000909;
+
+    s->dmac_ram = 0x00000009;
+
+    s->afi[0][0] = s->afi[0][1] = 0x09090909;
+    s->afi[1][0] = s->afi[1][1] = 0x09090909;
+    s->afi[2][0] = s->afi[2][1] = 0x09090909;
+    s->afi[3][0] = s->afi[3][1] = 0x09090909;
+    s->afi[0][2] = s->afi[1][2] = s->afi[2][2] = s->afi[3][2] = 0x00000909;
+
+    s->ocm[0] = 0x01010101;
+    s->ocm[1] = s->ocm[2] = 0x09090909;
+
+    s->devci_ram = 0x00000909;
+    s->csg_ram = 0x00000001;
+
+    s->ddriob[0] = s->ddriob[1] = s->ddriob[2] = s->ddriob[3] = 0x00000e00;
+    s->ddriob[4] = s->ddriob[5] = s->ddriob[6] = 0x00000e00;
+    s->ddriob[12] = 0x00000021;
+}
+
+static inline uint32_t zynq_slcr_read_imp(void *opaque,
+    target_phys_addr_t offset)
+{
+    ZynqSLCRState *s = (ZynqSLCRState *)opaque;
+
+    switch (offset) {
+    case 0x0: /* SCL */
+        return s->scl;
+    case 0x4: /* LOCK */
+    case 0x8: /* UNLOCK */
+        DB_PRINT("Reading SCLR_LOCK/UNLOCK is not enabled\n");
+        return 0;
+    case 0x0C: /* LOCKSTA */
+        return s->lockval;
+    case 0x100 ... 0x11C:
+        return s->pll[(offset - 0x100) / 4];
+    case 0x120 ... 0x16C:
+        return s->clk[(offset - 0x120) / 4];
+    case 0x170 ... 0x1AC:
+        return s->fpga[0][(offset - 0x170) / 4];
+    case 0x1B0 ... 0x1D8:
+        return s->misc[(offset - 0x1B0) / 4];
+    case 0x200 ... 0x258:
+        return s->reset[(offset - 0x200) / 4];
+    case 0x25c:
+        return 1;
+    case 0x300:
+        return s->apu_ctrl;
+    case 0x304:
+        return s->wdt_clk_sel;
+    case 0x400 ... 0x408:
+        return s->tz_ocm[(offset - 0x400) / 4];
+    case 0x430:
+        return s->tz_ddr;
+    case 0x440 ... 0x448:
+        return s->tz_dma[(offset - 0x440) / 4];
+    case 0x450 ... 0x458:
+        return s->tz_misc[(offset - 0x450) / 4];
+    case 0x484 ... 0x488:
+        return s->tz_fpga[(offset - 0x484) / 4];
+    case 0x500:
+        return s->dbg_ctrl;
+    case 0x530:
+        return s->pss_idcode;
+    case 0x600 ... 0x620:
+        if (offset == 0x604) {
+            goto bad_reg;
+        }
+        return s->ddr[(offset - 0x600) / 4];
+    case 0x700 ... 0x7D4:
+        return s->mio[(offset - 0x700) / 4];
+    case 0x800 ... 0x810:
+        return s->mio_func[(offset - 0x800) / 4];
+    case 0x830 ... 0x834:
+        return s->sd[(offset - 0x830) / 4];
+    case 0x900:
+        return s->lvl_shftr_en;
+    case 0x910:
+        return s->ocm_cfg;
+    case 0xA00 ... 0xA1C:
+        return s->cpu_ram[(offset - 0xA00) / 4];
+    case 0xA30 ... 0xA48:
+        return s->iou[(offset - 0xA30) / 4];
+    case 0xA50:
+        return s->dmac_ram;
+    case 0xA60 ... 0xA8C:
+        return s->afi[0][(offset - 0xA60) / 4];
+    case 0xA90 ... 0xA98:
+        return s->ocm[(offset - 0xA90) / 4];
+    case 0xAA0:
+        return s->devci_ram;
+    case 0xAB0:
+        return s->csg_ram;
+    case 0xB00 ... 0xB2C:
+        return s->gpiob[(offset - 0xB00) / 4];
+    case 0xB40 ... 0xB74:
+        return s->ddriob[(offset - 0xB40) / 4];
+    default:
+    bad_reg:
+        DB_PRINT("Bad register offset 0x%x\n", (int)offset);
+        return 0;
+    }
+}
+
+static uint64_t zynq_slcr_read(void *opaque, target_phys_addr_t offset,
+    unsigned size)
+{
+    uint32_t ret = zynq_slcr_read_imp(opaque, offset);
+
+    DB_PRINT("addr: %08x data: %08x\n", offset, ret);
+    return ret;
+}
+
+static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
+                          uint64_t val, unsigned size)
+{
+    ZynqSLCRState *s = (ZynqSLCRState *)opaque;
+
+    DB_PRINT("offset: %08x data: %08x\n", offset, (unsigned)val);
+
+    switch (offset) {
+    case 0x00: /* SCL */
+        s->scl = val & 0x1;
+    return;
+    case 0x4: /* SLCR_LOCK */
+        if ((val & 0xFFFF) == XILINX_LOCK_KEY) {
+            DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset,
+                (unsigned)val & 0xFFFF);
+            s->lockval = 1;
+        } else {
+            DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n",
+                (int)offset, (unsigned)val & 0xFFFF);
+        }
+        return;
+    case 0x8: /* SLCR_UNLOCK */
+        if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) {
+            DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset,
+                (unsigned)val & 0xFFFF);
+            s->lockval = 0;
+        } else {
+            DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n",
+                (int)offset, (unsigned)val & 0xFFFF);
+        }
+        return;
+    case 0xc: /* LOCKSTA */
+        DB_PRINT("Writing SCLR_LOCKSTA is not enabled\n");
+        return;
+    }
+
+    if (!s->lockval) {
+        switch (offset) {
+        case 0x100 ... 0x11C:
+            if (offset == 0x10C) {
+                goto bad_reg;
+            }
+            s->pll[(offset - 0x100) / 4] = val;
+            break;
+        case 0x120 ... 0x16C:
+            s->clk[(offset - 0x120) / 4] = val;
+            break;
+        case 0x170 ... 0x1AC:
+            s->fpga[0][(offset - 0x170) / 4] = val;
+            break;
+        case 0x1B0 ... 0x1D8:
+            s->misc[(offset - 0x1B0) / 4] = val;
+            break;
+        case 0x200 ... 0x25C:
+            if (offset == 0x250) {
+                goto bad_reg;
+            }
+            s->reset[(offset - 0x200) / 4] = val;
+            break;
+        case 0x300:
+            s->apu_ctrl = val;
+            break;
+        case 0x304:
+            s->wdt_clk_sel = val;
+            break;
+        case 0x400 ... 0x408:
+            s->tz_ocm[(offset - 0x400) / 4] = val;
+            break;
+        case 0x430:
+            s->tz_ddr = val;
+            break;
+        case 0x440 ... 0x448:
+            s->tz_dma[(offset - 0x440) / 4] = val;
+            break;
+        case 0x450 ... 0x458:
+            s->tz_misc[(offset - 0x450) / 4] = val;
+            break;
+        case 0x484 ... 0x488:
+            s->tz_fpga[(offset - 0x484) / 4] = val;
+            break;
+        case 0x500:
+            s->dbg_ctrl = val;
+            break;
+        case 0x530:
+            s->pss_idcode = val;
+            break;
+        case 0x600 ... 0x620:
+            if (offset == 0x604) {
+                goto bad_reg;
+            }
+            s->ddr[(offset - 0x600) / 4] = val;
+            break;
+        case 0x700 ... 0x7D4:
+            s->mio[(offset - 0x700) / 4] = val;
+            break;
+        case 0x800 ... 0x810:
+            s->mio_func[(offset - 0x800) / 4] = val;
+            break;
+        case 0x830 ... 0x834:
+            s->sd[(offset - 0x830) / 4] = val;
+            break;
+        case 0x900:
+            s->lvl_shftr_en = val;
+            break;
+        case 0x910:
+            break;
+        case 0xA00 ... 0xA1C:
+            s->cpu_ram[(offset - 0xA00) / 4] = val;
+            break;
+        case 0xA30 ... 0xA48:
+            s->iou[(offset - 0xA30) / 4] = val;
+            break;
+        case 0xA50:
+            s->dmac_ram = val;
+            break;
+        case 0xA60 ... 0xA8C:
+            s->afi[0][(offset - 0xA60) / 4] = val;
+            break;
+        case 0xA90:
+            s->ocm[0] = val;
+            break;
+        case 0xAA0:
+            s->devci_ram = val;
+            break;
+        case 0xAB0:
+            s->csg_ram = val;
+            break;
+        case 0xB00 ... 0xB2C:
+            if (offset == 0xB20 || offset == 0xB2C) {
+                goto bad_reg;
+            }
+            s->gpiob[(offset - 0xB00) / 4] = val;
+            break;
+        case 0xB40 ... 0xB74:
+            s->ddriob[(offset - 0xB40) / 4] = val;
+            break;
+        default:
+        bad_reg:
+            DB_PRINT("Bad register write %x <= %08x\n", (int)offset, val);
+        }
+    } else {
+        DB_PRINT("SCLR registers are locked. Unlock them first\n");
+    }
+}
+
+static const MemoryRegionOps slcr_ops = {
+    .read = zynq_slcr_read,
+    .write = zynq_slcr_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int zynq_slcr_init(SysBusDevice *dev)
+{
+    ZynqSLCRState *s = FROM_SYSBUS(ZynqSLCRState, dev);
+
+    memory_region_init_io(&s->iomem, &slcr_ops, s, "slcr", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_zynq_slcr = {
+    .name = "zynq_slcr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(data, ZynqSLCRState, 0x1000),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void zynq_slcr_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = zynq_slcr_init;
+    dc->vmsd = &vmstate_zynq_slcr;
+    dc->reset = zynq_slcr_reset;
+}
+
+static TypeInfo zynq_slcr_info = {
+    .class_init = zynq_slcr_class_init,
+    .name  = "xilinx,zynq_slcr",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ZynqSLCRState),
+};
+
+static void zynq_slcr_register_types(void)
+{
+    type_register_static(&zynq_slcr_info);
+}
+
+type_init(zynq_slcr_register_types)