summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--docs/memory.txt9
-rw-r--r--hw/arm/ast2400.c40
-rw-r--r--hw/arm/fsl-imx25.c8
-rw-r--r--hw/arm/fsl-imx31.c9
-rw-r--r--hw/arm/fsl-imx6.c8
-rw-r--r--hw/arm/palmetto-bmc.c31
-rw-r--r--hw/arm/sabrelite.c18
-rw-r--r--hw/arm/spitz.c12
-rw-r--r--hw/arm/tosa.c5
-rw-r--r--hw/arm/virt.c1
-rw-r--r--hw/arm/xilinx_zynq.c14
-rw-r--r--hw/arm/xlnx-ep108.c9
-rw-r--r--hw/arm/z2.c6
-rw-r--r--hw/block/m25p80.c76
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/register.c287
-rw-r--r--hw/display/ads7846.c5
-rw-r--r--hw/display/ssd0323.c5
-rw-r--r--hw/dma/Makefile.objs1
-rw-r--r--hw/dma/xlnx-zynq-devcfg.c400
-rw-r--r--hw/intc/armv7m_nvic.c8
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c9
-rw-r--r--hw/misc/max111x.c12
-rw-r--r--hw/sd/ssi-sd.c9
-rw-r--r--hw/ssi/Makefile.objs1
-rw-r--r--hw/ssi/aspeed_smc.c470
-rw-r--r--hw/ssi/ssi.c6
-rw-r--r--include/exec/memory.h24
-rw-r--r--include/hw/arm/ast2400.h3
-rw-r--r--include/hw/dma/xlnx-zynq-devcfg.h62
-rw-r--r--include/hw/register.h255
-rw-r--r--include/hw/ssi/aspeed_smc.h100
-rw-r--r--include/hw/ssi/ssi.h2
-rw-r--r--include/qemu/bitops.h3
-rw-r--r--linux-user/qemu.h6
-rw-r--r--memory.c16
-rw-r--r--pc-bios/bios-256k.binbin262144 -> 262144 bytes
-rw-r--r--pc-bios/bios-fast.binbin0 -> 65536 bytes
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--roms/Makefile4
-rw-r--r--roms/config.seabios-128k2
-rw-r--r--roms/config.seabios-fast27
m---------roms/seabios0
-rw-r--r--slirp/Makefile.objs2
-rw-r--r--slirp/dhcpv6.c209
-rw-r--r--slirp/dhcpv6.h22
-rw-r--r--slirp/ip6.h9
-rw-r--r--slirp/ip6_icmp.c27
-rw-r--r--slirp/ip6_icmp.h12
-rw-r--r--slirp/libslirp.h1
-rw-r--r--slirp/slirp.c126
-rw-r--r--slirp/socket.c7
-rw-r--r--slirp/tftp.c4
-rw-r--r--slirp/udp6.c13
-rw-r--r--target-arm/arm-semi.c47
57 files changed, 2273 insertions, 174 deletions
diff --git a/Makefile b/Makefile
index 9d12dc614f..7bfb8b4e90 100644
--- a/Makefile
+++ b/Makefile
@@ -408,7 +408,8 @@ common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \
 bepo    cz
 
 ifdef INSTALL_BLOBS
-BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
+BLOBS=bios.bin bios-256k.bin bios-fast.bin \
+sgabios.bin vgabios.bin vgabios-cirrus.bin \
 vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
 acpi-dsdt.aml \
 ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index c5bcba754a..7a19863b18 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -66,6 +66,7 @@ CONFIG_PXA2XX=y
 CONFIG_BITBANG_I2C=y
 CONFIG_FRAMEBUFFER=y
 CONFIG_XILINX_SPIPS=y
+CONFIG_ZYNQ_DEVCFG=y
 
 CONFIG_ARM11SCU=y
 CONFIG_A9SCU=y
diff --git a/docs/memory.txt b/docs/memory.txt
index 431d9ca88f..811b1bd3c5 100644
--- a/docs/memory.txt
+++ b/docs/memory.txt
@@ -41,8 +41,13 @@ MemoryRegion):
   MemoryRegionOps structure describing the callbacks.
 
 - ROM: a ROM memory region works like RAM for reads (directly accessing
-  a region of host memory), but like MMIO for writes (invoking a callback).
-  You initialize these with memory_region_init_rom_device().
+  a region of host memory), and forbids writes. You initialize these with
+  memory_region_init_rom().
+
+- ROM device: a ROM device memory region works like RAM for reads
+  (directly accessing a region of host memory), but like MMIO for
+  writes (invoking a callback).  You initialize these with
+  memory_region_init_rom_device().
 
 - IOMMU region: an IOMMU region translates addresses of accesses made to it
   and forwards them to some other target memory region.  As the name suggests,
diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index b14a82fcde..0555843620 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -23,11 +23,17 @@
 #define AST2400_UART_5_BASE      0x00184000
 #define AST2400_IOMEM_SIZE       0x00200000
 #define AST2400_IOMEM_BASE       0x1E600000
+#define AST2400_SMC_BASE         AST2400_IOMEM_BASE /* Legacy SMC */
+#define AST2400_FMC_BASE         0X1E620000
+#define AST2400_SPI_BASE         0X1E630000
 #define AST2400_VIC_BASE         0x1E6C0000
 #define AST2400_SCU_BASE         0x1E6E2000
 #define AST2400_TIMER_BASE       0x1E782000
 #define AST2400_I2C_BASE         0x1E78A000
 
+#define AST2400_FMC_FLASH_BASE   0x20000000
+#define AST2400_SPI_FLASH_BASE   0x30000000
+
 #define AST2400_A0_SILICON_REV   0x02000303
 
 static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
@@ -85,13 +91,21 @@ static void ast2400_init(Object *obj)
                               "hw-strap1", &error_abort);
     object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
                               "hw-strap2", &error_abort);
+
+    object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc");
+    object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
+    qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
+
+    object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi");
+    object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
+    qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
 }
 
 static void ast2400_realize(DeviceState *dev, Error **errp)
 {
     int i;
     AST2400State *s = AST2400(dev);
-    Error *err = NULL;
+    Error *err = NULL, *local_err = NULL;
 
     /* IO space */
     memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
@@ -147,6 +161,30 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, AST2400_I2C_BASE);
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
                        qdev_get_gpio_in(DEVICE(&s->vic), 12));
+
+    /* SMC */
+    object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
+    object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err);
+    error_propagate(&err, local_err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
+                       qdev_get_gpio_in(DEVICE(&s->vic), 19));
+
+    /* SPI */
+    object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
+    object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err);
+    error_propagate(&err, local_err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE);
 }
 
 static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 1cd749aa4b..1a53e51cf1 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -249,16 +249,16 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
     }
 
     /* initialize 2 x 16 KB ROM */
-    memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL,
-                                  "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
+    memory_region_init_rom(&s->rom[0], NULL,
+                           "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
     }
     memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR,
                                 &s->rom[0]);
-    memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL,
-                                  "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
+    memory_region_init_rom(&s->rom[1], NULL,
+                           "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index 31a3a87911..b283b71eb4 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -219,9 +219,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
     }
 
     /* On a real system, the first 16k is a `secure boot rom' */
-    memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL,
-                                  "imx31.secure_rom",
-                                  FSL_IMX31_SECURE_ROM_SIZE, &err);
+    memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom",
+                           FSL_IMX31_SECURE_ROM_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -230,8 +229,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
                                 &s->secure_rom);
 
     /* There is also a 16k ROM */
-    memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom",
-                                  FSL_IMX31_ROM_SIZE, &err);
+    memory_region_init_rom(&s->rom, NULL, "imx31.rom",
+                           FSL_IMX31_ROM_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index 0c00e7a560..ed392a9e7f 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -399,8 +399,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
                                         FSL_IMX6_ENET_MAC_1588_IRQ));
 
     /* ROM memory */
-    memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom",
-                                  FSL_IMX6_ROM_SIZE, &err);
+    memory_region_init_rom(&s->rom, NULL, "imx6.rom",
+                           FSL_IMX6_ROM_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -409,8 +409,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
                                 &s->rom);
 
     /* CAAM memory */
-    memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam",
-                                  FSL_IMX6_CAAM_MEM_SIZE, &err);
+    memory_region_init_rom(&s->caam, NULL, "imx6.caam",
+                           FSL_IMX6_CAAM_MEM_SIZE, &err);
     if (err) {
         error_propagate(errp, err);
         return;
diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
index b8eed21348..54e29a865d 100644
--- a/hw/arm/palmetto-bmc.c
+++ b/hw/arm/palmetto-bmc.c
@@ -18,6 +18,8 @@
 #include "hw/arm/ast2400.h"
 #include "hw/boards.h"
 #include "qemu/log.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/blockdev.h"
 
 static struct arm_boot_info palmetto_bmc_binfo = {
     .loader_start = AST2400_SDRAM_BASE,
@@ -30,6 +32,32 @@ typedef struct PalmettoBMCState {
     MemoryRegion ram;
 } PalmettoBMCState;
 
+static void palmetto_bmc_init_flashes(AspeedSMCState *s, const char *flashtype,
+                                      Error **errp)
+{
+    int i ;
+
+    for (i = 0; i < s->num_cs; ++i) {
+        AspeedSMCFlash *fl = &s->flashes[i];
+        DriveInfo *dinfo = drive_get_next(IF_MTD);
+        qemu_irq cs_line;
+
+        /*
+         * FIXME: check that we are not using a flash module exceeding
+         * the controller segment size
+         */
+        fl->flash = ssi_create_slave_no_init(s->spi, flashtype);
+        if (dinfo) {
+            qdev_prop_set_drive(fl->flash, "drive", blk_by_legacy_dinfo(dinfo),
+                                errp);
+        }
+        qdev_init_nofail(fl->flash);
+
+        cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
+        sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
+    }
+}
+
 static void palmetto_bmc_init(MachineState *machine)
 {
     PalmettoBMCState *bmc;
@@ -49,6 +77,9 @@ static void palmetto_bmc_init(MachineState *machine)
     object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
                              &error_abort);
 
+    palmetto_bmc_init_flashes(&bmc->soc.smc, "n25q256a", &error_abort);
+    palmetto_bmc_init_flashes(&bmc->soc.spi, "mx25l25635e", &error_abort);
+
     palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
     palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
     palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index 776c51e398..4e7ac8cc4f 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -86,13 +86,19 @@ static void sabrelite_init(MachineState *machine)
             spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi");
             if (spi_bus) {
                 DeviceState *flash_dev;
-
-                flash_dev = ssi_create_slave(spi_bus, "sst25vf016b");
-                if (flash_dev) {
-                    qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev,
-                                                              SSI_GPIO_CS, 0);
-                    sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line);
+                qemu_irq cs_line;
+                DriveInfo *dinfo = drive_get_next(IF_MTD);
+
+                flash_dev = ssi_create_slave_no_init(spi_bus, "sst25vf016b");
+                if (dinfo) {
+                    qdev_prop_set_drive(flash_dev, "drive",
+                                        blk_by_legacy_dinfo(dinfo),
+                                        &error_fatal);
                 }
+                qdev_init_nofail(flash_dev);
+
+                cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
+                sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line);
             }
         }
     }
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index ba40f8302b..41cc2eeeb1 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -598,15 +598,13 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value)
     return 0;
 }
 
-static int spitz_lcdtg_init(SSISlave *dev)
+static void spitz_lcdtg_realize(SSISlave *dev, Error **errp)
 {
     SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
 
     spitz_lcdtg = s;
     s->bl_power = 0;
     s->bl_intensity = 0x20;
-
-    return 0;
 }
 
 /* SSP devices */
@@ -666,7 +664,7 @@ static void spitz_adc_temp_on(void *opaque, int line, int level)
         max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
 }
 
-static int corgi_ssp_init(SSISlave *d)
+static void corgi_ssp_realize(SSISlave *d, Error **errp)
 {
     DeviceState *dev = DEVICE(d);
     CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d);
@@ -675,8 +673,6 @@ static int corgi_ssp_init(SSISlave *d)
     s->bus[0] = ssi_create_bus(dev, "ssi0");
     s->bus[1] = ssi_create_bus(dev, "ssi1");
     s->bus[2] = ssi_create_bus(dev, "ssi2");
-
-    return 0;
 }
 
 static void spitz_ssp_attach(PXA2xxState *cpu)
@@ -1121,7 +1117,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = corgi_ssp_init;
+    k->realize = corgi_ssp_realize;
     k->transfer = corgi_ssp_transfer;
     dc->vmsd = &vmstate_corgi_ssp_regs;
 }
@@ -1150,7 +1146,7 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = spitz_lcdtg_init;
+    k->realize = spitz_lcdtg_realize;
     k->transfer = spitz_lcdtg_transfer;
     dc->vmsd = &vmstate_spitz_lcdtg_regs;
 }
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 4e9494f94c..2db66508b5 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -127,10 +127,9 @@ static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value)
     return 0;
 }
 
-static int tosa_ssp_init(SSISlave *dev)
+static void tosa_ssp_realize(SSISlave *dev, Error **errp)
 {
     /* Nothing to do.  */
-    return 0;
 }
 
 #define TYPE_TOSA_DAC "tosa_dac"
@@ -283,7 +282,7 @@ static void tosa_ssp_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = tosa_ssp_init;
+    k->realize = tosa_ssp_realize;
     k->transfer = tosa_ssp_tansfer;
 }
 
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c5c125e920..6e098afd1f 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1021,6 +1021,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
     qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2);
     qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
                            nr_pcie_buses - 1);
+    qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0);
 
     if (vbi->v2m_phandle) {
         qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent",
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index aefebcfa6d..7dac20d67d 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -138,7 +138,13 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
         spi = (SSIBus *)qdev_get_child_bus(dev, bus_name);
 
         for (j = 0; j < num_ss; ++j) {
-            flash_dev = ssi_create_slave(spi, "n25q128");
+            DriveInfo *dinfo = drive_get_next(IF_MTD);
+            flash_dev = ssi_create_slave_no_init(spi, "n25q128");
+            if (dinfo) {
+                qdev_prop_set_drive(flash_dev, "drive",
+                                    blk_by_legacy_dinfo(dinfo), &error_fatal);
+            }
+            qdev_init_nofail(flash_dev);
 
             cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
             sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line);
@@ -294,6 +300,12 @@ static void zynq_init(MachineState *machine)
         sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]);
     }
 
