Merge remote branch 'mst/for_anthony' into staging
diff --git a/Makefile.target b/Makefile.target
index f498574..068408f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -46,6 +46,7 @@
 # cpu emulator library
 libobj-y = exec.o translate-all.o cpu-exec.o translate.o
 libobj-y += tcg/tcg.o
+libobj-y += rwhandler.o
 libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
 libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
 libobj-y += op_helper.o helper.o
diff --git a/bswap.h b/bswap.h
index 4558704..aace9b7 100644
--- a/bswap.h
+++ b/bswap.h
@@ -214,4 +214,10 @@
 #undef le_bswaps
 #undef be_bswaps
 
+/* len must be one of 1, 2, 4 */
+static inline uint32_t qemu_bswap_len(uint32_t value, int len)
+{
+    return bswap32(value) >> (32 - 8 * len);
+}
+
 #endif /* BSWAP_H */
diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c
index 8ec3d99..8f94c35 100644
--- a/hw/mac_dbdma.c
+++ b/hw/mac_dbdma.c
@@ -402,7 +402,9 @@
     ch->io.dma_end = dbdma_end;
     ch->io.is_dma_out = 1;
     ch->processing = 1;
-    ch->rw(&ch->io);
+    if (ch->rw) {
+        ch->rw(&ch->io);
+    }
 }
 
 static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
@@ -425,7 +427,9 @@
     ch->io.dma_end = dbdma_end;
     ch->io.is_dma_out = 0;
     ch->processing = 1;
-    ch->rw(&ch->io);
+    if (ch->rw) {
+        ch->rw(&ch->io);
+    }
 }
 
 static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
@@ -688,7 +692,7 @@
 
     if (status & ACTIVE)
         qemu_bh_schedule(dbdma_bh);
-    if (status & FLUSH)
+    if ((status & FLUSH) && ch->flush)
         ch->flush(&ch->io);
 }
 
diff --git a/hw/pci.h b/hw/pci.h
index 8b511d2..37ebdc4 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -70,7 +70,6 @@
 #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
 #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
 
-typedef uint64_t pcibus_t;
 #define FMT_PCIBUS                      PRIx64
 
 typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
diff --git a/hw/pci_host.c b/hw/pci_host.c
index 6289ead..b15d5fa 100644
--- a/hw/pci_host.c
+++ b/hw/pci_host.c
@@ -79,152 +79,120 @@
     return val;
 }
 
-static void pci_host_config_writel(void *opaque, target_phys_addr_t addr,
-                                   uint32_t val)
+static void pci_host_config_write(ReadWriteHandler *handler,
+                                  pcibus_t addr, uint32_t val, int len)
 {
-    PCIHostState *s = opaque;
+    PCIHostState *s = container_of(handler, PCIHostState, conf_handler);
 
+    PCI_DPRINTF("%s addr %" FMT_PCIBUS " %d val %"PRIx32"\n",
+                __func__, addr, len, val);
 #ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap32(val);
+    val = qemu_bswap_len(val, len);
 #endif
-    PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n",
-                __func__, addr, val);
     s->config_reg = val;
 }
 
-static uint32_t pci_host_config_readl(void *opaque, target_phys_addr_t addr)
+static uint32_t pci_host_config_read(ReadWriteHandler *handler,
+                                            pcibus_t addr, int len)
 {
-    PCIHostState *s = opaque;
+    PCIHostState *s = container_of(handler, PCIHostState, conf_handler);
     uint32_t val = s->config_reg;
-
 #ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap32(val);
+    val = qemu_bswap_len(val, len);
 #endif
-    PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n",
-                __func__, addr, val);
+    PCI_DPRINTF("%s addr %" FMT_PCIBUS " len %d val %"PRIx32"\n",
+                __func__, addr, len, val);
     return val;
 }
 
