summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--Makefile.target5
-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/microblaze_boot.c177
-rw-r--r--hw/microblaze_boot.h10
-rw-r--r--hw/petalogix_ml605_mmu.c144
-rw-r--r--hw/petalogix_s3adsp1800_mmu.c147
-rw-r--r--hw/xilinx_zynq.c157
-rw-r--r--hw/zynq_slcr.c535
-rw-r--r--libcacard/vcardt.h4
-rw-r--r--qemu-options.hx2
13 files changed, 3161 insertions, 262 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 0b3b3d8dc6..d249947d12 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -251,6 +251,13 @@ M: Peter Maydell <peter.maydell@linaro.org>
 S: Maintained
 F: hw/versatilepb.c
 
+Xilinx Zynq
+M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
+S: Maintained
+F: hw/xilinx_zynq.c
+F: hw/zynq_slcr.c
+F: hw/cadence_*
+
 CRIS Machines
 -------------
 Axis Dev88
diff --git a/Makefile.target b/Makefile.target
index 343eb78494..5f3fc40fe5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -307,6 +307,7 @@ obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
 
 obj-microblaze-y = petalogix_s3adsp1800_mmu.o
 obj-microblaze-y += petalogix_ml605_mmu.o
+obj-microblaze-y += microblaze_boot.o
 
 obj-microblaze-y += microblaze_pic_cpu.o
 obj-microblaze-y += xilinx_intc.o
@@ -345,6 +346,10 @@ endif
 obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
+obj-arm-y += cadence_uart.o
+obj-arm-y += cadence_ttc.o
+obj-arm-y += cadence_gem.o
+obj-arm-y += xilinx_zynq.o zynq_slcr.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
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/microblaze_boot.c b/hw/microblaze_boot.c
new file mode 100644
index 0000000000..b2f96df344
--- /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)(CPUState *);
+    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;
+    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(CPUState *env, target_phys_addr_t ddr_base,
+                            uint32_t ramsize, const char *dtb_filename,
+                                  void (*machine_cpu_reset)(CPUState *))
+{
+
+    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..69d4ac635b
--- /dev/null
+++ b/hw/microblaze_boot.h
@@ -0,0 +1,10 @@
+#ifndef __MICROBLAZE_BOOT__
+#define __MICROBLAZE_BOOT__
+
+#include "hw.h"
+
+void microblaze_load_kernel(CPUState *env, target_phys_addr_t ddr_base,
+                            uint32_t ramsize, const char *dtb_filename,
+                                  void (*machine_cpu_reset)(CPUState *));
+
+#endif /* __MICROBLAZE_BOOT __ */
diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c
index 98978f89b6..c87fa1186c 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(CPUState *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,
@@ -145,7 +76,6 @@ petalogix_ml605_init(ram_addr_t ram_size,
     MemoryRegion *address_space_mem = get_system_memory();
     DeviceState *dev;
     CPUState *env;
-    int kernel_size;
     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..2cb0b1f10a 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(CPUState *env)
 {
-    return addr - 0x30000000LL;
+    /* FIXME: move to machine specfic cpu reset */
+    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
 }
 
 static void
@@ -122,10 +64,9 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size,
 {
     DeviceState *dev;
     CPUState *env;
-    int kernel_size;
     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/xilinx_zynq.c b/hw/xilinx_zynq.c
new file mode 100644
index 0000000000..ea13e8c844
--- /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)
+{
+    CPUState *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/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)
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
index d4d8e2ed18..d3e9522774 100644
--- a/libcacard/vcardt.h
+++ b/libcacard/vcardt.h
@@ -26,8 +26,8 @@ typedef struct VCardEmulStruct VCardEmul;
 #define MAX_CHANNEL 4
 
 /* create an ATR with appropriate historical bytes */
-#define VCARD_ATR_PREFIX(size) (0x3b, 0x68+(size), 0x00, 0xff, \
-                               'V', 'C', 'A', 'R', 'D', '_')
+#define VCARD_ATR_PREFIX(size) 0x3b, 0x68+(size), 0x00, 0xff, \
+                               'V', 'C', 'A', 'R', 'D', '_'
 
 
 typedef enum {
diff --git a/qemu-options.hx b/qemu-options.hx
index e38799c648..daefce3038 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2038,7 +2038,7 @@ first module.
 ETEXI
 
 DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \
-    "-dtb    file    use 'file' as device tree image\n", QEMU_ARCH_ARM)
+    "-dtb    file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
 STEXI
 @item -dtb @var{file}
 @findex -dtb