+    dev = qdev_create(NULL, "xlnx.ps7-dev-cfg");
+    qdev_init_nofail(dev);
+    busdev = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
+    sysbus_mmio_map(busdev, 0, 0xF8007000);
+
     zynq_binfo.ram_size = ram_size;
     zynq_binfo.kernel_filename = kernel_filename;
     zynq_binfo.kernel_cmdline = kernel_cmdline;
diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c
index 34b4641712..4ec590a25d 100644
--- a/hw/arm/xlnx-ep108.c
+++ b/hw/arm/xlnx-ep108.c
@@ -88,12 +88,19 @@ static void xlnx_ep108_init(MachineState *machine)
         SSIBus *spi_bus;
         DeviceState *flash_dev;
         qemu_irq cs_line;
+        DriveInfo *dinfo = drive_get_next(IF_MTD);
         gchar *bus_name = g_strdup_printf("spi%d", i);
 
         spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
         g_free(bus_name);
 
-        flash_dev = ssi_create_slave(spi_bus, "sst25wf080");
+        flash_dev = ssi_create_slave_no_init(spi_bus, "sst25wf080");
+        if (dinfo) {
+            qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo),
+                                &error_fatal);
+        }
+        qdev_init_nofail(flash_dev);
+
         cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
 
         sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index aea895a500..68a92f3184 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -151,14 +151,12 @@ static void z2_lcd_cs(void *opaque, int line, int level)
     z2_lcd->selected = !level;
 }
 
-static int zipit_lcd_init(SSISlave *dev)
+static void zipit_lcd_realize(SSISlave *dev, Error **errp)
 {
     ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
     z->selected = 0;
     z->enabled = 0;
     z->pos = 0;
-
-    return 0;
 }
 
 static VMStateDescription vmstate_zipit_lcd_state = {
@@ -181,7 +179,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = zipit_lcd_init;
+    k->realize = zipit_lcd_realize;
     k->transfer = zipit_lcd_transfer;
     dc->vmsd = &vmstate_zipit_lcd_state;
 }
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 326b688e83..d9b27939dd 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -28,6 +28,7 @@
 #include "hw/ssi/ssi.h"
 #include "qemu/bitops.h"
 #include "qemu/log.h"
+#include "qapi/error.h"
 
 #ifndef M25P80_ERR_DEBUG
 #define M25P80_ERR_DEBUG 0
@@ -389,7 +390,7 @@ typedef struct Flash {
     uint32_t pos;
     uint8_t needed_bytes;
     uint8_t cmd_in_progress;
-    uint64_t cur_addr;
+    uint32_t cur_addr;
     uint32_t nonvolatile_cfg;
     /* Configuration register for Macronix */
     uint32_t volatile_cfg;
@@ -446,6 +447,11 @@ static inline Manufacturer get_man(Flash *s)
 
 static void blk_sync_complete(void *opaque, int ret)
 {
+    QEMUIOVector *iov = opaque;
+
+    qemu_iovec_destroy(iov);
+    g_free(iov);
+
     /* do nothing. Masters do not directly interact with the backing store,
      * only the working copy so no mutexing required.
      */
@@ -453,31 +459,31 @@ static void blk_sync_complete(void *opaque, int ret)
 
 static void flash_sync_page(Flash *s, int page)
 {
-    QEMUIOVector iov;
+    QEMUIOVector *iov = g_new(QEMUIOVector, 1);
 
     if (!s->blk || blk_is_read_only(s->blk)) {
         return;
     }
 
-    qemu_iovec_init(&iov, 1);
-    qemu_iovec_add(&iov, s->storage + page * s->pi->page_size,
+    qemu_iovec_init(iov, 1);
+    qemu_iovec_add(iov, s->storage + page * s->pi->page_size,
                    s->pi->page_size);
-    blk_aio_pwritev(s->blk, page * s->pi->page_size, &iov, 0,
-                    blk_sync_complete, NULL);
+    blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0,
+                    blk_sync_complete, iov);
 }
 
 static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
 {
-    QEMUIOVector iov;
+    QEMUIOVector *iov = g_new(QEMUIOVector, 1);
 
     if (!s->blk || blk_is_read_only(s->blk)) {
         return;
     }
 
     assert(!(len % BDRV_SECTOR_SIZE));
-    qemu_iovec_init(&iov, 1);
-    qemu_iovec_add(&iov, s->storage + off, len);
-    blk_aio_pwritev(s->blk, off, &iov, 0, blk_sync_complete, NULL);
+    qemu_iovec_init(iov, 1);
+    qemu_iovec_add(iov, s->storage + off, len);
+    blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov);
 }
 
 static void flash_erase(Flash *s, int offset, FlashCMD cmd)
@@ -530,9 +536,9 @@ static inline void flash_sync_dirty(Flash *s, int64_t newpage)
 }
 
 static inline
-void flash_write8(Flash *s, uint64_t addr, uint8_t data)
+void flash_write8(Flash *s, uint32_t addr, uint8_t data)
 {
-    int64_t page = addr / s->pi->page_size;
+    uint32_t page = addr / s->pi->page_size;
     uint8_t prev = s->storage[s->cur_addr];
 
     if (!s->write_enable) {
@@ -540,7 +546,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
     }
 
     if ((prev ^ data) & data) {
-        DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 "  %" PRIx8
+        DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 "  %" PRIx8
                    " -> %" PRIx8 "\n", addr, prev, data);
     }
 
@@ -581,18 +587,16 @@ static inline int get_addr_length(Flash *s)
 
 static void complete_collecting_data(Flash *s)
 {
-    int i;
-
-    s->cur_addr = 0;
+    int i, n;
 
-    for (i = 0; i < get_addr_length(s); ++i) {
+    n = get_addr_length(s);
+    s->cur_addr = (n == 3 ? s->ear : 0);
+    for (i = 0; i < n; ++i) {
         s->cur_addr <<= 8;
         s->cur_addr |= s->data[i];
     }
 
-    if (get_addr_length(s) == 3) {
-        s->cur_addr += s->ear * MAX_3BYTES_SIZE;
-    }
+    s->cur_addr &= s->size - 1;
 
     s->state = STATE_IDLE;
 
@@ -1091,17 +1095,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
     switch (s->state) {
 
     case STATE_PAGE_PROGRAM:
-        DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n",
+        DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n",
                    s->cur_addr, (uint8_t)tx);
         flash_write8(s, s->cur_addr, (uint8_t)tx);
-        s->cur_addr++;
+        s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
         break;
 
     case STATE_READ:
         r = s->storage[s->cur_addr];
-        DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr,
+        DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr,
                    (uint8_t)r);
-        s->cur_addr = (s->cur_addr + 1) % s->size;
+        s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
         break;
 
     case STATE_COLLECTING_DATA:
@@ -1132,9 +1136,8 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
     return r;
 }
 
-static int m25p80_init(SSISlave *ss)
+static void m25p80_realize(SSISlave *ss, Error **errp)
 {
-    DriveInfo *dinfo;
     Flash *s = M25P80(ss);
     M25P80Class *mc = M25P80_GET_CLASS(s);
 
@@ -1143,28 +1146,19 @@ static int m25p80_init(SSISlave *ss)
     s->size = s->pi->sector_size * s->pi->n_sectors;
     s->dirty_page = -1;
 
-    /* FIXME use a qdev drive property instead of drive_get_next() */
-    dinfo = drive_get_next(IF_MTD);
-
-    if (dinfo) {
+    if (s->blk) {
         DB_PRINT_L(0, "Binding to IF_MTD drive\n");
-        s->blk = blk_by_legacy_dinfo(dinfo);
-        blk_attach_dev_nofail(s->blk, s);
-
         s->storage = blk_blockalign(s->blk, s->size);
 
-        /* FIXME: Move to late init */
         if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) {
-            fprintf(stderr, "Failed to initialize SPI flash!\n");
-            return 1;
+            error_setg(errp, "failed to read the initial flash content");
+            return;
         }
     } else {
         DB_PRINT_L(0, "No BDRV - binding to RAM\n");
         s->storage = blk_blockalign(NULL, s->size);
         memset(s->storage, 0xFF, s->size);
     }
-
-    return 0;
 }
 
 static void m25p80_reset(DeviceState *d)
@@ -1186,6 +1180,7 @@ static Property m25p80_properties[] = {
     DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8),
     DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2),
     DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10),
+    DEFINE_PROP_DRIVE("drive", Flash, blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -1201,7 +1196,8 @@ static const VMStateDescription vmstate_m25p80 = {
         VMSTATE_UINT32(pos, Flash),
         VMSTATE_UINT8(needed_bytes, Flash),
         VMSTATE_UINT8(cmd_in_progress, Flash),
-        VMSTATE_UINT64(cur_addr, Flash),
+        VMSTATE_UNUSED(4),
+        VMSTATE_UINT32(cur_addr, Flash),
         VMSTATE_BOOL(write_enable, Flash),
         VMSTATE_BOOL_V(reset_enable, Flash, 2),
         VMSTATE_UINT8_V(ear, Flash, 2),
@@ -1224,7 +1220,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data)
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
     M25P80Class *mc = M25P80_CLASS(klass);
 
-    k->init = m25p80_init;
+    k->realize = m25p80_realize;
     k->transfer = m25p80_transfer8;
     k->set_cs = m25p80_cs;
     k->cs_polarity = SSI_CS_LOW;
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 82a9ef84f8..cfd4840397 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -15,4 +15,5 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o
 common-obj-$(CONFIG_SOFTMMU) += null-machine.o
 common-obj-$(CONFIG_SOFTMMU) += loader.o
 common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+common-obj-$(CONFIG_SOFTMMU) += register.o
 common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
diff --git a/hw/core/register.c b/hw/core/register.c
new file mode 100644
index 0000000000..4bfbc508de
--- /dev/null
+++ b/hw/core/register.c
@@ -0,0 +1,287 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/register.h"
+#include "hw/qdev.h"
+#include "qemu/log.h"
+
+static inline void register_write_val(RegisterInfo *reg, uint64_t val)
+{
+    g_assert(reg->data);
+
+    switch (reg->data_size) {
+    case 1:
+        *(uint8_t *)reg->data = val;
+        break;
+    case 2:
+        *(uint16_t *)reg->data = val;
+        break;
+    case 4:
+        *(uint32_t *)reg->data = val;
+        break;
+    case 8:
+        *(uint64_t *)reg->data = val;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static inline uint64_t register_read_val(RegisterInfo *reg)
+{
+    switch (reg->data_size) {
+    case 1:
+        return *(uint8_t *)reg->data;
+    case 2:
+        return *(uint16_t *)reg->data;
+    case 4:
+        return *(uint32_t *)reg->data;
+    case 8:
+        return *(uint64_t *)reg->data;
+    default:
+        g_assert_not_reached();
+    }
+    return 0; /* unreachable */
+}
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we,
+                    const char *prefix, bool debug)
+{
+    uint64_t old_val, new_val, test, no_w_mask;
+    const RegisterAccessInfo *ac;
+
+    assert(reg);
+
+    ac = reg->access;
+
+    if (!ac || !ac->name) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state "
+                      "(written value: %#" PRIx64 ")\n", prefix, val);
+        return;
+    }
+
+    old_val = reg->data ? register_read_val(reg) : ac->reset;
+
+    test = (old_val ^ val) & ac->rsvd;
+    if (test) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit"
+                      "fields: %#" PRIx64 ")\n", prefix, test);
+    }
+
+    test = val & ac->unimp;
+    if (test) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s:%s writing %#" PRIx64 " to unimplemented bits:" \
+                      " %#" PRIx64 "",
+                      prefix, reg->access->name, val, ac->unimp);
+    }
+
+    /* Create the no write mask based on the read only, write to clear and
+     * reserved bit masks.
+     */
+    no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we;
+    new_val = (val & ~no_w_mask) | (old_val & no_w_mask);
+    new_val &= ~(val & ac->w1c);
+
+    if (ac->pre_write) {
+        new_val = ac->pre_write(reg, new_val);
+    }
+
+    if (debug) {
+        qemu_log("%s:%s: write of value %#" PRIx64 "\n", prefix, ac->name,
+                 new_val);
+    }
+
+    register_write_val(reg, new_val);
+
+    if (ac->post_write) {
+        ac->post_write(reg, new_val);
+    }
+}
+
+uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix,
+                       bool debug)
+{
+    uint64_t ret;
+    const RegisterAccessInfo *ac;
+
+    assert(reg);
+
+    ac = reg->access;
+    if (!ac || !ac->name) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n",
+                      prefix);
+        return 0;
+    }
+
+    ret = reg->data ? register_read_val(reg) : ac->reset;
+
+    register_write_val(reg, ret & ~(ac->cor & re));
+
+    /* Mask based on the read enable size */
+    ret &= re;
+
+    if (ac->post_read) {
+        ret = ac->post_read(reg, ret);
+    }
+
+    if (debug) {
+        qemu_log("%s:%s: read of value %#" PRIx64 "\n", prefix,
+                 ac->name, ret);
+    }
+
+    return ret;
+}
+
+void register_reset(RegisterInfo *reg)
+{
+    g_assert(reg);
+
+    if (!reg->data || !reg->access) {
+        return;
+    }
+
+    register_write_val(reg, reg->access->reset);
+}
+
+void register_init(RegisterInfo *reg)
+{
+    assert(reg);
+
+    if (!reg->data || !reg->access) {
+        return;
+    }
+
+    object_initialize((void *)reg, sizeof(*reg), TYPE_REGISTER);
+}
+
+void register_write_memory(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
+{
+    RegisterInfoArray *reg_array = opaque;
+    RegisterInfo *reg = NULL;
+    uint64_t we;
+    int i;
+
+    for (i = 0; i < reg_array->num_elements; i++) {
+        if (reg_array->r[i]->access->addr == addr) {
+            reg = reg_array->r[i];
+            break;
+        }
+    }
+
+    if (!reg) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Write to unimplemented register at " \
+                      "address: %#" PRIx64 "\n", addr);
+        return;
+    }
+
+    /* Generate appropriate write enable mask */
+    if (reg->data_size < size) {
+        we = MAKE_64BIT_MASK(0, reg->data_size * 8);
+    } else {
+        we = MAKE_64BIT_MASK(0, size * 8);
+    }
+
+    register_write(reg, value, we, reg_array->prefix,
+                   reg_array->debug);
+}
+
+uint64_t register_read_memory(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    RegisterInfoArray *reg_array = opaque;
+    RegisterInfo *reg = NULL;
+    uint64_t read_val;
+    int i;
+
+    for (i = 0; i < reg_array->num_elements; i++) {
+        if (reg_array->r[i]->access->addr == addr) {
+            reg = reg_array->r[i];
+            break;
+        }
+    }
+
+    if (!reg) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Read to unimplemented register at " \
+                      "address: %#" PRIx64 "\n", addr);
+        return 0;
+    }
+
+    read_val = register_read(reg, size * 8, reg_array->prefix,
+                             reg_array->debug);
+
+    return extract64(read_val, 0, size * 8);
+}
+
+RegisterInfoArray *register_init_block32(DeviceState *owner,
+                                         const RegisterAccessInfo *rae,
+                                         int num, RegisterInfo *ri,
+                                         uint32_t *data,
+                                         const MemoryRegionOps *ops,
+                                         bool debug_enabled,
+                                         uint64_t memory_size)
+{
+    const char *device_prefix = object_get_typename(OBJECT(owner));
+    RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1);
+    int i;
+
+    r_array->r = g_new0(RegisterInfo *, num);
+    r_array->num_elements = num;
+    r_array->debug = debug_enabled;
+    r_array->prefix = device_prefix;
+
+    for (i = 0; i < num; i++) {
+        int index = rae[i].addr / 4;
+        RegisterInfo *r = &ri[index];
+
+        *r = (RegisterInfo) {
+            .data = &data[index],
+            .data_size = sizeof(uint32_t),
+            .access = &rae[i],
+            .opaque = owner,
+        };
+        register_init(r);
+
+        r_array->r[i] = r;
+    }
+
+    memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array,
+                          device_prefix, memory_size);
+
+    return r_array;
+}
+
+void register_finalize_block(RegisterInfoArray *r_array)
+{
+    object_unparent(OBJECT(&r_array->mem));
+    g_free(r_array->r);
+    g_free(r_array);
+}
+
+static const TypeInfo register_info = {
+    .name  = TYPE_REGISTER,
+    .parent = TYPE_DEVICE,
+};
+
+static void register_register_types(void)
+{
+    type_register_static(&register_info);
+}
+
+type_init(register_register_types)
diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c
index 05aa2d1e6b..166edade7d 100644
--- a/hw/display/ads7846.c
+++ b/hw/display/ads7846.c
@@ -133,7 +133,7 @@ static const VMStateDescription vmstate_ads7846 = {
     }
 };
 