-static CPUWriteMemoryFunc * const pci_host_config_write[] = {
-    &pci_host_config_writel,
-    &pci_host_config_writel,
-    &pci_host_config_writel,
-};
+static void pci_host_config_write_noswap(ReadWriteHandler *handler,
+                                         pcibus_t addr, uint32_t val, int len)
+{
+    PCIHostState *s = container_of(handler, PCIHostState, conf_noswap_handler);
 
-static CPUReadMemoryFunc * const pci_host_config_read[] = {
-    &pci_host_config_readl,
-    &pci_host_config_readl,
-    &pci_host_config_readl,
-};
+    PCI_DPRINTF("%s addr %" FMT_PCIBUS " %d val %"PRIx32"\n",
+                __func__, addr, len, val);
+    s->config_reg = val;
+}
+
+static uint32_t pci_host_config_read_noswap(ReadWriteHandler *handler,
+                                            pcibus_t addr, int len)
+{
+    PCIHostState *s = container_of(handler, PCIHostState, conf_noswap_handler);
+    uint32_t val = s->config_reg;
+
+    PCI_DPRINTF("%s addr %" FMT_PCIBUS " len %d val %"PRIx32"\n",
+                __func__, addr, len, val);
+    return val;
+}
+
+static void pci_host_data_write(ReadWriteHandler *handler,
+                                pcibus_t addr, uint32_t val, int len)
+{
+    PCIHostState *s = container_of(handler, PCIHostState, data_handler);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    PCI_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n",
+                addr, len, val);
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+}
+
+static uint32_t pci_host_data_read(ReadWriteHandler *handler,
+                                   pcibus_t addr, int len)
+{
+    PCIHostState *s = container_of(handler, PCIHostState, data_handler);
+    uint32_t val;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xffffffff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
+    PCI_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n",
+                addr, len, val);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    return val;
+}
+
+static void pci_host_init(PCIHostState *s)
+{
+    s->conf_handler.write = pci_host_config_write;
+    s->conf_handler.read = pci_host_config_read;
+    s->conf_noswap_handler.write = pci_host_config_write_noswap;
+    s->conf_noswap_handler.read = pci_host_config_read_noswap;
+    s->data_handler.write = pci_host_data_write;
+    s->data_handler.read = pci_host_data_read;
+}
 
 int pci_host_conf_register_mmio(PCIHostState *s)
 {
-    return cpu_register_io_memory(pci_host_config_read,
-                                  pci_host_config_write, s);
+    pci_host_init(s);
+    return cpu_register_io_memory_simple(&s->conf_handler);
 }
 
-static void pci_host_config_writel_noswap(void *opaque,
-                                          target_phys_addr_t addr,
-                                          uint32_t val)
-{
-    PCIHostState *s = opaque;
-
-    PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n",
-                __func__, addr, val);
-    s->config_reg = val;
-}
-
-static uint32_t pci_host_config_readl_noswap(void *opaque,
-                                             target_phys_addr_t addr)
-{
-    PCIHostState *s = opaque;
-    uint32_t val = s->config_reg;
-
-    PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n",
-                __func__, addr, val);
-    return val;
-}
-
-static CPUWriteMemoryFunc * const pci_host_config_write_noswap[] = {
-    &pci_host_config_writel_noswap,
-    &pci_host_config_writel_noswap,
-    &pci_host_config_writel_noswap,
-};
-
-static CPUReadMemoryFunc * const pci_host_config_read_noswap[] = {
-    &pci_host_config_readl_noswap,
-    &pci_host_config_readl_noswap,
-    &pci_host_config_readl_noswap,
-};
-
 int pci_host_conf_register_mmio_noswap(PCIHostState *s)
 {
-    return cpu_register_io_memory(pci_host_config_read_noswap,
-                                  pci_host_config_write_noswap, s);
-}
-
-static void pci_host_config_writel_ioport(void *opaque,
-                                          uint32_t addr, uint32_t val)
-{
-    PCIHostState *s = opaque;
-
-    PCI_DPRINTF("%s addr %"PRIx32 " val %"PRIx32"\n", __func__, addr, val);
-    s->config_reg = val;
-}
-
-static uint32_t pci_host_config_readl_ioport(void *opaque, uint32_t addr)
-{
-    PCIHostState *s = opaque;
-    uint32_t val = s->config_reg;
-
-    PCI_DPRINTF("%s addr %"PRIx32" val %"PRIx32"\n", __func__, addr, val);
-    return val;
+    pci_host_init(s);
+    return cpu_register_io_memory_simple(&s->conf_noswap_handler);
 }
 
 void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s)
 {
-    register_ioport_write(ioport, 4, 4, pci_host_config_writel_ioport, s);
-    register_ioport_read(ioport, 4, 4, pci_host_config_readl_ioport, s);
+    pci_host_init(s);
+    register_ioport_simple(&s->conf_noswap_handler, ioport, 4, 4);
 }
 
-#define PCI_ADDR_T      target_phys_addr_t
-#define PCI_HOST_SUFFIX _mmio
-
-#include "pci_host_template.h"
-
-static CPUWriteMemoryFunc * const pci_host_data_write_mmio[] = {
-    pci_host_data_writeb_mmio,
-    pci_host_data_writew_mmio,
-    pci_host_data_writel_mmio,
-};
-
-static CPUReadMemoryFunc * const pci_host_data_read_mmio[] = {
-    pci_host_data_readb_mmio,
-    pci_host_data_readw_mmio,
-    pci_host_data_readl_mmio,
-};
-
 int pci_host_data_register_mmio(PCIHostState *s)
 {
-    return cpu_register_io_memory(pci_host_data_read_mmio,
-                                  pci_host_data_write_mmio,
-                                  s);
+    pci_host_init(s);
+    return cpu_register_io_memory_simple(&s->data_handler);
 }
 