-static int ads7846_init(SSISlave *d)
+static void ads7846_realize(SSISlave *d, Error **errp)
 {
     DeviceState *dev = DEVICE(d);
     ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d);
@@ -152,14 +152,13 @@ static int ads7846_init(SSISlave *d)
     ads7846_int_update(s);
 
     vmstate_register(NULL, -1, &vmstate_ads7846, s);
-    return 0;
 }
 
 static void ads7846_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = ads7846_init;
+    k->realize = ads7846_realize;
     k->transfer = ads7846_transfer;
 }
 
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index 14c1bf339c..6d1faf44af 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -361,7 +361,7 @@ static const GraphicHwOps ssd0323_ops = {
     .gfx_update  = ssd0323_update_display,
 };
 
-static int ssd0323_init(SSISlave *d)
+static void ssd0323_realize(SSISlave *d, Error **errp)
 {
     DeviceState *dev = DEVICE(d);
     ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d);
@@ -375,14 +375,13 @@ static int ssd0323_init(SSISlave *d)
 
     register_savevm(dev, "ssd0323_oled", -1, 1,
                     ssd0323_save, ssd0323_load, s);
-    return 0;
 }
 
 static void ssd0323_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = ssd0323_init;
+    k->realize = ssd0323_realize;
     k->transfer = ssd0323_transfer;
     k->cs_polarity = SSI_CS_HIGH;
 }
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index 8b0823e593..087c8e6855 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -5,6 +5,7 @@ common-obj-$(CONFIG_PL330) += pl330.o
 common-obj-$(CONFIG_I82374) += i82374.o
 common-obj-$(CONFIG_I8257) += i8257.o
 common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
+common-obj-$(CONFIG_ZYNQ_DEVCFG) += xlnx-zynq-devcfg.o
 common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
 common-obj-$(CONFIG_STP2000) += sparc32_dma.o
 common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c