-#undef PCI_ADDR_T
-#undef PCI_HOST_SUFFIX
-
-#define PCI_ADDR_T      uint32_t
-#define PCI_HOST_SUFFIX _ioport
-
-#include "pci_host_template.h"
-
 void pci_host_data_register_ioport(pio_addr_t ioport, PCIHostState *s)
 {
-    register_ioport_write(ioport, 4, 1, pci_host_data_writeb_ioport, s);
-    register_ioport_write(ioport, 4, 2, pci_host_data_writew_ioport, s);
-    register_ioport_write(ioport, 4, 4, pci_host_data_writel_ioport, s);
-    register_ioport_read(ioport, 4, 1, pci_host_data_readb_ioport, s);
-    register_ioport_read(ioport, 4, 2, pci_host_data_readw_ioport, s);
-    register_ioport_read(ioport, 4, 4, pci_host_data_readl_ioport, s);
+    pci_host_init(s);
+    register_ioport_simple(&s->data_handler, ioport, 4, 1);
+    register_ioport_simple(&s->data_handler, ioport, 4, 2);
+    register_ioport_simple(&s->data_handler, ioport, 4, 4);
 }
diff --git a/hw/pci_host.h b/hw/pci_host.h
index a006687..5ff6443 100644
--- a/hw/pci_host.h
+++ b/hw/pci_host.h
@@ -29,9 +29,13 @@
 #define PCI_HOST_H
 
 #include "sysbus.h"
+#include "rwhandler.h"
 
 struct PCIHostState {
     SysBusDevice busdev;
+    ReadWriteHandler conf_noswap_handler;
+    ReadWriteHandler conf_handler;
+    ReadWriteHandler data_handler;
     uint32_t config_reg;
     PCIBus *bus;
 };
diff --git a/hw/pci_host_template.h b/hw/pci_host_template.h
deleted file mode 100644
index 11e6c88..0000000
--- a/hw/pci_host_template.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * QEMU Common PCI Host bridge configuration data space access routines.
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * 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.
- */
-
-/* Worker routines for a PCI host controller that uses an {address,data}
-   register pair to access PCI configuration space.  */
-
-static void glue(pci_host_data_writeb, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr, uint32_t val)
-{
-    PCIHostState *s = opaque;
-
-    PCI_DPRINTF("writeb addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-    if (s->config_reg & (1u << 31))
-        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1);
-}
-
-static void glue(pci_host_data_writew, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr, uint32_t val)
-{
-    PCIHostState *s = opaque;
-#ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap16(val);
-#endif
-    PCI_DPRINTF("writew addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-    if (s->config_reg & (1u << 31))
-        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2);
-}
-
-static void glue(pci_host_data_writel, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr, uint32_t val)
-{
-    PCIHostState *s = opaque;
-#ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap32(val);
-#endif
-    PCI_DPRINTF("writel addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-    if (s->config_reg & (1u << 31))
-        pci_data_write(s->bus, s->config_reg, val, 4);
-}
-
-static uint32_t glue(pci_host_data_readb, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr)
-{
-    PCIHostState *s = opaque;
-    uint32_t val;
-
-    if (!(s->config_reg & (1 << 31)))
-        return 0xff;
-    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 1);
-    PCI_DPRINTF("readb addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-    return val;
-}
-
-static uint32_t glue(pci_host_data_readw, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr)
-{
-    PCIHostState *s = opaque;
-    uint32_t val;
-    if (!(s->config_reg & (1 << 31)))
-        return 0xffff;
-    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2);
-    PCI_DPRINTF("readw addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-#ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap16(val);
-#endif
-    return val;
-}
-
-static uint32_t glue(pci_host_data_readl, PCI_HOST_SUFFIX)(
-    void* opaque, PCI_ADDR_T addr)
-{
-    PCIHostState *s = opaque;
-    uint32_t val;
-    if (!(s->config_reg & (1 << 31)))
-        return 0xffffffff;
-    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4);
-    PCI_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n",
-                (target_phys_addr_t)addr, val);
-#ifdef TARGET_WORDS_BIGENDIAN
-    val = bswap32(val);
-#endif
-    return val;
-}
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 63379c2..fe7a121 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -63,6 +63,7 @@
 
 #define PCI_VENDOR_ID_APPLE              0x106b
 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP    0x0020
+#define PCI_DEVICE_ID_APPLE_U3_AGP       0x004b
 
 #define PCI_VENDOR_ID_SUN                0x108e
 #define PCI_DEVICE_ID_SUN_EBUS           0x1000
diff --git a/hw/ppc.h b/hw/ppc.h
index bbf3a98..de13092 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -40,10 +40,12 @@
     ARCH_PREP = 0,
     ARCH_MAC99,
     ARCH_HEATHROW,
+    ARCH_MAC99_U3,
 };
 
 #define FW_CFG_PPC_WIDTH	(FW_CFG_ARCH_LOCAL + 0x00)
 #define FW_CFG_PPC_HEIGHT	(FW_CFG_ARCH_LOCAL + 0x01)
 #define FW_CFG_PPC_DEPTH	(FW_CFG_ARCH_LOCAL + 0x02)
+#define FW_CFG_PPC_TBFREQ	(FW_CFG_ARCH_LOCAL + 0x03)
 
 #define PPC_SERIAL_MM_BAUDBASE 399193
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index a04dffe..89f96bb 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -58,6 +58,7 @@
 
 /* UniNorth PCI */
 PCIBus *pci_pmac_init(qemu_irq *pic);
+PCIBus *pci_pmac_u3_init(qemu_irq *pic);
 
 /* Mac NVRAM */
 typedef struct MacIONVRAMState MacIONVRAMState;
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index a4c714a..bc86c85 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -21,6 +21,30 @@
  * 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.
+ *
+ * PCI bus layout on a real G5 (U3 based):
+ *
+ * 0000:f0:0b.0 Host bridge [0600]: Apple Computer Inc. U3 AGP [106b:004b]
+ * 0000:f0:10.0 VGA compatible controller [0300]: ATI Technologies Inc RV350 AP [Radeon 9600] [1002:4150]
+ * 0001:00:00.0 Host bridge [0600]: Apple Computer Inc. CPC945 HT Bridge [106b:004a]
+ * 0001:00:01.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12)
+ * 0001:00:02.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12)
+ * 0001:00:03.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0045]
+ * 0001:00:04.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0046]
+ * 0001:00:05.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0047]
+ * 0001:00:06.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0048]
+ * 0001:00:07.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0049]
+ * 0001:01:07.0 Class [ff00]: Apple Computer Inc. K2 KeyLargo Mac/IO [106b:0041] (rev 20)
+ * 0001:01:08.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040]
+ * 0001:01:09.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040]
+ * 0001:02:0b.0 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43)
+ * 0001:02:0b.1 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43)
+ * 0001:02:0b.2 USB Controller [0c03]: NEC Corporation USB 2.0 [1033:00e0] (rev 04)
+ * 0001:03:0d.0 Class [ff00]: Apple Computer Inc. K2 ATA/100 [106b:0043]
+ * 0001:03:0e.0 FireWire (IEEE 1394) [0c00]: Apple Computer Inc. K2 FireWire [106b:0042]
+ * 0001:04:0f.0 Ethernet controller [0200]: Apple Computer Inc. K2 GMAC (Sun GEM) [106b:004c]
+ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240]
+ *
  */
 #include "hw.h"
 #include "ppc.h"
@@ -40,6 +64,8 @@
 #include "loader.h"
 #include "elf.h"
 #include "kvm.h"
+#include "kvm_ppc.h"
+#include "hw/usb.h"
 
 #define MAX_IDE_BUS 2
 #define VGA_BIOS_SIZE 65536
@@ -109,11 +135,13 @@
     int nvram_mem_index;
     int vga_bios_size, bios_size;
     int pic_mem_index, dbdma_mem_index, cuda_mem_index, escc_mem_index;
+    int ide_mem_index[3];
     int ppc_boot_device;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     void *fw_cfg;
     void *dbdma;
     uint8_t *vga_bios_ptr;
+    int machine_arch;
 
     linux_boot = (kernel_filename != NULL);
 
@@ -317,7 +345,14 @@
         }
     }
     pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL);
-    pci_bus = pci_pmac_init(pic);
+    if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) {
+        /* 970 gets a U3 bus */
+        pci_bus = pci_pmac_u3_init(pic);
+        machine_arch = ARCH_MAC99_U3;
+    } else {
+        pci_bus = pci_pmac_init(pic);
+        machine_arch = ARCH_MAC99;
+    }
     /* init basic PC hardware */
     pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size);
 
@@ -331,27 +366,41 @@
         fprintf(stderr, "qemu: too many IDE bus\n");
         exit(1);
     }
-    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
-        hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
-    }
     dbdma = DBDMA_init(&dbdma_mem_index);