new file mode 100644
index 0000000000..3b10523430
--- /dev/null
+++ b/hw/dma/xlnx-zynq-devcfg.c
@@ -0,0 +1,400 @@
+/*
+ * QEMU model of the Xilinx Zynq Devcfg Interface
+ *
+ * (C) 2011 PetaLogix Pty Ltd
+ * (C) 2014 Xilinx Inc.
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/dma/xlnx-zynq-devcfg.h"
+#include "qemu/bitops.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "qemu/log.h"
+
+#define FREQ_HZ 900000000
+
+#define BTT_MAX 0x400
+
+#ifndef XLNX_ZYNQ_DEVCFG_ERR_DEBUG
+#define XLNX_ZYNQ_DEVCFG_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT(fmt, args...) do { \
+    if (XLNX_ZYNQ_DEVCFG_ERR_DEBUG) { \
+        qemu_log("%s: " fmt, __func__, ## args); \
+    } \
+} while (0);
+
+REG32(CTRL, 0x00)
+    FIELD(CTRL,     FORCE_RST,          31,  1) /* Not supported, wr ignored */
+    FIELD(CTRL,     PCAP_PR,            27,  1) /* Forced to 0 on bad unlock */
+    FIELD(CTRL,     PCAP_MODE,          26,  1)
+    FIELD(CTRL,     MULTIBOOT_EN,       24,  1)
+    FIELD(CTRL,     USER_MODE,          15,  1)
+    FIELD(CTRL,     PCFG_AES_FUSE,      12,  1)
+    FIELD(CTRL,     PCFG_AES_EN,         9,  3)
+    FIELD(CTRL,     SEU_EN,              8,  1)
+    FIELD(CTRL,     SEC_EN,              7,  1)
+    FIELD(CTRL,     SPNIDEN,             6,  1)
+    FIELD(CTRL,     SPIDEN,              5,  1)
+    FIELD(CTRL,     NIDEN,               4,  1)
+    FIELD(CTRL,     DBGEN,               3,  1)
+    FIELD(CTRL,     DAP_EN,              0,  3)
+
+REG32(LOCK, 0x04)
+#define AES_FUSE_LOCK        4
+#define AES_EN_LOCK          3
+#define SEU_LOCK             2
+#define SEC_LOCK             1
+#define DBG_LOCK             0
+
+/* mapping bits in R_LOCK to what they lock in R_CTRL */
+static const uint32_t lock_ctrl_map[] = {
+    [AES_FUSE_LOCK] = R_CTRL_PCFG_AES_FUSE_MASK,
+    [AES_EN_LOCK]   = R_CTRL_PCFG_AES_EN_MASK,
+    [SEU_LOCK]      = R_CTRL_SEU_EN_MASK,
+    [SEC_LOCK]      = R_CTRL_SEC_EN_MASK,
+    [DBG_LOCK]      = R_CTRL_SPNIDEN_MASK | R_CTRL_SPIDEN_MASK |
+                      R_CTRL_NIDEN_MASK   | R_CTRL_DBGEN_MASK  |
+                      R_CTRL_DAP_EN_MASK,
+};
+
+REG32(CFG, 0x08)
+    FIELD(CFG,      RFIFO_TH,           10,  2)
+    FIELD(CFG,      WFIFO_TH,            8,  2)
+    FIELD(CFG,      RCLK_EDGE,           7,  1)
+    FIELD(CFG,      WCLK_EDGE,           6,  1)
+    FIELD(CFG,      DISABLE_SRC_INC,     5,  1)
+    FIELD(CFG,      DISABLE_DST_INC,     4,  1)
+#define R_CFG_RESET 0x50B
+
+REG32(INT_STS, 0x0C)
+    FIELD(INT_STS,  PSS_GTS_USR_B,      31,  1)
+    FIELD(INT_STS,  PSS_FST_CFG_B,      30,  1)
+    FIELD(INT_STS,  PSS_CFG_RESET_B,    27,  1)
+    FIELD(INT_STS,  RX_FIFO_OV,         18,  1)
+    FIELD(INT_STS,  WR_FIFO_LVL,        17,  1)
+    FIELD(INT_STS,  RD_FIFO_LVL,        16,  1)
+    FIELD(INT_STS,  DMA_CMD_ERR,        15,  1)
+    FIELD(INT_STS,  DMA_Q_OV,           14,  1)
+    FIELD(INT_STS,  DMA_DONE,           13,  1)
+    FIELD(INT_STS,  DMA_P_DONE,         12,  1)
+    FIELD(INT_STS,  P2D_LEN_ERR,        11,  1)
+    FIELD(INT_STS,  PCFG_DONE,           2,  1)
+#define R_INT_STS_RSVD       ((0x7 << 24) | (0x1 << 19) | (0xF < 7))
+
+REG32(INT_MASK, 0x10)
+
+REG32(STATUS, 0x14)
+    FIELD(STATUS,   DMA_CMD_Q_F,        31,  1)
+    FIELD(STATUS,   DMA_CMD_Q_E,        30,  1)
+    FIELD(STATUS,   DMA_DONE_CNT,       28,  2)
+    FIELD(STATUS,   RX_FIFO_LVL,        20,  5)
+    FIELD(STATUS,   TX_FIFO_LVL,        12,  7)
+    FIELD(STATUS,   PSS_GTS_USR_B,      11,  1)
+    FIELD(STATUS,   PSS_FST_CFG_B,      10,  1)
+    FIELD(STATUS,   PSS_CFG_RESET_B,     5,  1)
+
+REG32(DMA_SRC_ADDR, 0x18)
+REG32(DMA_DST_ADDR, 0x1C)
+REG32(DMA_SRC_LEN, 0x20)
+REG32(DMA_DST_LEN, 0x24)
+REG32(ROM_SHADOW, 0x28)
+REG32(SW_ID, 0x30)
+REG32(UNLOCK, 0x34)
+
+#define R_UNLOCK_MAGIC 0x757BDF0D
+
+REG32(MCTRL, 0x80)
+    FIELD(MCTRL,    PS_VERSION,         28,  4)
+    FIELD(MCTRL,    PCFG_POR_B,          8,  1)
+    FIELD(MCTRL,    INT_PCAP_LPBK,       4,  1)
+    FIELD(MCTRL,    QEMU,                3,  1)
+
+static void xlnx_zynq_devcfg_update_ixr(XlnxZynqDevcfg *s)
+{
+    qemu_set_irq(s->irq, ~s->regs[R_INT_MASK] & s->regs[R_INT_STS]);
+}
+
+static void xlnx_zynq_devcfg_reset(DeviceState *dev)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(dev);
+    int i;
+
+    for (i = 0; i < XLNX_ZYNQ_DEVCFG_R_MAX; ++i) {
+        register_reset(&s->regs_info[i]);
+    }
+}
+
+static void xlnx_zynq_devcfg_dma_go(XlnxZynqDevcfg *s)
+{
+    do {
+        uint8_t buf[BTT_MAX];
+        XlnxZynqDevcfgDMACmd *dmah = s->dma_cmd_fifo;
+        uint32_t btt = BTT_MAX;
+        bool loopback = s->regs[R_MCTRL] & R_MCTRL_INT_PCAP_LPBK_MASK;
+
+        btt = MIN(btt, dmah->src_len);
+        if (loopback) {
+            btt = MIN(btt, dmah->dest_len);
+        }
+        DB_PRINT("reading %x bytes from %x\n", btt, dmah->src_addr);
+        dma_memory_read(&address_space_memory, dmah->src_addr, buf, btt);
+        dmah->src_len -= btt;
+        dmah->src_addr += btt;
+        if (loopback && (dmah->src_len || dmah->dest_len)) {
+            DB_PRINT("writing %x bytes from %x\n", btt, dmah->dest_addr);
+            dma_memory_write(&address_space_memory, dmah->dest_addr, buf, btt);
+            dmah->dest_len -= btt;
+            dmah->dest_addr += btt;
+        }
+        if (!dmah->src_len && !dmah->dest_len) {
+            DB_PRINT("dma operation finished\n");
+            s->regs[R_INT_STS] |= R_INT_STS_DMA_DONE_MASK |
+                                  R_INT_STS_DMA_P_DONE_MASK;
+            s->dma_cmd_fifo_num--;
+            memmove(s->dma_cmd_fifo, &s->dma_cmd_fifo[1],
+                    sizeof(s->dma_cmd_fifo) - sizeof(s->dma_cmd_fifo[0]));
+        }
+        xlnx_zynq_devcfg_update_ixr(s);
+    } while (s->dma_cmd_fifo_num);
+}
+
+static void r_ixr_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+    xlnx_zynq_devcfg_update_ixr(s);
+}
+
+static uint64_t r_ctrl_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(lock_ctrl_map); ++i) {
+        if (s->regs[R_LOCK] & 1 << i) {
+            val &= ~lock_ctrl_map[i];
+            val |= lock_ctrl_map[i] & s->regs[R_CTRL];
+        }
+    }
+    return val;
+}
+
+static void r_ctrl_post_write(RegisterInfo *reg, uint64_t val)
+{
+    const char *device_prefix = object_get_typename(OBJECT(reg->opaque));
+    uint32_t aes_en = FIELD_EX32(val, CTRL, PCFG_AES_EN);
+
+    if (aes_en != 0 && aes_en != 7) {
+        qemu_log_mask(LOG_UNIMP, "%s: warning, aes-en bits inconsistent,"
+                      "unimplemented security reset should happen!\n",
+                      device_prefix);
+    }
+}
+
+static void r_unlock_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+    const char *device_prefix = object_get_typename(OBJECT(s));
+
+    if (val == R_UNLOCK_MAGIC) {
+        DB_PRINT("successful unlock\n");
+        s->regs[R_CTRL] |= R_CTRL_PCAP_PR_MASK;
+        s->regs[R_CTRL] |= R_CTRL_PCFG_AES_EN_MASK;
+        memory_region_set_enabled(&s->iomem, true);
+    } else { /* bad unlock attempt */
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: failed unlock\n", device_prefix);
+        s->regs[R_CTRL] &= ~R_CTRL_PCAP_PR_MASK;
+        s->regs[R_CTRL] &= ~R_CTRL_PCFG_AES_EN_MASK;
+        /* core becomes inaccessible */
+        memory_region_set_enabled(&s->iomem, false);
+    }
+}
+
+static uint64_t r_lock_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+    /* once bits are locked they stay locked */
+    return s->regs[R_LOCK] | val;
+}
+
+static void r_dma_dst_len_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+    s->dma_cmd_fifo[s->dma_cmd_fifo_num] = (XlnxZynqDevcfgDMACmd) {
+            .src_addr = s->regs[R_DMA_SRC_ADDR] & ~0x3UL,
+            .dest_addr = s->regs[R_DMA_DST_ADDR] & ~0x3UL,
+            .src_len = s->regs[R_DMA_SRC_LEN] << 2,
+            .dest_len = s->regs[R_DMA_DST_LEN] << 2,
+    };
+    s->dma_cmd_fifo_num++;
+    DB_PRINT("dma transfer started; %d total transfers pending\n",
+             s->dma_cmd_fifo_num);
+    xlnx_zynq_devcfg_dma_go(s);
+}
+
+static const RegisterAccessInfo xlnx_zynq_devcfg_regs_info[] = {
+    {   .name = "CTRL",                 .addr = A_CTRL,
+        .reset = R_CTRL_PCAP_PR_MASK | R_CTRL_PCAP_MODE_MASK | 0x3 << 13,
+        .rsvd = 0x1 << 28 | 0x3ff << 13 | 0x3 << 13,
+        .pre_write = r_ctrl_pre_write,
+        .post_write = r_ctrl_post_write,
+    },
+    {   .name = "LOCK",                 .addr = A_LOCK,
+        .rsvd = MAKE_64BIT_MASK(5, 64 - 5),
+        .pre_write = r_lock_pre_write,
+    },
+    {   .name = "CFG",                  .addr = A_CFG,
+        .reset = R_CFG_RESET,
+        .rsvd = 0xfffff00f,
+    },
+    {   .name = "INT_STS",              .addr = A_INT_STS,
+        .w1c = ~R_INT_STS_RSVD,
+        .reset = R_INT_STS_PSS_GTS_USR_B_MASK   |
+                 R_INT_STS_PSS_CFG_RESET_B_MASK |
+                 R_INT_STS_WR_FIFO_LVL_MASK,
+        .rsvd = R_INT_STS_RSVD,
+        .post_write = r_ixr_post_write,
+    },
+    {   .name = "INT_MASK",            .addr = A_INT_MASK,
+        .reset = ~0,
+        .rsvd = R_INT_STS_RSVD,
+        .post_write = r_ixr_post_write,
+    },
+    {   .name = "STATUS",               .addr = A_STATUS,
+        .reset = R_STATUS_DMA_CMD_Q_E_MASK      |
+                 R_STATUS_PSS_GTS_USR_B_MASK    |
+                 R_STATUS_PSS_CFG_RESET_B_MASK,
+        .ro = ~0,
+    },
+    {   .name = "DMA_SRC_ADDR",         .addr = A_DMA_SRC_ADDR, },
+    {   .name = "DMA_DST_ADDR",         .addr = A_DMA_DST_ADDR, },
+    {   .name = "DMA_SRC_LEN",          .addr = A_DMA_SRC_LEN,
+        .ro = MAKE_64BIT_MASK(27, 64 - 27) },
+    {   .name = "DMA_DST_LEN",          .addr = A_DMA_DST_LEN,
+        .ro = MAKE_64BIT_MASK(27, 64 - 27),
+        .post_write = r_dma_dst_len_post_write,
+    },
+    {   .name = "ROM_SHADOW",           .addr = A_ROM_SHADOW,
+        .rsvd = ~0ull,
+    },
+    {   .name = "SW_ID",                .addr = A_SW_ID, },
+    {   .name = "UNLOCK",               .addr = A_UNLOCK,
+        .post_write = r_unlock_post_write,
+    },
+    {   .name = "MCTRL",                .addr = R_MCTRL * 4,
+       /* Silicon 3.0 for version field, the mysterious reserved bit 23
+        * and QEMU platform identifier.
+        */
+       .reset = 0x2 << R_MCTRL_PS_VERSION_SHIFT | 1 << 23 | R_MCTRL_QEMU_MASK,
+       .ro = ~R_MCTRL_INT_PCAP_LPBK_MASK,
+       .rsvd = 0x00f00303,
+    },
+};
+
+static const MemoryRegionOps xlnx_zynq_devcfg_reg_ops = {
+    .read = register_read_memory,
+    .write = register_write_memory,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static const VMStateDescription vmstate_xlnx_zynq_devcfg_dma_cmd = {
+    .name = "xlnx_zynq_devcfg_dma_cmd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(src_addr, XlnxZynqDevcfgDMACmd),
+        VMSTATE_UINT32(dest_addr, XlnxZynqDevcfgDMACmd),
+        VMSTATE_UINT32(src_len, XlnxZynqDevcfgDMACmd),
+        VMSTATE_UINT32(dest_len, XlnxZynqDevcfgDMACmd),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_xlnx_zynq_devcfg = {
+    .name = "xlnx_zynq_devcfg",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(dma_cmd_fifo, XlnxZynqDevcfg,
+                             XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN, 0,
+                             vmstate_xlnx_zynq_devcfg_dma_cmd,
+                             XlnxZynqDevcfgDMACmd),
+        VMSTATE_UINT8(dma_cmd_fifo_num, XlnxZynqDevcfg),
+        VMSTATE_UINT32_ARRAY(regs, XlnxZynqDevcfg, XLNX_ZYNQ_DEVCFG_R_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void xlnx_zynq_devcfg_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(obj);
+    RegisterInfoArray *reg_array;
+
+    sysbus_init_irq(sbd, &s->irq);
+
+    memory_region_init(&s->iomem, obj, "devcfg", XLNX_ZYNQ_DEVCFG_R_MAX * 4);
+    reg_array =
+        register_init_block32(DEVICE(obj), xlnx_zynq_devcfg_regs_info,
+                              ARRAY_SIZE(xlnx_zynq_devcfg_regs_info),
+                              s->regs_info, s->regs,
+                              &xlnx_zynq_devcfg_reg_ops,
+                              XLNX_ZYNQ_DEVCFG_ERR_DEBUG,
+                              XLNX_ZYNQ_DEVCFG_R_MAX);
+    memory_region_add_subregion(&s->iomem,
+                                A_CTRL,
+                                &reg_array->mem);
+
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = xlnx_zynq_devcfg_reset;
+    dc->vmsd = &vmstate_xlnx_zynq_devcfg;
+}
+
+static const TypeInfo xlnx_zynq_devcfg_info = {
+    .name           = TYPE_XLNX_ZYNQ_DEVCFG,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(XlnxZynqDevcfg),
+    .instance_init  = xlnx_zynq_devcfg_init,
+    .class_init     = xlnx_zynq_devcfg_class_init,
+};
+
+static void xlnx_zynq_devcfg_register_types(void)
+{
+    type_register_static(&xlnx_zynq_devcfg_info);
+}
+
+type_init(xlnx_zynq_devcfg_register_types)
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 890d5d7442..06d8db6bd6 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -187,11 +187,11 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
     case 0x1c: /* SysTick Calibration Value.  */
         return 10000;
     case 0xd00: /* CPUID Base.  */
-        cpu = ARM_CPU(current_cpu);
+        cpu = ARM_CPU(qemu_get_cpu(0));
         return cpu->midr;
     case 0xd04: /* Interrupt Control State.  */
         /* VECTACTIVE */
-        cpu = ARM_CPU(current_cpu);
+        cpu = ARM_CPU(qemu_get_cpu(0));
         val = cpu->env.v7m.exception;
         if (val == 1023) {
             val = 0;
@@ -222,7 +222,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
             val |= (1 << 31);
         return val;
     case 0xd08: /* Vector Table Offset.  */
-        cpu = ARM_CPU(current_cpu);
+        cpu = ARM_CPU(qemu_get_cpu(0));
         return cpu->env.v7m.vecbase;
     case 0xd0c: /* Application Interrupt/Reset Control.  */
         return 0xfa050000;
@@ -349,7 +349,7 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
         }
         break;
     case 0xd08: /* Vector Table Offset.  */
-        cpu = ARM_CPU(current_cpu);
+        cpu = ARM_CPU(qemu_get_cpu(0));
         cpu->env.v7m.vecbase = value & 0xffffff80;
         break;
     case 0xd0c: /* Application Interrupt/Reset Control.  */
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 07527b677b..4968bdbb28 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -191,9 +191,16 @@ petalogix_ml605_init(MachineState *machine)
         spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
 
         for (i = 0; i < NUM_SPI_FLASHES; i++) {
+            DriveInfo *dinfo = drive_get_next(IF_MTD);
             qemu_irq cs_line;
 
-            dev = ssi_create_slave(spi, "n25q128");
+            dev = ssi_create_slave_no_init(spi, "n25q128");
+            if (dinfo) {
+                qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo),
+                                    &error_fatal);
+            }
+            qdev_init_nofail(dev);
+
             cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0);
             sysbus_connect_irq(busdev, i+1, cs_line);
         }
diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c
index 9014f0f705..2a277bdb86 100644
--- a/hw/misc/max111x.c
+++ b/hw/misc/max111x.c
@@ -147,14 +147,14 @@ static int max111x_init(SSISlave *d, int inputs)
     return 0;
 }
 
-static int max1110_init(SSISlave *dev)
+static void max1110_realize(SSISlave *dev, Error **errp)
 {
-    return max111x_init(dev, 8);
+    max111x_init(dev, 8);
 }
 
-static int max1111_init(SSISlave *dev)
+static void max1111_realize(SSISlave *dev, Error **errp)
 {
-    return max111x_init(dev, 4);
+    max111x_init(dev, 4);
 }
 
 void max111x_set_input(DeviceState *dev, int line, uint8_t value)