-    pci_cmd646_ide_init(pci_bus, hd, 0);
+
+    /* We only emulate 2 out of 3 IDE controllers for now */
+    ide_mem_index[0] = -1;
+    hd[0] = drive_get(IF_IDE, 0, 0);
+    hd[1] = drive_get(IF_IDE, 0, 1);
+    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]);
+    hd[0] = drive_get(IF_IDE, 1, 0);
+    hd[1] = drive_get(IF_IDE, 1, 1);
+    ide_mem_index[2] = pmac_ide_init(hd, pic[0x0e], dbdma, 0x1a, pic[0x02]);
 
     /* cuda also initialize ADB */
+    if (machine_arch == ARCH_MAC99_U3) {
+        usb_enabled = 1;
+    }
     cuda_init(&cuda_mem_index, pic[0x19]);
 
     adb_kbd_init(&adb_bus);
     adb_mouse_init(&adb_bus);
 
-
     macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem_index,
-               dbdma_mem_index, cuda_mem_index, NULL, 0, NULL,
+               dbdma_mem_index, cuda_mem_index, NULL, 3, ide_mem_index,
                escc_mem_index);
 
     if (usb_enabled) {
         usb_ohci_init_pci(pci_bus, -1);
     }
 
+    /* U3 needs to use USB for input because Linux doesn't support via-cuda
+       on PPC64 */
+    if (machine_arch == ARCH_MAC99_U3) {
+        usbdevice_create("keyboard");
+        usbdevice_create("mouse");
+    }
+
     if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
         graphic_depth = 15;
 
@@ -364,7 +413,7 @@
     fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
-    fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_MAC99);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch);
     fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
     fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
     if (kernel_cmdline) {
@@ -381,6 +430,14 @@
     fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
     fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
 
+    if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+#endif
+    } else {
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+    }
+
     qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
 }
 
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 7ccc6a1..04a7835 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -40,6 +40,7 @@
 #include "loader.h"
 #include "elf.h"
 #include "kvm.h"
+#include "kvm_ppc.h"
 
 #define MAX_IDE_BUS 2
 #define VGA_BIOS_SIZE 65536
@@ -401,6 +402,14 @@
     fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
     fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
 
+    if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+#endif
+    } else {
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+    }
+
     qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
 }
 
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
index 19eb5e0..7bdf430 100644
--- a/hw/unin_pci.c
+++ b/hw/unin_pci.c
@@ -36,22 +36,31 @@
 #define UNIN_DPRINTF(fmt, ...)
 #endif
 
+static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e };
+
 typedef struct UNINState {
     SysBusDevice busdev;
     PCIHostState host_state;
+    ReadWriteHandler data_handler;
 } UNINState;
 
-/* Don't know if this matches real hardware, but it agrees with OHW.  */
 static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num)
 {
-    return (irq_num + (pci_dev->devfn >> 3)) & 3;
+    int retval;
+    int devfn = pci_dev->devfn & 0x00FFFFFF;
+
+    retval = (((devfn >> 11) & 0x1F) + irq_num) & 3;
+
+    return retval;
 }
 
 static void pci_unin_set_irq(void *opaque, int irq_num, int level)
 {
     qemu_irq *pic = opaque;
 
-    qemu_set_irq(pic[irq_num + 8], level);
+    UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__,
+                 unin_irq_line[irq_num], level);
+    qemu_set_irq(pic[unin_irq_line[irq_num]], level);
 }
 
 static void pci_unin_save(QEMUFile* f, void *opaque)
@@ -75,6 +84,68 @@
 {
 }
 
+static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
+{
+    uint32_t retval;
+
+    if (reg & (1u << 31)) {
+        /* XXX OpenBIOS compatibility hack */
+        retval = reg | (addr & 3);
+    } else if (reg & 1) {
+        /* CFA1 style */
+        retval = (reg & ~7u) | (addr & 7);
+    } else {
+        uint32_t slot, func;
+
+        /* Grab CFA0 style values */
+        slot = ffs(reg & 0xfffff800) - 1;
+        func = (reg >> 8) & 7;
+
+        /* ... and then convert them to x86 format */
+        /* config pointer */
+        retval = (reg & (0xff - 7)) | (addr & 7);
+        /* slot */
+        retval |= slot << 11;
+        /* fn */
+        retval |= func << 8;
+    }
+
+
+    UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
+                 reg, addr, retval);
+
+    return retval;
+}
+
+static void unin_data_write(ReadWriteHandler *handler,
+                            pcibus_t addr, uint32_t val, int len)
+{
+    UNINState *s = container_of(handler, UNINState, data_handler);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    UNIN_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+    pci_data_write(s->host_state.bus,
+                   unin_get_config_reg(s->host_state.config_reg, addr),
+                   val, len);
+}
+
+static uint32_t unin_data_read(ReadWriteHandler *handler,
+                               pcibus_t addr, int len)
+{
+    UNINState *s = container_of(handler, UNINState, data_handler);
+    uint32_t val;
+
+    val = pci_data_read(s->host_state.bus,
+                        unin_get_config_reg(s->host_state.config_reg, addr),
+                        len);
+    UNIN_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = qemu_bswap_len(val, len);
+#endif
+    return val;
+}
+
 static int pci_unin_main_init_device(SysBusDevice *dev)
 {
     UNINState *s;
@@ -85,7 +156,9 @@
     s = FROM_SYSBUS(UNINState, dev);
 
     pci_mem_config = pci_host_conf_register_mmio(&s->host_state);
-    pci_mem_data = pci_host_data_register_mmio(&s->host_state);
+    s->data_handler.read = unin_data_read;
+    s->data_handler.write = unin_data_write;
+    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler);
     sysbus_init_mmio(dev, 0x1000, pci_mem_config);
     sysbus_init_mmio(dev, 0x1000, pci_mem_data);
 
@@ -94,6 +167,27 @@
     return 0;
 }
 
+static int pci_u3_agp_init_device(SysBusDevice *dev)
+{
+    UNINState *s;
+    int pci_mem_config, pci_mem_data;
+
+    /* Uninorth U3 AGP bus */
+    s = FROM_SYSBUS(UNINState, dev);
+
+    pci_mem_config = pci_host_conf_register_mmio(&s->host_state);
+    s->data_handler.read = unin_data_read;
+    s->data_handler.write = unin_data_write;
+    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler);
+    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+    sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+
+    register_savevm("uninorth", 0, 1, pci_unin_save, pci_unin_load, &s->host_state);
+    qemu_register_reset(pci_unin_reset, &s->host_state);
+
+    return 0;
+}
+
 static int pci_unin_agp_init_device(SysBusDevice *dev)
 {
     UNINState *s;
@@ -175,6 +269,31 @@
     return d->host_state.bus;
 }
 
+PCIBus *pci_pmac_u3_init(qemu_irq *pic)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    UNINState *d;
+
+    /* Uninorth AGP bus */
+
+    dev = qdev_create(NULL, "u3-agp");
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    d = FROM_SYSBUS(UNINState, s);
+
+    d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
+                                         pci_unin_set_irq, pci_unin_map_irq,
+                                         pic, 11 << 3, 4);
+
+    sysbus_mmio_map(s, 0, 0xf0800000);
+    sysbus_mmio_map(s, 1, 0xf0c00000);
+
+    pci_create_simple(d->host_state.bus, 11 << 3, "u3-agp");
+
+    return d->host_state.bus;
+}
+
 static int unin_main_pci_host_init(PCIDevice *d)
 {
     pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
@@ -201,6 +320,21 @@
     return 0;
 }
 
+static int u3_agp_pci_host_init(PCIDevice *d)
+{
+    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_U3_AGP);
+    /* revision */
+    d->config[0x08] = 0x00;
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+    /* cache line size */
+    d->config[0x0C] = 0x08;
+    /* latency timer */
+    d->config[0x0D] = 0x10;
+    d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
+    return 0;
+}
+
 static int unin_internal_pci_host_init(PCIDevice *d)
 {
     pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
@@ -220,6 +354,12 @@
     .init      = unin_main_pci_host_init,
 };
 
+static PCIDeviceInfo u3_agp_pci_host_info = {
+    .qdev.name = "u3-agp",
+    .qdev.size = sizeof(PCIDevice),
+    .init      = u3_agp_pci_host_init,
+};
+
 static PCIDeviceInfo unin_agp_pci_host_info = {
     .qdev.name = "uni-north-agp",
     .qdev.size = sizeof(PCIDevice),
@@ -237,6 +377,9 @@
     sysbus_register_dev("uni-north", sizeof(UNINState),
                         pci_unin_main_init_device);
     pci_qdev_register(&unin_main_pci_host_info);
+    sysbus_register_dev("u3-agp", sizeof(UNINState),
+                        pci_u3_agp_init_device);
+    pci_qdev_register(&u3_agp_pci_host_info);
     sysbus_register_dev("uni-north-agp", sizeof(UNINState),
                         pci_unin_agp_init_device);
     pci_qdev_register(&unin_agp_pci_host_info);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index 153c651..7048fb8 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -147,14 +147,10 @@
     pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_XILINX);
     /* Both boards have the same device ID.  Oh well.  */
     pci_config_set_device_id(d->config, PCI_DEVICE_ID_XILINX_XC2VP30);