@@ -183,7 +183,7 @@ static void max1110_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = max1110_init;
+    k->realize = max1110_realize;
 }
 
 static const TypeInfo max1110_info = {
@@ -196,7 +196,7 @@ static void max1111_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = max1111_init;
+    k->realize = max1111_realize;
 }
 
 static const TypeInfo max1111_info = {
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index 075e4ed5df..3ff0886dd5 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -15,6 +15,7 @@
 #include "sysemu/blockdev.h"
 #include "hw/ssi/ssi.h"
 #include "hw/sd/sd.h"
+#include "qapi/error.h"
 
 //#define DEBUG_SSI_SD 1
 
@@ -249,7 +250,7 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
-static int ssi_sd_init(SSISlave *d)
+static void ssi_sd_realize(SSISlave *d, Error **errp)
 {
     DeviceState *dev = DEVICE(d);
     ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d);
@@ -260,17 +261,17 @@ static int ssi_sd_init(SSISlave *d)
     dinfo = drive_get_next(IF_SD);
     s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
     if (s->sd == NULL) {
-        return -1;
+        error_setg(errp, "Device initialization failed.");
+        return;
     }
     register_savevm(dev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
-    return 0;
 }
 
 static void ssi_sd_class_init(ObjectClass *klass, void *data)
 {
     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 
-    k->init = ssi_sd_init;
+    k->realize = ssi_sd_realize;
     k->transfer = ssi_sd_transfer;
     k->cs_polarity = SSI_CS_LOW;
 }
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index fcbb79ef01..c79a8dcd86 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL022) += pl022.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
 
 obj-$(CONFIG_OMAP) += omap_spi.o
 obj-$(CONFIG_IMX) += imx_spi.o
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
new file mode 100644
index 0000000000..a371e302d4
--- /dev/null
+++ b/hw/ssi/aspeed_smc.c
@@ -0,0 +1,470 @@
+/*
+ * ASPEED AST2400 SMC Controller (SPI Flash Only)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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/osdep.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/log.h"
+#include "include/qemu/error-report.h"
+#include "exec/address-spaces.h"
+
+#include "hw/ssi/aspeed_smc.h"
+
+/* CE Type Setting Register */
+#define R_CONF            (0x00 / 4)
+#define   CONF_LEGACY_DISABLE  (1 << 31)
+#define   CONF_ENABLE_W4       20
+#define   CONF_ENABLE_W3       19
+#define   CONF_ENABLE_W2       18
+#define   CONF_ENABLE_W1       17
+#define   CONF_ENABLE_W0       16
+#define   CONF_FLASH_TYPE4     9
+#define   CONF_FLASH_TYPE3     7
+#define   CONF_FLASH_TYPE2     5
+#define   CONF_FLASH_TYPE1     3
+#define   CONF_FLASH_TYPE0     1
+
+/* CE Control Register */
+#define R_CE_CTRL            (0x04 / 4)
+#define   CTRL_EXTENDED4       4  /* 32 bit addressing for SPI */
+#define   CTRL_EXTENDED3       3  /* 32 bit addressing for SPI */
+#define   CTRL_EXTENDED2       2  /* 32 bit addressing for SPI */
+#define   CTRL_EXTENDED1       1  /* 32 bit addressing for SPI */
+#define   CTRL_EXTENDED0       0  /* 32 bit addressing for SPI */
+
+/* Interrupt Control and Status Register */
+#define R_INTR_CTRL       (0x08 / 4)
+#define   INTR_CTRL_DMA_STATUS            (1 << 11)
+#define   INTR_CTRL_CMD_ABORT_STATUS      (1 << 10)
+#define   INTR_CTRL_WRITE_PROTECT_STATUS  (1 << 9)
+#define   INTR_CTRL_DMA_EN                (1 << 3)
+#define   INTR_CTRL_CMD_ABORT_EN          (1 << 2)
+#define   INTR_CTRL_WRITE_PROTECT_EN      (1 << 1)
+
+/* CEx Control Register */
+#define R_CTRL0           (0x10 / 4)
+#define   CTRL_CMD_SHIFT           16
+#define   CTRL_CMD_MASK            0xff
+#define   CTRL_CE_STOP_ACTIVE      (1 << 2)
+#define   CTRL_CMD_MODE_MASK       0x3
+#define     CTRL_READMODE          0x0
+#define     CTRL_FREADMODE         0x1
+#define     CTRL_WRITEMODE         0x2
+#define     CTRL_USERMODE          0x3
+#define R_CTRL1           (0x14 / 4)
+#define R_CTRL2           (0x18 / 4)
+#define R_CTRL3           (0x1C / 4)
+#define R_CTRL4           (0x20 / 4)
+
+/* CEx Segment Address Register */
+#define R_SEG_ADDR0       (0x30 / 4)
+#define   SEG_SIZE_SHIFT       24   /* 8MB units */
+#define   SEG_SIZE_MASK        0x7f
+#define   SEG_START_SHIFT      16   /* address bit [A29-A23] */
+#define   SEG_START_MASK       0x7f
+#define R_SEG_ADDR1       (0x34 / 4)
+#define R_SEG_ADDR2       (0x38 / 4)
+#define R_SEG_ADDR3       (0x3C / 4)
+#define R_SEG_ADDR4       (0x40 / 4)
+
+/* Misc Control Register #1 */
+#define R_MISC_CTRL1      (0x50 / 4)
+
+/* Misc Control Register #2 */
+#define R_MISC_CTRL2      (0x54 / 4)
+
+/* DMA Control/Status Register */
+#define R_DMA_CTRL        (0x80 / 4)
+#define   DMA_CTRL_DELAY_MASK   0xf
+#define   DMA_CTRL_DELAY_SHIFT  8
+#define   DMA_CTRL_FREQ_MASK    0xf
+#define   DMA_CTRL_FREQ_SHIFT   4
+#define   DMA_CTRL_MODE         (1 << 3)
+#define   DMA_CTRL_CKSUM        (1 << 2)
+#define   DMA_CTRL_DIR          (1 << 1)
+#define   DMA_CTRL_EN           (1 << 0)
+
+/* DMA Flash Side Address */
+#define R_DMA_FLASH_ADDR  (0x84 / 4)
+
+/* DMA DRAM Side Address */
+#define R_DMA_DRAM_ADDR   (0x88 / 4)
+
+/* DMA Length Register */
+#define R_DMA_LEN         (0x8C / 4)
+
+/* Checksum Calculation Result */
+#define R_DMA_CHECKSUM    (0x90 / 4)
+
+/* Misc Control Register #2 */
+#define R_TIMINGS         (0x94 / 4)
+
+/* SPI controller registers and bits */
+#define R_SPI_CONF        (0x00 / 4)
+#define   SPI_CONF_ENABLE_W0   0
+#define R_SPI_CTRL0       (0x4 / 4)
+#define R_SPI_MISC_CTRL   (0x10 / 4)
+#define R_SPI_TIMINGS     (0x14 / 4)
+
+/*
+ * Default segments mapping addresses and size for each slave per
+ * controller. These can be changed when board is initialized with the
+ * Segment Address Registers but they don't seem do be used on the
+ * field.
+ */
+static const AspeedSegments aspeed_segments_legacy[] = {
+    { 0x10000000, 32 * 1024 * 1024 },
+};
+
+static const AspeedSegments aspeed_segments_fmc[] = {
+    { 0x20000000, 64 * 1024 * 1024 },
+    { 0x24000000, 32 * 1024 * 1024 },
+    { 0x26000000, 32 * 1024 * 1024 },
+    { 0x28000000, 32 * 1024 * 1024 },
+    { 0x2A000000, 32 * 1024 * 1024 }
+};
+
+static const AspeedSegments aspeed_segments_spi[] = {
+    { 0x30000000, 64 * 1024 * 1024 },
+};
+
+static const AspeedSMCController controllers[] = {
+    { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+      CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 },
+    { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+      CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 },
+    { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS,
+      SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 },
+};
+
+static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr,
+                                              unsigned size)
+{
+    qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u"
+                  PRIx64 "\n", __func__, addr, size);
+    return 0;
+}
+
+static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr,
+                                           uint64_t data, unsigned size)
+{
+   qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%"
+                 PRIx64 "\n", __func__, addr, size, data);
+}
+
+static const MemoryRegionOps aspeed_smc_flash_default_ops = {
+    .read = aspeed_smc_flash_default_read,
+    .write = aspeed_smc_flash_default_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs)
+{
+    return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK;
+}
+
+static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs)
+{
+    return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE;
+}
+
+static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs)
+{
+    return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs));
+}
+
+static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
+{
+    AspeedSMCFlash *fl = opaque;
+    const AspeedSMCState *s = fl->controller;
+    uint64_t ret = 0;
+    int i;
+
+    if (aspeed_smc_is_usermode(s, fl->id)) {
+        for (i = 0; i < size; i++) {
+            ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
+        }
+    } else {
+        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
+                      __func__);
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
+                           unsigned size)
+{
+    AspeedSMCFlash *fl = opaque;
+    const AspeedSMCState *s = fl->controller;
+    int i;
+
+    if (!aspeed_smc_is_writable(s, fl->id)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%"
+                      HWADDR_PRIx "\n", __func__, addr);
+        return;
+    }
+
+    if (!aspeed_smc_is_usermode(s, fl->id)) {
+        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
+                      __func__);
+        return;
+    }
+
+    for (i = 0; i < size; i++) {
+        ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
+    }
+}
+
+static const MemoryRegionOps aspeed_smc_flash_ops = {
+    .read = aspeed_smc_flash_read,
+    .write = aspeed_smc_flash_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs)
+{
+    return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
+}
+
+static void aspeed_smc_update_cs(const AspeedSMCState *s)
+{
+    int i;
+
+    for (i = 0; i < s->num_cs; ++i) {
+        qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
+    }
+}
+
+static void aspeed_smc_reset(DeviceState *d)
+{
+    AspeedSMCState *s = ASPEED_SMC(d);
+    int i;
+
+    memset(s->regs, 0, sizeof s->regs);
+
+    /* Unselect all slaves */
+    for (i = 0; i < s->num_cs; ++i) {
+        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
+    }
+
+    aspeed_smc_update_cs(s);
+}
+
+static bool aspeed_smc_is_implemented(AspeedSMCState *s, hwaddr addr)
+{
+    return (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl ||
+            (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs));
+}
+
+static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    AspeedSMCState *s = ASPEED_SMC(opaque);
+
+    addr >>= 2;
+
+    if (addr >= ARRAY_SIZE(s->regs)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+        return 0;
+    }
+
+    if (!aspeed_smc_is_implemented(s, addr)) {
+        qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
+                __func__, addr);
+        return 0;
+    }
+
+    return s->regs[addr];
+}
+
+static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned int size)
+{
+    AspeedSMCState *s = ASPEED_SMC(opaque);
+    uint32_t value = data;
+
+    addr >>= 2;
+
+    if (addr >= ARRAY_SIZE(s->regs)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+        return;
+    }
+
+    if (!aspeed_smc_is_implemented(s, addr)) {
+        qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+        return;
+    }
+
+    /*
+     * Not much to do apart from storing the value and set the cs
+     * lines if the register is a controlling one.
+     */
+    s->regs[addr] = value;
+    if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
+        aspeed_smc_update_cs(s);
+    }
+}
+
+static const MemoryRegionOps aspeed_smc_ops = {
+    .read = aspeed_smc_read,
+    .write = aspeed_smc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.unaligned = true,
+};
+
+static void aspeed_smc_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    AspeedSMCState *s = ASPEED_SMC(dev);
+    AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s);
+    int i;
+    char name[32];
+    hwaddr offset = 0;
+
+    s->ctrl = mc->ctrl;
+
+    /* keep a copy under AspeedSMCState to speed up accesses */
+    s->r_conf = s->ctrl->r_conf;
+    s->r_ce_ctrl = s->ctrl->r_ce_ctrl;
+    s->r_ctrl0 = s->ctrl->r_ctrl0;
+    s->r_timings = s->ctrl->r_timings;
+    s->conf_enable_w0 = s->ctrl->conf_enable_w0;
+
+    /* Enforce some real HW limits */
+    if (s->num_cs > s->ctrl->max_slaves) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: num_cs cannot exceed: %d\n",
+                      __func__, s->ctrl->max_slaves);
+        s->num_cs = s->ctrl->max_slaves;
+    }
+
+    s->spi = ssi_create_bus(dev, "spi");
+
+    /* Setup cs_lines for slaves */
+    sysbus_init_irq(sbd, &s->irq);
+    s->cs_lines = g_new0(qemu_irq, s->num_cs);
+    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
+
+    for (i = 0; i < s->num_cs; ++i) {
+        sysbus_init_irq(sbd, &s->cs_lines[i]);
+    }
+
+    aspeed_smc_reset(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
+                          s->ctrl->name, ASPEED_SMC_R_MAX * 4);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    /*
+     * Memory region where flash modules are remapped
+     */
+    snprintf(name, sizeof(name), "%s.flash", s->ctrl->name);
+
+    memory_region_init_io(&s->mmio_flash, OBJECT(s),
+                          &aspeed_smc_flash_default_ops, s, name,
+                          s->ctrl->mapping_window_size);
+    sysbus_init_mmio(sbd, &s->mmio_flash);
+
+    s->flashes = g_new0(AspeedSMCFlash, s->num_cs);
+
+    for (i = 0; i < s->num_cs; ++i) {
+        AspeedSMCFlash *fl = &s->flashes[i];
+
+        snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i);
+
+        fl->id = i;
+        fl->controller = s;
+        fl->size = s->ctrl->segments[i].size;
+        memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops,
+                              fl, name, fl->size);
+        memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio);
+        offset += fl->size;
+    }
+}
+
+static const VMStateDescription vmstate_aspeed_smc = {
+    .name = "aspeed.smc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property aspeed_smc_properties[] = {
+    DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_smc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    AspeedSMCClass *mc = ASPEED_SMC_CLASS(klass);
+
+    dc->realize = aspeed_smc_realize;
+    dc->reset = aspeed_smc_reset;
+    dc->props = aspeed_smc_properties;
+    dc->vmsd = &vmstate_aspeed_smc;
+    mc->ctrl = data;
+}
+
+static const TypeInfo aspeed_smc_info = {
+    .name           = TYPE_ASPEED_SMC,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(AspeedSMCState),
+    .class_size     = sizeof(AspeedSMCClass),
+    .abstract       = true,
+};
+
+static void aspeed_smc_register_types(void)
+{
+    int i;
+
+    type_register_static(&aspeed_smc_info);
+    for (i = 0; i < ARRAY_SIZE(controllers); ++i) {
+        TypeInfo ti = {
+            .name       = controllers[i].name,
+            .parent     = TYPE_ASPEED_SMC,
+            .class_init = aspeed_smc_class_init,
+            .class_data = (void *)&controllers[i],
+        };
+        type_register(&ti);
+    }
+}
+
+type_init(aspeed_smc_register_types)
diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c
index 9791c0d947..7eaaf565fd 100644
--- a/hw/ssi/ssi.c
+++ b/hw/ssi/ssi.c
@@ -54,7 +54,7 @@ static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
     return 0;
 }
 
-static int ssi_slave_init(DeviceState *dev)
+static void ssi_slave_realize(DeviceState *dev, Error **errp)
 {
     SSISlave *s = SSI_SLAVE(dev);
     SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
@@ -64,7 +64,7 @@ static int ssi_slave_init(DeviceState *dev)
         qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1);
     }
 
-    return ssc->init(s);
+    ssc->realize(s, errp);
 }
 
 static void ssi_slave_class_init(ObjectClass *klass, void *data)
@@ -72,7 +72,7 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data)
     SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
 