-    d->config[0x04] = 0x00;
-    d->config[0x05] = 0x00;
-    d->config[0x06] = 0x20;
-    d->config[0x07] = 0x02;
-    d->config[0x08] = 0x00; // revision
-    d->config[0x09] = 0x00; // programming i/f
+    pci_set_word(d->config + PCI_STATUS,
+		 PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
     pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_CO);
-    d->config[0x0D] = 0x10; // latency_timer
+    pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
     return 0;
 }
 
diff --git a/qemu-common.h b/qemu-common.h
index a98fccd..fc32d8d 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -227,6 +227,8 @@
 typedef struct DeviceState DeviceState;
 typedef struct SSIBus SSIBus;
 
+typedef uint64_t pcibus_t;
+
 /* CPU save/load.  */
 void cpu_save(QEMUFile *f, void *opaque);
 int cpu_load(QEMUFile *f, void *opaque, int version_id);
diff --git a/rwhandler.c b/rwhandler.c
new file mode 100644
index 0000000..c50c4bf
--- /dev/null
+++ b/rwhandler.c
@@ -0,0 +1,91 @@
+#include "rwhandler.h"
+#include "ioport.h"
+#include "cpu-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define RWHANDLER_WRITE(name, len, type) \
+static void name(void *opaque, type addr, uint32_t value) \
+{\
+    struct ReadWriteHandler *handler = opaque;\
+    handler->write(handler, addr, value, len);\
+}
+
+#define RWHANDLER_READ(name, len, type) \
+static uint32_t name(void *opaque, type addr) \
+{ \
+    struct ReadWriteHandler *handler = opaque; \
+    return handler->read(handler, addr, len); \
+}
+
+RWHANDLER_WRITE(cpu_io_memory_simple_writeb, 1, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readb, 1, target_phys_addr_t);
+RWHANDLER_WRITE(cpu_io_memory_simple_writew, 2, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readw, 2, target_phys_addr_t);
+RWHANDLER_WRITE(cpu_io_memory_simple_writel, 4, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readl, 4, target_phys_addr_t);
+
+static CPUWriteMemoryFunc * const cpu_io_memory_simple_write[] = {
+    &cpu_io_memory_simple_writeb,
+    &cpu_io_memory_simple_writew,
+    &cpu_io_memory_simple_writel,
+};
+
+static CPUReadMemoryFunc * const cpu_io_memory_simple_read[] = {
+    &cpu_io_memory_simple_readb,
+    &cpu_io_memory_simple_readw,
+    &cpu_io_memory_simple_readl,
+};
+
+int cpu_register_io_memory_simple(struct ReadWriteHandler *handler)
+{
+    if (!handler->read || !handler->write) {
+        return -1;
+    }
+    return cpu_register_io_memory(cpu_io_memory_simple_read,
+                                  cpu_io_memory_simple_write,
+                                  handler);
+}
+
+RWHANDLER_WRITE(ioport_simple_writeb, 1, uint32_t);
+RWHANDLER_READ(ioport_simple_readb, 1, uint32_t);
+RWHANDLER_WRITE(ioport_simple_writew, 2, uint32_t);
+RWHANDLER_READ(ioport_simple_readw, 2, uint32_t);
+RWHANDLER_WRITE(ioport_simple_writel, 4, uint32_t);
+RWHANDLER_READ(ioport_simple_readl, 4, uint32_t);
+
+int register_ioport_simple(ReadWriteHandler* handler,
+                           pio_addr_t start, int length, int size)
+{
+    IOPortWriteFunc *write;
+    IOPortReadFunc *read;
+    int r;
+    switch (size) {
+    case 1:
+        write = ioport_simple_writeb;
+        read = ioport_simple_readb;
+        break;
+    case 2:
+        write = ioport_simple_writew;
+        read = ioport_simple_readw;
+        break;
+    default:
+        write = ioport_simple_writel;
+        read = ioport_simple_readl;
+    }
+    if (handler->write) {
+        r = register_ioport_write(start, length, size, write, handler);
+        if (r < 0) {
+            return r;
+        }
+    }
+    if (handler->read) {
+        r = register_ioport_read(start, length, size, read, handler);
+        if (r < 0) {
+            return r;
+        }
+    }
+    return 0;
+}
+
+#endif
diff --git a/rwhandler.h b/rwhandler.h
new file mode 100644
index 0000000..bc11849
--- /dev/null
+++ b/rwhandler.h
@@ -0,0 +1,27 @@
+#ifndef READ_WRITE_HANDLER_H
+#define READ_WRITE_HANDLER_H
+
+#include "qemu-common.h"
+#include "ioport.h"
+
+typedef struct ReadWriteHandler ReadWriteHandler;
+
+/* len is guaranteed to be one of 1, 2 or 4, addr is guaranteed to fit in an
+ * appropriate type (io/memory/etc). They do not need to be range checked. */
+typedef void WriteHandlerFunc(ReadWriteHandler *, pcibus_t addr,
+                              uint32_t value, int len);
+typedef uint32_t ReadHandlerFunc(ReadWriteHandler *, pcibus_t addr, int len);
+
+struct ReadWriteHandler {
+    WriteHandlerFunc *write;
+    ReadHandlerFunc *read;
+};
+
+/* Helpers for when we want to use a single routine with length. */
+/* CPU memory handler: both read and write must be present. */
+int cpu_register_io_memory_simple(ReadWriteHandler *);
+/* io port handler: can supply only read or write handlers. */
+int register_ioport_simple(ReadWriteHandler *,
+                           pio_addr_t start, int length, int size);
+
+#endif
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index a4fae31..cd1c9fe 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -736,14 +736,13 @@
                     PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
         if (slb_is_valid(slb)) {
             /* SLB entry is valid */
+            mask = 0xFFFFFFFFF0000000ULL;
             if (slb->tmp & 0x8) {
-                /* 1 TB Segment */
-                mask = 0xFFFF000000000000ULL;
+                /* 16 MB PTEs */
                 if (target_page_bits)
-                    *target_page_bits = 24; // XXX 16M pages?
+                    *target_page_bits = 24;
             } else {
-                /* 256MB Segment */
-                mask = 0xFFFFFFFFF0000000ULL;
+                /* 4 KB PTEs */
                 if (target_page_bits)
                     *target_page_bits = TARGET_PAGE_BITS;
             }
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index 0424a78..8ad0037 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -37,6 +37,22 @@
     do { } while (0)
 #endif
 
+/* XXX For some odd reason we sometimes hang inside KVM forever. I'd guess it's
+ *     a race condition where we actually have a level triggered interrupt, but
+ *     the infrastructure can't expose that yet, so the guest ACKs it, goes to
+ *     sleep and never gets notified that there's still an interrupt pending.
+ *
+ *     As a quick workaround, let's just wake up every 500 ms. That way we can
+ *     assure that we're always reinjecting interrupts in time.
+ */
+static QEMUTimer *idle_timer;
+
+static void do_nothing(void *opaque)
+{
+    qemu_mod_timer(idle_timer, qemu_get_clock(vm_clock) +
+                   (get_ticks_per_sec() / 2));
+}
+
 int kvm_arch_init(KVMState *s, int smp_cpus)
 {
     return 0;
@@ -173,6 +189,12 @@
     int r;
     unsigned irq;
 
+    if (!idle_timer) {
+        idle_timer = qemu_new_timer(vm_clock, do_nothing, NULL);
+        qemu_mod_timer(idle_timer, qemu_get_clock(vm_clock) +
+                       (get_ticks_per_sec() / 2));
+    }
+
     /* PowerPC Qemu tracks the various core input pins (interrupt, critical
      * interrupt, reset, etc) in PPC-specific env->irq_input_state. */
     if (run->ready_for_interrupt_injection &&
@@ -252,3 +274,50 @@
     return ret;
 }
 
+static int read_cpuinfo(const char *field, char *value, int len)
+{
+    FILE *f;
+    int ret = -1;
+    int field_len = strlen(field);
+    char line[512];
+
+    f = fopen("/proc/cpuinfo", "r");
+    if (!f) {
+        return -1;
+    }
+
+    do {
+        if(!fgets(line, sizeof(line), f)) {
+            break;
+        }
+        if (!strncmp(line, field, field_len)) {
+            strncpy(value, line, len);
+            ret = 0;
+            break;
+        }
+    } while(*line);
+
+    fclose(f);
+
+    return ret;
+}
+
+uint32_t kvmppc_get_tbfreq(void)
+{
+    char line[512];
+    char *ns;
+    uint32_t retval = get_ticks_per_sec();
+
+    if (read_cpuinfo("timebase", line, sizeof(line))) {
+        return retval;
+    }
+
+    if (!(ns = strchr(line, ':'))) {
+        return retval;
+    }
+
+    ns++;
+
+    retval = atoi(ns);
+    return retval;
+}
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index 3792ef7..e8d66e8 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -14,4 +14,6 @@
 int kvmppc_read_host_property(const char *node_path, const char *prop,
                                      void *val, size_t len);
 
+uint32_t kvmppc_get_tbfreq(void);
+
 #endif /* __KVM_PPC_H__ */