-    dc->init = ssi_slave_init;
+    dc->realize = ssi_slave_realize;
     dc->bus_type = TYPE_SSI_BUS;
     if (!ssc->transfer_raw) {
         ssc->transfer_raw = ssi_transfer_raw_default;
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 23c7399131..3e4d4164cd 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -445,15 +445,31 @@ void memory_region_init_alias(MemoryRegion *mr,
                               uint64_t size);
 
 /**
+ * memory_region_init_rom: Initialize a ROM memory region.
+ *
+ * This has the same effect as calling memory_region_init_ram()
+ * and then marking the resulting region read-only with
+ * memory_region_set_readonly().
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @owner: the object that tracks the region's reference count
+ * @name: the name of the region.
+ * @size: size of the region.
+ * @errp: pointer to Error*, to store an error if it happens.
+ */
+void memory_region_init_rom(MemoryRegion *mr,
+                            struct Object *owner,
+                            const char *name,
+                            uint64_t size,
+                            Error **errp);
+
+/**
  * memory_region_init_rom_device:  Initialize a ROM memory region.  Writes are
  *                                 handled via callbacks.
  *
- * If NULL callbacks pointer is given, then I/O space is not supposed to be
- * handled by QEMU itself. Any access via the memory API will cause an abort().
- *
  * @mr: the #MemoryRegion to be initialized.
  * @owner: the object that tracks the region's reference count
- * @ops: callbacks for write access handling.
+ * @ops: callbacks for write access handling (must not be NULL).
  * @name: the name of the region.
  * @size: size of the region.
  * @errp: pointer to Error*, to store an error if it happens.
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
index f1a64fd389..7833bc716c 100644
--- a/include/hw/arm/ast2400.h
+++ b/include/hw/arm/ast2400.h
@@ -17,6 +17,7 @@
 #include "hw/misc/aspeed_scu.h"
 #include "hw/timer/aspeed_timer.h"
 #include "hw/i2c/aspeed_i2c.h"
+#include "hw/ssi/aspeed_smc.h"
 
 typedef struct AST2400State {
     /*< private >*/
@@ -29,6 +30,8 @@ typedef struct AST2400State {
     AspeedTimerCtrlState timerctrl;
     AspeedI2CState i2c;
     AspeedSCUState scu;
+    AspeedSMCState smc;
+    AspeedSMCState spi;
 } AST2400State;
 
 #define TYPE_AST2400 "ast2400"
diff --git a/include/hw/dma/xlnx-zynq-devcfg.h b/include/hw/dma/xlnx-zynq-devcfg.h
new file mode 100644
index 0000000000..d40e5c8df6
--- /dev/null
+++ b/include/hw/dma/xlnx-zynq-devcfg.h
@@ -0,0 +1,62 @@
+/*
+ * QEMU model of the Xilinx Devcfg Interface
+ *
+ * (C) 2011 PetaLogix Pty Ltd
+ * (C) 2014 Xilinx Inc.
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef XLNX_ZYNQ_DEVCFG_H
+
+#include "hw/register.h"
+#include "hw/sysbus.h"
+
+#define TYPE_XLNX_ZYNQ_DEVCFG "xlnx.ps7-dev-cfg"
+
+#define XLNX_ZYNQ_DEVCFG(obj) \
+    OBJECT_CHECK(XlnxZynqDevcfg, (obj), TYPE_XLNX_ZYNQ_DEVCFG)
+
+#define XLNX_ZYNQ_DEVCFG_R_MAX 0x118
+
+#define XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN 10
+
+typedef struct XlnxZynqDevcfgDMACmd {
+    uint32_t src_addr;
+    uint32_t dest_addr;
+    uint32_t src_len;
+    uint32_t dest_len;
+} XlnxZynqDevcfgDMACmd;
+
+typedef struct XlnxZynqDevcfg {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    XlnxZynqDevcfgDMACmd dma_cmd_fifo[XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN];
+    uint8_t dma_cmd_fifo_num;
+
+    uint32_t regs[XLNX_ZYNQ_DEVCFG_R_MAX];
+    RegisterInfo regs_info[XLNX_ZYNQ_DEVCFG_R_MAX];
+} XlnxZynqDevcfg;
+
+#define XLNX_ZYNQ_DEVCFG_H
+#endif
diff --git a/include/hw/register.h b/include/hw/register.h
new file mode 100644
index 0000000000..8c12233b75
--- /dev/null
+++ b/include/hw/register.h
@@ -0,0 +1,255 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef REGISTER_H
+#define REGISTER_H
+
+#include "hw/qdev-core.h"
+#include "exec/memory.h"
+
+typedef struct RegisterInfo RegisterInfo;
+typedef struct RegisterAccessInfo RegisterAccessInfo;
+typedef struct RegisterInfoArray RegisterInfoArray;
+
+/**
+ * Access description for a register that is part of guest accessible device
+ * state.
+ *
+ * @name: String name of the register
+ * @ro: whether or not the bit is read-only
+ * @w1c: bits with the common write 1 to clear semantic.
+ * @reset: reset value.
+ * @cor: Bits that are clear on read
+ * @rsvd: Bits that are reserved and should not be changed
+ *
+ * @pre_write: Pre write callback. Passed the value that's to be written,
+ * immediately before the actual write. The returned value is what is written,
+ * giving the handler a chance to modify the written value.
+ * @post_write: Post write callback. Passed the written value. Most write side
+ * effects should be implemented here.
+ *
+ * @post_read: Post read callback. Passes the value that is about to be returned
+ * for a read. The return value from this function is what is ultimately read,
+ * allowing this function to modify the value before return to the client.
+ */
+
+struct RegisterAccessInfo {
+    const char *name;
+    uint64_t ro;
+    uint64_t w1c;
+    uint64_t reset;
+    uint64_t cor;
+    uint64_t rsvd;
+    uint64_t unimp;
+
+    uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val);
+    void (*post_write)(RegisterInfo *reg, uint64_t val);
+
+    uint64_t (*post_read)(RegisterInfo *reg, uint64_t val);
+
+    hwaddr addr;
+};
+
+/**
+ * A register that is part of guest accessible state
+ * @data: pointer to the register data. Will be cast
+ * to the relevant uint type depending on data_size.
+ * @data_size: Size of the register in bytes. Must be
+ * 1, 2, 4 or 8
+ *
+ * @access: Access description of this register
+ *
+ * @debug: Whether or not verbose debug is enabled
+ * @prefix: String prefix for log and debug messages
+ *
+ * @opaque: Opaque data for the register
+ */
+
+struct RegisterInfo {
+    /* <private> */
+    DeviceState parent_obj;
+
+    /* <public> */
+    void *data;
+    int data_size;
+
+    const RegisterAccessInfo *access;
+
+    void *opaque;
+};
+
+#define TYPE_REGISTER "qemu,register"
+#define REGISTER(obj) OBJECT_CHECK(RegisterInfo, (obj), TYPE_REGISTER)
+
+/**
+ * This structure is used to group all of the individual registers which are
+ * modeled using the RegisterInfo structure.
+ *
+ * @r is an aray containing of all the relevent RegisterInfo structures.
+ *
+ * @num_elements is the number of elements in the array r
+ *
+ * @mem: optional Memory region for the register
+ */
+
+struct RegisterInfoArray {
+    MemoryRegion mem;
+
+    int num_elements;
+    RegisterInfo **r;
+
+    bool debug;
+    const char *prefix;
+};
+
+/**
+ * write a value to a register, subject to its restrictions
+ * @reg: register to write to
+ * @val: value to write
+ * @we: write enable mask
+ * @prefix: The device prefix that should be printed before the register name
+ * @debug: Should the write operation debug information be printed?
+ */
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we,
+                    const char *prefix, bool debug);
+
+/**
+ * read a value from a register, subject to its restrictions
+ * @reg: register to read from
+ * @re: read enable mask
+ * @prefix: The device prefix that should be printed before the register name
+ * @debug: Should the read operation debug information be printed?
+ * returns: value read
+ */
+
+uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix,
+                       bool debug);
+
+/**
+ * reset a register
+ * @reg: register to reset
+ */
+
+void register_reset(RegisterInfo *reg);
+
+/**
+ * Initialize a register.
+ * @reg: Register to initialize
+ */
+
+void register_init(RegisterInfo *reg);
+
+/**
+ * Memory API MMIO write handler that will write to a Register API register.
+ * @opaque: RegisterInfo to write to
+ * @addr: Address to write
+ * @value: Value to write
+ * @size: Number of bytes to write
+ */
+
+void register_write_memory(void *opaque, hwaddr addr, uint64_t value,
+                           unsigned size);
+
+/**
+ * Memory API MMIO read handler that will read from a Register API register.
+ * @opaque: RegisterInfo to read from
+ * @addr: Address to read
+ * @size: Number of bytes to read
+ * returns: Value read from register
+ */
+
+uint64_t register_read_memory(void *opaque, hwaddr addr, unsigned size);
+
+/**
+ * Init a block of registers into a container MemoryRegion. A
+ * number of constant register definitions are parsed to create a corresponding
+ * array of RegisterInfo's.
+ *
+ * @owner: device owning the registers
+ * @rae: Register definitions to init
+ * @num: number of registers to init (length of @rae)
+ * @ri: Register array to init, must already be allocated
+ * @data: Array to use for register data, must already be allocated
+ * @ops: Memory region ops to access registers.
+ * @debug enabled: turn on/off verbose debug information
+ * returns: A structure containing all of the registers and an initialized
+ *          memory region (r_array->mem) the caller should add to a container.
+ */
+
+RegisterInfoArray *register_init_block32(DeviceState *owner,
+                                         const RegisterAccessInfo *rae,
+                                         int num, RegisterInfo *ri,
+                                         uint32_t *data,
+                                         const MemoryRegionOps *ops,
+                                         bool debug_enabled,
+                                         uint64_t memory_size);
+
+/**
+ * This function should be called to cleanup the registers that were initialized
+ * when calling register_init_block32(). This function should only be called
+ * from the device's instance_finalize function.
+ *
+ * Any memory operations that the device performed that require cleanup (such
+ * as creating subregions) need to be called before calling this function.
+ *
+ * @r_array: A structure containing all of the registers, as returned by
+ *           register_init_block32()
+ */
+
+void register_finalize_block(RegisterInfoArray *r_array);
+
+/* Define constants for a 32 bit register */
+
+/* This macro will define A_FOO, for the byte address of a register
+ * as well as R_FOO for the uint32_t[] register number (A_FOO / 4).
+ */
+#define REG32(reg, addr)                                                  \
+    enum { A_ ## reg = (addr) };                                          \
+    enum { R_ ## reg = (addr) / 4 };
+
+/* Define SHIFT, LENGTH and MASK constants for a field within a register */
+
+/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH 
+ * constants for field BAR in register FOO.
+ */
+#define FIELD(reg, field, shift, length)                                  \
+    enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)};                  \
+    enum { R_ ## reg ## _ ## field ## _LENGTH = (length)};                \
+    enum { R_ ## reg ## _ ## field ## _MASK =                             \
+                                        MAKE_64BIT_MASK(shift, length)};
+
+/* Extract a field from a register */
+#define FIELD_EX32(storage, reg, field)                                   \
+    extract32((storage), R_ ## reg ## _ ## field ## _SHIFT,               \
+              R_ ## reg ## _ ## field ## _LENGTH)
+
+/* Extract a field from an array of registers */
+#define ARRAY_FIELD_EX32(regs, reg, field)                                \
+    FIELD_EX32((regs)[R_ ## reg], reg, field)
+
+/* Deposit a register field.
+ * Assigning values larger then the target field will result in
+ * compilation warnings.
+ */
+#define FIELD_DP32(storage, reg, field, val) ({                           \
+    struct {                                                              \
+        unsigned int v:R_ ## reg ## _ ## field ## _LENGTH;                \
+    } v = { .v = val };                                                   \
+    uint32_t d;                                                           \
+    d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT,           \
+                  R_ ## reg ## _ ## field ## _LENGTH, v.v);               \
+    d; })
+
+/* Deposit a field to array of registers.  */
+#define ARRAY_FIELD_DP32(regs, reg, field, val)                           \
+    (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val);
+
+#endif
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
new file mode 100644
index 0000000000..def3b4507e
--- /dev/null
+++ b/include/hw/ssi/aspeed_smc.h
@@ -0,0 +1,100 @@
+/*
+ * ASPEED AST2400 SMC Controller (SPI Flash Only)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ASPEED_SMC_H
+#define ASPEED_SMC_H
+
+#include "hw/ssi/ssi.h"
+
+typedef struct AspeedSegments {
+    hwaddr addr;
+    uint32_t size;
+} AspeedSegments;
+
+struct AspeedSMCState;
+typedef struct AspeedSMCController {
+    const char *name;
+    uint8_t r_conf;
+    uint8_t r_ce_ctrl;
+    uint8_t r_ctrl0;
+    uint8_t r_timings;
+    uint8_t conf_enable_w0;
+    uint8_t max_slaves;
+    const AspeedSegments *segments;
+    uint32_t mapping_window_size;
+} AspeedSMCController;
+
+typedef struct AspeedSMCFlash {
+    const struct AspeedSMCState *controller;
+
+    uint8_t id;
+    uint32_t size;
+
+    MemoryRegion mmio;
+    DeviceState *flash;
+} AspeedSMCFlash;
+
+#define TYPE_ASPEED_SMC "aspeed.smc"
+#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC)
+#define ASPEED_SMC_CLASS(klass) \
+     OBJECT_CLASS_CHECK(AspeedSMCClass, (klass), TYPE_ASPEED_SMC)
+#define ASPEED_SMC_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(AspeedSMCClass, (obj), TYPE_ASPEED_SMC)
+
+typedef struct  AspeedSMCClass {
+    SysBusDevice parent_obj;
+    const AspeedSMCController *ctrl;
+}  AspeedSMCClass;
+
+#define ASPEED_SMC_R_MAX        (0x100 / 4)
+
+typedef struct AspeedSMCState {
+    SysBusDevice parent_obj;
+
+    const AspeedSMCController *ctrl;
+
+    MemoryRegion mmio;
+    MemoryRegion mmio_flash;
+
+    qemu_irq irq;
+    int irqline;
+
+    uint32_t num_cs;
+    qemu_irq *cs_lines;
+
+    SSIBus *spi;
+
+    uint32_t regs[ASPEED_SMC_R_MAX];
+
+    /* depends on the controller type */
+    uint8_t r_conf;
+    uint8_t r_ce_ctrl;
+    uint8_t r_ctrl0;
+    uint8_t r_timings;
+    uint8_t conf_enable_w0;
+
+    AspeedSMCFlash *flashes;
+} AspeedSMCState;
+
+#endif /* ASPEED_SMC_H */
diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h
index 4a0a53903c..6a0c3c3cdb 100644
--- a/include/hw/ssi/ssi.h
+++ b/include/hw/ssi/ssi.h
@@ -37,7 +37,7 @@ enum SSICSMode {
 struct SSISlaveClass {
     DeviceClass parent_class;
 
-    int (*init)(SSISlave *dev);
+    void (*realize)(SSISlave *dev, Error **errp);
 
     /* if you have standard or no CS behaviour, just override transfer.
      * This is called when the device cs is active (true by default).
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 15418a86df..98fb005aba 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -24,6 +24,9 @@
 #define BIT_WORD(nr)            ((nr) / BITS_PER_LONG)
 #define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
 
+#define MAKE_64BIT_MASK(shift, length) \
+    (((~0ULL) >> (64 - (length))) << (shift))
+
 /**
  * set_bit - Set a bit in memory
  * @nr: the bit to set
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index e8a5aede95..cdf23a723a 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -116,10 +116,10 @@ typedef struct TaskState {
 #endif
 #if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
     /* Extra fields for semihosted binaries.  */
-    uint32_t heap_base;
-    uint32_t heap_limit;
+    abi_ulong heap_base;
+    abi_ulong heap_limit;
 #endif
-    uint32_t stack_base;
+    abi_ulong stack_base;
     int used; /* non zero if used */
     struct image_info *info;
     struct linux_binprm *bprm;
diff --git a/memory.c b/memory.c
index 33799e810b..0eb6895fe6 100644
--- a/memory.c
+++ b/memory.c
@@ -1376,6 +1376,21 @@ void memory_region_init_alias(MemoryRegion *mr,
     mr->alias_offset = offset;
 }
 
+void memory_region_init_rom(MemoryRegion *mr,
+                            struct Object *owner,
+                            const char *name,
+                            uint64_t size,
+                            Error **errp)
+{
+    memory_region_init(mr, owner, name, size);
+    mr->ram = true;
+    mr->readonly = true;
+    mr->terminates = true;
+    mr->destructor = memory_region_destructor_ram;
+    mr->ram_block = qemu_ram_alloc(size, mr, errp);
+    mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+}
+
 void memory_region_init_rom_device(MemoryRegion *mr,
                                    Object *owner,
                                    const MemoryRegionOps *ops,
@@ -1384,6 +1399,7 @@ void memory_region_init_rom_device(MemoryRegion *mr,
                                    uint64_t size,
                                    Error **errp)
 {
+    assert(ops);
     memory_region_init(mr, owner, name, size);
     mr->ops = ops;
     mr->opaque = opaque;
diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin
index e7a7e72e32..57fb4d88b1 100644
--- a/pc-bios/bios-256k.bin
+++ b/pc-bios/bios-256k.bin
Binary files differdiff --git a/pc-bios/bios-fast.bin b/pc-bios/bios-fast.bin
new file mode 100644
index 0000000000..00cda3cb82
--- /dev/null
+++ b/pc-bios/bios-fast.bin
Binary files differdiff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
index b0ae502f6e..8a6869ff1b 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differdiff --git a/roms/Makefile b/roms/Makefile
index 7bd1252737..26b058659a 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -61,9 +61,11 @@ default:
 	@echo "  slof           -- update slof.bin"
 	@echo "  u-boot.e500    -- update u-boot.e500"
 
-bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k
+bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k \
+      build-seabios-config-seabios-fast
 	cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin
 	cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin
+	cp seabios/builds/seabios-fast/bios.bin ../pc-bios/bios-fast.bin
 
 seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants))
 
diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k
index 0a9da77a5e..93203af0de 100644
--- a/roms/config.seabios-128k
+++ b/roms/config.seabios-128k
@@ -2,9 +2,11 @@
 # need to turn off features (xhci,uas) to make it fit into 128k
 CONFIG_QEMU=y
 CONFIG_ROM_SIZE=128
+CONFIG_BOOTSPLASH=n
 CONFIG_XEN=n
 CONFIG_USB_OHCI=n
 CONFIG_USB_XHCI=n
 CONFIG_USB_UAS=n
 CONFIG_SDCARD=n
 CONFIG_TCGBIOS=n
+CONFIG_MPT_SCSI=n
diff --git a/roms/config.seabios-fast b/roms/config.seabios-fast
new file mode 100644
index 0000000000..045693ea59
--- /dev/null
+++ b/roms/config.seabios-fast
@@ -0,0 +1,27 @@
+# The fastest SeaBIOS that can boot Linux using -kernel.
+# NB: Since we are using -kernel, we don't need SeaBIOS itself
+# to do any boot drive detection, boot ordering, etc.
+
+# general stuff
+CONFIG_QEMU=y
+CONFIG_ROM_SIZE=0
+CONFIG_WRITABLE_UPPERMEMORY=y
+CONFIG_RELOCATE_INIT=n
+
+# no input, no boot menu
+CONFIG_MOUSE=n
+CONFIG_KEYBOARD=n
+CONFIG_BOOTMENU=n
+CONFIG_BOOTSPLASH=n
+CONFIG_BOOTORDER=n
+
+# hardware support we don't need
+CONFIG_LPT=n
+CONFIG_SERIAL=n
+CONFIG_USB=n
+CONFIG_DRIVES=n
+CONFIG_TCGBIOS=n
+CONFIG_VGAHOOKS=n
+
+# no logging
+CONFIG_DEBUG_LEVEL=0
diff --git a/roms/seabios b/roms/seabios
-Subproject b3ef39f532db52bf17457ba931da758eeb38d6b
+Subproject e2fc41e24ee0ada60fc511d60b15a41b294538b
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
index 6748e4f60a..1baa1f1c7c 100644
--- a/slirp/Makefile.objs
+++ b/slirp/Makefile.objs
@@ -1,5 +1,5 @@
 common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o \
-               ip_input.o ip_output.o dnssearch.o
+               ip_input.o ip_output.o dnssearch.o dhcpv6.o
 common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
 common-obj-y += tcp_subr.o tcp_timer.o udp.o udp6.o bootp.o tftp.o arp_table.o \
                 ndp_table.o
diff --git a/slirp/dhcpv6.c b/slirp/dhcpv6.c
new file mode 100644
index 0000000000..02c51c7756
--- /dev/null
+++ b/slirp/dhcpv6.c
@@ -0,0 +1,209 @@
+/*
+ * SLIRP stateless DHCPv6
+ *
+ * We only support stateless DHCPv6, e.g. for network booting.
+ * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details.
+ *
+ * Copyright 2016 Thomas Huth, Red Hat Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/log.h"
+#include "slirp.h"
+#include "dhcpv6.h"
+
+/* DHCPv6 message types */
+#define MSGTYPE_REPLY        7
+#define MSGTYPE_INFO_REQUEST 11
+
+/* DHCPv6 option types */
+#define OPTION_CLIENTID      1
+#define OPTION_IAADDR        5
+#define OPTION_ORO           6
+#define OPTION_DNS_SERVERS   23
+#define OPTION_BOOTFILE_URL  59
+
+struct requested_infos {
+    uint8_t *client_id;
+    int client_id_len;
+    bool want_dns;
+    bool want_boot_url;
+};
+
+/**
+ * Analyze the info request message sent by the client to see what data it
+ * provided and what it wants to have. The information is gathered in the
+ * "requested_infos" struct. Note that client_id (if provided) points into
+ * the odata region, thus the caller must keep odata valid as long as it
+ * needs to access the requested_infos struct.
+ */
+static int dhcpv6_parse_info_request(uint8_t *odata, int olen,
+                                     struct requested_infos *ri)
+{
+    int i, req_opt;
+
+    while (olen > 4) {
+        /* Parse one option */
+        int option = odata[0] << 8 | odata[1];
+        int len = odata[2] << 8 | odata[3];
+
+        if (len + 4 > olen) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Guest sent bad DHCPv6 packet!\n");
+            return -E2BIG;
+        }
+
+        switch (option) {
+        case OPTION_IAADDR:
+            /* According to RFC3315, we must discard requests with IA option */
+            return -EINVAL;
+        case OPTION_CLIENTID:
+            if (len > 256) {
+                /* Avoid very long IDs which could cause problems later */
+                return -E2BIG;
+            }
+            ri->client_id = odata + 4;
+            ri->client_id_len = len;
+            break;
+        case OPTION_ORO:        /* Option request option */
+            if (len & 1) {
+                return -EINVAL;
+            }
+            /* Check which options the client wants to have */
+            for (i = 0; i < len; i += 2) {
+                req_opt = odata[4 + i] << 8 | odata[4 + i + 1];
+                switch (req_opt) {
+                case OPTION_DNS_SERVERS:
+                    ri->want_dns = true;
+                    break;
+                case OPTION_BOOTFILE_URL:
+                    ri->want_boot_url = true;
+                    break;
+                default:
+                    DEBUG_MISC((dfd, "dhcpv6: Unsupported option request %d\n",
+                                req_opt));
+                }
+            }
+            break;
+        default:
+            DEBUG_MISC((dfd, "dhcpv6 info req: Unsupported option %d, len=%d\n",
+                        option, len));
+        }
+
+        odata += len + 4;
+        olen -= len + 4;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Handle information request messages
+ */
+static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas,
+                                uint32_t xid, uint8_t *odata, int olen)
+{
+    struct requested_infos ri = { NULL };
+    struct sockaddr_in6 sa6, da6;
+    struct mbuf *m;
+    uint8_t *resp;
+
+    if (dhcpv6_parse_info_request(odata, olen, &ri) < 0) {
+        return;
+    }
+
+    m = m_get(slirp);
+    if (!m) {
+        return;
+    }
+    memset(m->m_data, 0, m->m_size);
+    m->m_data += IF_MAXLINKHDR;
+    resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr);
+
+    /* Fill in response */
+    *resp++ = MSGTYPE_REPLY;
+    *resp++ = (uint8_t)(xid >> 16);
+    *resp++ = (uint8_t)(xid >> 8);
+    *resp++ = (uint8_t)xid;
+
+    if (ri.client_id) {
+        *resp++ = OPTION_CLIENTID >> 8;         /* option-code high byte */
+        *resp++ = OPTION_CLIENTID;              /* option-code low byte */
+        *resp++ = ri.client_id_len >> 8;        /* option-len high byte */
+        *resp++ = ri.client_id_len;             /* option-len low byte */
+        memcpy(resp, ri.client_id, ri.client_id_len);
+        resp += ri.client_id_len;
+    }
+    if (ri.want_dns) {
+        *resp++ = OPTION_DNS_SERVERS >> 8;      /* option-code high byte */
+        *resp++ = OPTION_DNS_SERVERS;           /* option-code low byte */
+        *resp++ = 0;                            /* option-len high byte */
+        *resp++ = 16;                           /* option-len low byte */
+        memcpy(resp, &slirp->vnameserver_addr6, 16);
+        resp += 16;
+    }
+    if (ri.want_boot_url) {
+        uint8_t *sa = slirp->vhost_addr6.s6_addr;
+        int slen, smaxlen;
+
+        *resp++ = OPTION_BOOTFILE_URL >> 8;     /* option-code high byte */
+        *resp++ = OPTION_BOOTFILE_URL;          /* option-code low byte */
+        smaxlen = (uint8_t *)m->m_data + IF_MTU - (resp + 2);
+        slen = snprintf((char *)resp + 2, smaxlen,
+                        "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
+                                "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s",
+                        sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7],
+                        sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14],
+                        sa[15], slirp->bootp_filename);
+        slen = min(slen, smaxlen);
+        *resp++ = slen >> 8;                    /* option-len high byte */
+        *resp++ = slen;                         /* option-len low byte */
+        resp += slen;
+    }
+
+    sa6.sin6_addr = slirp->vhost_addr6;
+    sa6.sin6_port = DHCPV6_SERVER_PORT;
+    da6.sin6_addr = srcsas->sin6_addr;
+    da6.sin6_port = srcsas->sin6_port;
+    m->m_data += sizeof(struct ip6) + sizeof(struct udphdr);
+    m->m_len = resp - (uint8_t *)m->m_data;
+    udp6_output(NULL, m, &sa6, &da6);
+}
+
+/**
+ * Handle DHCPv6 messages sent by the client
+ */
+void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m)
+{
+    uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr);
+    int data_len = m->m_len - sizeof(struct udphdr);
+    uint32_t xid;
+
+    if (data_len < 4) {
+        return;
+    }
+
+    xid = ntohl(*(uint32_t *)data) & 0xffffff;
+
+    switch (data[0]) {
+    case MSGTYPE_INFO_REQUEST:
+        dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4);
+        break;
+    default:
+        DEBUG_MISC((dfd, "dhcpv6_input: Unsupported message type 0x%x\n",
+                    data[0]));
+    }
+}
diff --git a/slirp/dhcpv6.h b/slirp/dhcpv6.h
new file mode 100644
index 0000000000..9189cd3f2d
--- /dev/null
+++ b/slirp/dhcpv6.h
@@ -0,0 +1,22 @@
+/*
+ * Definitions and prototypes for SLIRP stateless DHCPv6
+ *
+ * Copyright 2016 Thomas Huth, Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2
+ * or later. See the COPYING file in the top-level directory.
+ */
+#ifndef SLIRP_DHCPV6_H
+#define SLIRP_DHCPV6_H
+
+#define DHCPV6_SERVER_PORT 547
+
+#define ALLDHCP_MULTICAST { .s6_addr = \
+                            { 0xff, 0x02, 0x00, 0x00,\
+                            0x00, 0x00, 0x00, 0x00,\
+                            0x00, 0x00, 0x00, 0x00,\
+                            0x00, 0x01, 0x00, 0x02 } }
+
+void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m);
+
+#endif
diff --git a/slirp/ip6.h b/slirp/ip6.h
index 8ddfa242c4..da23de66f1 100644
--- a/slirp/ip6.h
+++ b/slirp/ip6.h
@@ -26,6 +26,12 @@
                         0x00, 0x00, 0x00, 0x00,\
                         0x00, 0x00, 0x00, 0x02 } }
 
+#define ZERO_ADDR  { .s6_addr = \
+                        { 0x00, 0x00, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x00 } }
+
 static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b)
 {
     return memcmp(a, b, sizeof(*a)) == 0;
@@ -84,6 +90,9 @@ static inline bool in6_equal_mach(const struct in6_addr *a,
 #define in6_solicitednode_multicast(a)\
     (in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104))
 
+#define in6_zero(a)\
+    (in6_equal(a, &(struct in6_addr)ZERO_ADDR))
+
 /* Compute emulated host MAC address from its ipv6 address */
 static inline void in6_compute_ethaddr(struct in6_addr ip,
                                        uint8_t eth[ETH_ALEN])
diff --git a/slirp/ip6_icmp.c b/slirp/ip6_icmp.c
index 48016a9f85..6d18e28985 100644
--- a/slirp/ip6_icmp.c
+++ b/slirp/ip6_icmp.c
@@ -148,7 +148,11 @@ void ndp_send_ra(Slirp *slirp)
     rip->ip_nh = IPPROTO_ICMPV6;
     rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
                         + NDPOPT_LINKLAYER_LEN
-                        + NDPOPT_PREFIXINFO_LEN);
+                        + NDPOPT_PREFIXINFO_LEN
+#ifndef _WIN32
+                        + NDPOPT_RDNSS_LEN
+#endif
+                        );
     t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 
     /* Build ICMPv6 packet */
@@ -166,16 +170,16 @@ void ndp_send_ra(Slirp *slirp)
     ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
     ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
     ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
+    t->m_data += ICMP6_NDP_RA_MINLEN;
 
     /* Source link-layer address (NDP option) */
-    t->m_data += ICMP6_NDP_RA_MINLEN;
     struct ndpopt *opt = mtod(t, struct ndpopt *);
     opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
     opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
     in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
+    t->m_data += NDPOPT_LINKLAYER_LEN;
 
     /* Prefix information (NDP option) */
-    t->m_data += NDPOPT_LINKLAYER_LEN;
     struct ndpopt *opt2 = mtod(t, struct ndpopt *);
     opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
     opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
@@ -187,8 +191,25 @@ void ndp_send_ra(Slirp *slirp)
     opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
     opt2->ndpopt_prefixinfo.reserved2 = 0;
     opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
+    t->m_data += NDPOPT_PREFIXINFO_LEN;
+
+#ifndef _WIN32
+    /* Prefix information (NDP option) */
+    /* disabled for windows for now, until get_dns6_addr is implemented */
+    struct ndpopt *opt3 = mtod(t, struct ndpopt *);
+    opt3->ndpopt_type = NDPOPT_RDNSS;
+    opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
+    opt3->ndpopt_rdnss.reserved = 0;
+    opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
+    opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
+    t->m_data += NDPOPT_RDNSS_LEN;
+#endif
 
     /* ICMPv6 Checksum */
+#ifndef _WIN32
+    t->m_data -= NDPOPT_RDNSS_LEN;
+#endif
+    t->m_data -= NDPOPT_PREFIXINFO_LEN;
     t->m_data -= NDPOPT_LINKLAYER_LEN;
     t->m_data -= ICMP6_NDP_RA_MINLEN;
     t->m_data -= sizeof(struct ip6);
diff --git a/slirp/ip6_icmp.h b/slirp/ip6_icmp.h
index 9460bf837a..2282d29076 100644
--- a/slirp/ip6_icmp.h
+++ b/slirp/ip6_icmp.h
@@ -122,6 +122,7 @@ struct ndpopt {
     uint8_t     ndpopt_len;                     /* /!\ In units of 8 octets */
     union {
         unsigned char   linklayer_addr[6];      /* Source/Target Link-layer */
+#define ndpopt_linklayer ndpopt_body.linklayer_addr
         struct prefixinfo {                     /* Prefix Information */
             uint8_t     prefix_length;
 #ifdef HOST_WORDS_BIGENDIAN
@@ -134,19 +135,26 @@ struct ndpopt {
             uint32_t    reserved2;
             struct in6_addr prefix;
         } QEMU_PACKED prefixinfo;
-    } ndpopt_body;
-#define ndpopt_linklayer ndpopt_body.linklayer_addr
 #define ndpopt_prefixinfo ndpopt_body.prefixinfo
+        struct rdnss {
+            uint16_t reserved;
+            uint32_t lifetime;
+            struct in6_addr addr;
+        } QEMU_PACKED rdnss;
+#define ndpopt_rdnss ndpopt_body.rdnss
+    } ndpopt_body;
 } QEMU_PACKED;
 
 /* NDP options type */
 #define NDPOPT_LINKLAYER_SOURCE     1   /* Source Link-Layer Address */
 #define NDPOPT_LINKLAYER_TARGET     2   /* Target Link-Layer Address */
 #define NDPOPT_PREFIX_INFO          3   /* Prefix Information */
+#define NDPOPT_RDNSS                25  /* Recursive DNS Server Address */
 
 /* NDP options size, in octets. */
 #define NDPOPT_LINKLAYER_LEN    8
 #define NDPOPT_PREFIXINFO_LEN   32
+#define NDPOPT_RDNSS_LEN        24
 
 /*
  * Definition of type and code field values.
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 127aa41d40..b6fc584219 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -7,6 +7,7 @@ struct Slirp;
 typedef struct Slirp Slirp;
 
 int get_dns_addr(struct in_addr *pdns_addr);
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id);
 
 Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
                   struct in_addr vnetmask, struct in_addr vhost,
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 9f4bea3d3b..7eb183d0e9 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -30,6 +30,10 @@
 #include "hw/hw.h"
 #include "qemu/cutils.h"
 
+#ifndef _WIN32
+#include <net/if.h>
+#endif
+
 /* host loopback address */
 struct in_addr loopback_addr;
 /* host loopback network mask */
@@ -46,7 +50,13 @@ static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
     QTAILQ_HEAD_INITIALIZER(slirp_instances);
 
 static struct in_addr dns_addr;
+#ifndef _WIN32
+static struct in6_addr dns6_addr;
+#endif
 static u_int dns_addr_time;
+#ifndef _WIN32
+static u_int dns6_addr_time;
+#endif
 
 #define TIMEOUT_FAST 2  /* milliseconds */
 #define TIMEOUT_SLOW 499  /* milliseconds */
@@ -100,6 +110,11 @@ int get_dns_addr(struct in_addr *pdns_addr)
     return 0;
 }
 
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
+{
+    return -1;
+}
+
 static void winsock_cleanup(void)
 {
     WSACleanup();
@@ -107,33 +122,39 @@ static void winsock_cleanup(void)
 
 #else
 
-static struct stat dns_addr_stat;
+static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
+                               socklen_t addrlen,
+                               struct stat *cached_stat, u_int *cached_time)
+{
+    struct stat old_stat;
+    if (curtime - *cached_time < TIMEOUT_DEFAULT) {
+        memcpy(pdns_addr, cached_addr, addrlen);
+        return 0;
+    }
+    old_stat = *cached_stat;
+    if (stat("/etc/resolv.conf", cached_stat) != 0) {
+        return -1;
+    }
+    if (cached_stat->st_dev == old_stat.st_dev
+        && cached_stat->st_ino == old_stat.st_ino
+        && cached_stat->st_size == old_stat.st_size
+        && cached_stat->st_mtime == old_stat.st_mtime) {
+        memcpy(pdns_addr, cached_addr, addrlen);
+        return 0;
+    }
+    return 1;
+}
 
-int get_dns_addr(struct in_addr *pdns_addr)
+static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
+                                    socklen_t addrlen, uint32_t *scope_id,
+                                    u_int *cached_time)
 {
     char buff[512];
     char buff2[257];
     FILE *f;
     int found = 0;
-    struct in_addr tmp_addr;
-
-    if (dns_addr.s_addr != 0) {
-        struct stat old_stat;
-        if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
-            *pdns_addr = dns_addr;
-            return 0;
-        }
-        old_stat = dns_addr_stat;
-        if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
-            return -1;
-        if ((dns_addr_stat.st_dev == old_stat.st_dev)
-            && (dns_addr_stat.st_ino == old_stat.st_ino)
-            && (dns_addr_stat.st_size == old_stat.st_size)
-            && (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
-            *pdns_addr = dns_addr;
-            return 0;
-        }
-    }
+    void *tmp_addr = alloca(addrlen);
+    unsigned if_index;
 
     f = fopen("/etc/resolv.conf", "r");
     if (!f)
@@ -144,13 +165,25 @@ int get_dns_addr(struct in_addr *pdns_addr)
 #endif
     while (fgets(buff, 512, f) != NULL) {
         if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
-            if (!inet_aton(buff2, &tmp_addr))
+            char *c = strchr(buff2, '%');
+            if (c) {
+                if_index = if_nametoindex(c + 1);
+                *c = '\0';
+            } else {
+                if_index = 0;
+            }
+
+            if (!inet_pton(af, buff2, tmp_addr)) {
                 continue;
+            }
             /* If it's the first one, set it to dns_addr */
             if (!found) {
-                *pdns_addr = tmp_addr;
-                dns_addr = tmp_addr;
-                dns_addr_time = curtime;
+                memcpy(pdns_addr, tmp_addr, addrlen);
+                memcpy(cached_addr, tmp_addr, addrlen);
+                if (scope_id) {
+                    *scope_id = if_index;
+                }
+                *cached_time = curtime;
             }
 #ifdef DEBUG
             else
@@ -163,8 +196,14 @@ int get_dns_addr(struct in_addr *pdns_addr)
                 break;
             }
 #ifdef DEBUG
-            else
-                fprintf(stderr, "%s", inet_ntoa(tmp_addr));
+            else {
+                char s[INET6_ADDRSTRLEN];
+                char *res = inet_ntop(af, tmp_addr, s, sizeof(s));
+                if (!res) {
+                    res = "(string conversion error)";
+                }
+                fprintf(stderr, "%s", res);
+            }
 #endif
         }
     }
@@ -174,6 +213,39 @@ int get_dns_addr(struct in_addr *pdns_addr)
     return 0;
 }
 
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+    static struct stat dns_addr_stat;
+
+    if (dns_addr.s_addr != 0) {
+        int ret;
+        ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr),
+                                  &dns_addr_stat, &dns_addr_time);
+        if (ret <= 0) {
+            return ret;
+        }
+    }
+    return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr,
+                                    sizeof(dns_addr), NULL, &dns_addr_time);
+}
+
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
+{
+    static struct stat dns6_addr_stat;
+
+    if (!in6_zero(&dns6_addr)) {
+        int ret;
+        ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),
+                                  &dns6_addr_stat, &dns6_addr_time);
+        if (ret <= 0) {
+            return ret;
+        }
+    }
+    return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr,
+                                    sizeof(dns6_addr),
+                                    scope_id, &dns6_addr_time);
+}
+
 #endif
 
 static void slirp_init_once(void)
diff --git a/slirp/socket.c b/slirp/socket.c
index b336586c7b..02e89ce1f2 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -816,9 +816,12 @@ void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
         if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6,
                     slirp->vprefix_len)) {
             if (in6_equal(&so->so_faddr6, &slirp->vnameserver_addr6)) {
-                /*if (get_dns_addr(&addr) < 0) {*/ /* TODO */
+                uint32_t scope_id;
+                if (get_dns6_addr(&sin6->sin6_addr, &scope_id) >= 0) {
+                    sin6->sin6_scope_id = scope_id;
+                } else {
                     sin6->sin6_addr = in6addr_loopback;
-                /*}*/
+                }
             } else {
                 sin6->sin6_addr = in6addr_loopback;
             }
diff --git a/slirp/tftp.c b/slirp/tftp.c
index 12b5ff6e25..367340222d 100644
--- a/slirp/tftp.c
+++ b/slirp/tftp.c
@@ -208,8 +208,6 @@ static void tftp_send_error(struct tftp_session *spt,
     goto out;
   }
 
-  memset(m->m_data, 0, m->m_size);
-
   tp = tftp_prep_mbuf_data(spt, m);
 
   tp->tp_op = htons(TFTP_ERROR);
@@ -237,8 +235,6 @@ static void tftp_send_next_block(struct tftp_session *spt,
     return;
   }
 
-  memset(m->m_data, 0, m->m_size);
-
   tp = tftp_prep_mbuf_data(spt, m);
 
   tp->tp_op = htons(TFTP_DATA);
diff --git a/slirp/udp6.c b/slirp/udp6.c
index 94efb13240..9fa314bc2d 100644
--- a/slirp/udp6.c
+++ b/slirp/udp6.c
@@ -7,6 +7,7 @@
 #include "qemu-common.h"
 #include "slirp.h"
 #include "udp.h"
+#include "dhcpv6.h"
 
 void udp6_input(struct mbuf *m)
 {
@@ -61,7 +62,17 @@ void udp6_input(struct mbuf *m)
     lhost.sin6_addr = ip->ip_src;
     lhost.sin6_port = uh->uh_sport;
 
-    /* TODO handle DHCP/BOOTP */
+    /* handle DHCPv6 */
+    if (ntohs(uh->uh_dport) == DHCPV6_SERVER_PORT &&
+        (in6_equal(&ip->ip_dst, &slirp->vhost_addr6) ||
+         in6_equal(&ip->ip_dst, &(struct in6_addr)ALLDHCP_MULTICAST))) {
+        m->m_data += iphlen;
+        m->m_len -= iphlen;
+        dhcpv6_input(&lhost, m);
+        m->m_data -= iphlen;
+        m->m_len += iphlen;
+        goto bad;
+    }
 
     /* handle TFTP */
     if (ntohs(uh->uh_dport) == TFTP_SERVER &&
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index 8be0645eb0..d50726f65d 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -564,8 +564,10 @@ target_ulong do_arm_semihosting(CPUARMState *env)
         }
     case TARGET_SYS_HEAPINFO:
         {
-            uint32_t *ptr;
+            target_ulong retvals[4];
             uint32_t limit;
+            int i;
+
             GET_ARG(0);
 
 #ifdef CONFIG_USER_ONLY
@@ -587,30 +589,33 @@ target_ulong do_arm_semihosting(CPUARMState *env)
                 ts->heap_limit = limit;
             }
 
-            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
-            if (!ptr) {
-                /* FIXME - should this error code be -TARGET_EFAULT ? */
-                return (uint32_t)-1;
-            }
-            ptr[0] = tswap32(ts->heap_base);
-            ptr[1] = tswap32(ts->heap_limit);
-            ptr[2] = tswap32(ts->stack_base);
-            ptr[3] = tswap32(0); /* Stack limit.  */
-            unlock_user(ptr, arg0, 16);
+            retvals[0] = ts->heap_base;
+            retvals[1] = ts->heap_limit;
+            retvals[2] = ts->stack_base;
+            retvals[3] = 0; /* Stack limit.  */
 #else
             limit = ram_size;
-            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
-            if (!ptr) {
-                /* FIXME - should this error code be -TARGET_EFAULT ? */
-                return (uint32_t)-1;
-            }
             /* TODO: Make this use the limit of the loaded application.  */
-            ptr[0] = tswap32(limit / 2);
-            ptr[1] = tswap32(limit);
-            ptr[2] = tswap32(limit); /* Stack base */
-            ptr[3] = tswap32(0); /* Stack limit.  */
-            unlock_user(ptr, arg0, 16);
+            retvals[0] = limit / 2;
+            retvals[1] = limit;
+            retvals[2] = limit; /* Stack base */
+            retvals[3] = 0; /* Stack limit.  */
 #endif
+
+            for (i = 0; i < ARRAY_SIZE(retvals); i++) {
+                bool fail;
+
+                if (is_a64(env)) {
+                    fail = put_user_u64(retvals[i], arg0 + i * 8);
+                } else {
+                    fail = put_user_u32(retvals[i], arg0 + i * 4);
+                }
+
+                if (fail) {
+                    /* Couldn't write back to argument block */
+                    return -1;
+                }
+            }
             return 0;
         }
     case TARGET_SYS_EXIT: