diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index 5510a8a374..4a9de0e10c 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -18,12 +18,14 @@
 #include "hw/arm/ast2400.h"
 #include "hw/char/serial.h"
 #include "qemu/log.h"
+#include "hw/i2c/aspeed_i2c.h"
 
 #define AST2400_UART_5_BASE      0x00184000
 #define AST2400_IOMEM_SIZE       0x00200000
 #define AST2400_IOMEM_BASE       0x1E600000
 #define AST2400_VIC_BASE         0x1E6C0000
 #define AST2400_TIMER_BASE       0x1E782000
+#define AST2400_I2C_BASE         0x1E78A000
 
 static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
 static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
@@ -66,6 +68,10 @@ static void ast2400_init(Object *obj)
     object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
     object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
     qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
+
+    object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
+    object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
+    qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
 }
 
 static void ast2400_realize(DeviceState *dev, Error **errp)
@@ -110,6 +116,16 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
         serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
                        uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
     }
+
+    /* I2C */
+    object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    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));
 }
 
 static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 234d518430..2e641a3989 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -14,6 +14,7 @@
 #include "hw/misc/bcm2835_mbox_defs.h"
 #include "hw/arm/raspi_platform.h"
 #include "sysemu/char.h"
+#include "sysemu/sysemu.h"
 
 /* Peripheral base address on the VC (GPU) system bus */
 #define BCM2835_VC_PERI_BASE 0x7e000000
@@ -106,7 +107,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     MemoryRegion *ram;
     Error *err = NULL;
     uint32_t ram_size, vcram_size;
-    CharDriverState *chr;
     int n;
 
     obj = object_property_get_link(OBJECT(dev), "ram", &err);
@@ -147,6 +147,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
 
     /* UART0 */
+    qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hds[0]);
     object_property_set_bool(OBJECT(s->uart0), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
@@ -158,17 +159,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(s->uart0, 0,
         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
                                INTERRUPT_UART));
-
     /* AUX / UART1 */
-    /* TODO: don't call qemu_char_get_next_serial() here, instead set
-     * chardev properties for each uart at the board level, once pl011
-     * (uart0) has been updated to avoid qemu_char_get_next_serial()
-     */
-    chr = qemu_char_get_next_serial();
-    if (chr == NULL) {
-        chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
-    }
-    qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
+    qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hds[1]);
 
     object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
     if (err) {
@@ -292,8 +284,6 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
     DeviceClass *dc = DEVICE_CLASS(oc);
 
     dc->realize = bcm2835_peripherals_realize;
-    /* Reason: realize() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
 }
 
 static const TypeInfo bcm2835_peripherals_type_info = {
diff --git a/hw/arm/digic.c b/hw/arm/digic.c
index e0f9730325..d60ea395f4 100644
--- a/hw/arm/digic.c
+++ b/hw/arm/digic.c
@@ -23,6 +23,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "hw/arm/digic.h"
+#include "sysemu/sysemu.h"
 
 #define DIGIC4_TIMER_BASE(n)    (0xc0210000 + (n) * 0x100)
 
@@ -84,6 +85,7 @@ static void digic_realize(DeviceState *dev, Error **errp)
         sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i));
     }
 
+    qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hds[0]);
     object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
     if (err != NULL) {
         error_propagate(errp, err);
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index 41029a651d..80e5fd458b 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -30,6 +30,7 @@
 #include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
+#include "hw/char/pl011.h"
 
 #define SMP_BOOT_ADDR           0x100
 #define SMP_BOOT_REG            0x40
@@ -326,7 +327,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
     busdev = SYS_BUS_DEVICE(dev);
     sysbus_mmio_map(busdev, 0, 0xfff34000);
     sysbus_connect_irq(busdev, 0, pic[18]);
-    sysbus_create_simple("pl011", 0xfff36000, pic[20]);
+    pl011_create(0xfff36000, pic[20], serial_hds[0]);
 
     dev = qdev_create(NULL, "highbank-regs");
     qdev_init_nofail(dev);
diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c
index 24f16874f9..96dc150025 100644
--- a/hw/arm/integratorcp.c
+++ b/hw/arm/integratorcp.c
@@ -20,6 +20,7 @@
 #include "exec/address-spaces.h"
 #include "sysemu/sysemu.h"
 #include "qemu/error-report.h"
+#include "hw/char/pl011.h"
 
 #define TYPE_INTEGRATOR_CM "integrator_core"
 #define INTEGRATOR_CM(obj) \
@@ -588,8 +589,8 @@ static void integratorcp_init(MachineState *machine)
     sysbus_create_varargs("integrator_pit", 0x13000000,
                           pic[5], pic[6], pic[7], NULL);
     sysbus_create_simple("pl031", 0x15000000, pic[8]);
-    sysbus_create_simple("pl011", 0x16000000, pic[1]);
-    sysbus_create_simple("pl011", 0x17000000, pic[2]);
+    pl011_create(0x16000000, pic[1], serial_hds[0]);
+    pl011_create(0x17000000, pic[2], serial_hds[1]);
     icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000,
                                qdev_get_gpio_in(sic, 3));
     sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]);
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 3222b360e4..7d0aa6ff4f 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -23,6 +23,7 @@
 #include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
+#include "hw/char/pl011.h"
 
 #define SMP_BOOT_ADDR 0xe0000000
 #define SMP_BOOTREG_ADDR 0x10000030
@@ -202,10 +203,10 @@ static void realview_init(MachineState *machine,
     sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]);
     sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]);
 
-    sysbus_create_simple("pl011", 0x10009000, pic[12]);
-    sysbus_create_simple("pl011", 0x1000a000, pic[13]);
-    sysbus_create_simple("pl011", 0x1000b000, pic[14]);
-    sysbus_create_simple("pl011", 0x1000c000, pic[15]);
+    pl011_create(0x10009000, pic[12], serial_hds[0]);
+    pl011_create(0x1000a000, pic[13], serial_hds[1]);
+    pl011_create(0x1000b000, pic[14], serial_hds[2]);
+    pl011_create(0x1000c000, pic[15], serial_hds[3]);
 
     /* DMA controller is optional, apparently.  */
     sysbus_create_simple("pl081", 0x10030000, pic[24]);
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 44591716fe..794a3ada71 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -20,6 +20,7 @@
 #include "qemu/log.h"
 #include "exec/address-spaces.h"
 #include "sysemu/sysemu.h"
+#include "hw/char/pl011.h"
 
 #define GPIO_A 0
 #define GPIO_B 1
@@ -1303,8 +1304,9 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
 
     for (i = 0; i < 4; i++) {
         if (board->dc2 & (1 << i)) {
-            sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
-                                 qdev_get_gpio_in(nvic, uart_irq[i]));
+            pl011_luminary_create(0x4000c000 + i * 0x1000,
+                                  qdev_get_gpio_in(nvic, uart_irq[i]),
+                                  serial_hds[i]);
         }
     }
     if (board->dc2 & (1 << 4)) {
diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c
index 9c1dafbb19..de26b8caff 100644
--- a/hw/arm/stm32f205_soc.c
+++ b/hw/arm/stm32f205_soc.c
@@ -107,6 +107,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
     /* Attach UART (uses USART registers) and USART controllers */
     for (i = 0; i < STM_NUM_USARTS; i++) {
         usartdev = DEVICE(&(s->usart[i]));
+        qdev_prop_set_chr(usartdev, "chardev", i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL);
         object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err);
         if (err != NULL) {
             error_propagate(errp, err);
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index d079bc9e82..20dd3561c8 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -23,6 +23,7 @@
 #include "exec/address-spaces.h"
 #include "hw/block/flash.h"
 #include "qemu/error-report.h"
+#include "hw/char/pl011.h"
 
 #define VERSATILE_FLASH_ADDR 0x34000000
 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024)
@@ -284,10 +285,10 @@ static void versatile_init(MachineState *machine, int board_id)
         n--;
     }
 
-    sysbus_create_simple("pl011", 0x101f1000, pic[12]);
-    sysbus_create_simple("pl011", 0x101f2000, pic[13]);
-    sysbus_create_simple("pl011", 0x101f3000, pic[14]);
-    sysbus_create_simple("pl011", 0x10009000, sic[6]);
+    pl011_create(0x101f1000, pic[12], serial_hds[0]);
+    pl011_create(0x101f2000, pic[13], serial_hds[1]);
+    pl011_create(0x101f3000, pic[14], serial_hds[2]);
+    pl011_create(0x10009000, sic[6], serial_hds[3]);
 
     sysbus_create_simple("pl080", 0x10130000, pic[17]);
     sysbus_create_simple("sp804", 0x101e2000, pic[4]);
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 70b3e701e0..58760f40ca 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -39,6 +39,7 @@
 #include "sysemu/device_tree.h"
 #include "qemu/error-report.h"
 #include <libfdt.h>
+#include "hw/char/pl011.h"
 
 #define VEXPRESS_BOARD_ID 0x8e0
 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
@@ -631,10 +632,10 @@ static void vexpress_common_init(MachineState *machine)
     sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]);
     sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]);
 
-    sysbus_create_simple("pl011", map[VE_UART0], pic[5]);
-    sysbus_create_simple("pl011", map[VE_UART1], pic[6]);
-    sysbus_create_simple("pl011", map[VE_UART2], pic[7]);
-    sysbus_create_simple("pl011", map[VE_UART3], pic[8]);
+    pl011_create(map[VE_UART0], pic[5], serial_hds[0]);
+    pl011_create(map[VE_UART1], pic[6], serial_hds[1]);
+    pl011_create(map[VE_UART2], pic[7], serial_hds[2]);
+    pl011_create(map[VE_UART3], pic[8], serial_hds[3]);
 
     sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
     sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 26a7bac48f..83a54203b8 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -231,7 +231,8 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
         aml_append(rbuf,
             aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
                              AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
-                             base_mmio_high, base_mmio_high, 0x0000,
+                             base_mmio_high,
+                             base_mmio_high + size_mmio_high - 1, 0x0000,
                              size_mmio_high));
     }
 
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e77ed88afb..8e46137e9b 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -536,6 +536,7 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart,
     DeviceState *dev = qdev_create(NULL, "pl011");
     SysBusDevice *s = SYS_BUS_DEVICE(dev);
 
+    qdev_prop_set_chr(dev, "chardev", serial_hds[0]);
     qdev_init_nofail(dev);
     memory_region_add_subregion(mem, base,
                                 sysbus_mmio_get_region(s, 0));
@@ -1122,10 +1123,14 @@ static void machvirt_init(MachineState *machine)
      * KVM is not available yet
      */
     if (!gic_version) {
+        if (!kvm_enabled()) {
+            error_report("gic-version=host requires KVM");
+            exit(1);
+        }
+
         gic_version = kvm_arm_vgic_probe();
         if (!gic_version) {
             error_report("Unable to determine GIC version supported by host");
-            error_printf("KVM acceleration is probably not supported\n");
             exit(1);
         }
     }
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 98b17c9aed..aefebcfa6d 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -32,6 +32,7 @@
 #include "hw/ssi/ssi.h"
 #include "qemu/error-report.h"
 #include "hw/sd/sd.h"
+#include "hw/char/cadence_uart.h"
 
 #define NUM_SPI_FLASHES 4
 #define NUM_QSPI_FLASHES 2
@@ -235,8 +236,8 @@ static void zynq_init(MachineState *machine)
     sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
     sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
 
-    sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
-    sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
+    cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hds[0]);
+    cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hds[1]);
 
     sysbus_create_varargs("cadence_ttc", 0xF8001000,
             pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c
index 40f7cc1425..34b4641712 100644
--- a/hw/arm/xlnx-ep108.c
+++ b/hw/arm/xlnx-ep108.c
@@ -114,3 +114,11 @@ static void xlnx_ep108_machine_init(MachineClass *mc)
 }
 
 DEFINE_MACHINE("xlnx-ep108", xlnx_ep108_machine_init)
+
+static void xlnx_zcu102_machine_init(MachineClass *mc)
+{
+    mc->desc = "Xilinx ZynqMP ZCU102 board";
+    mc->init = xlnx_ep108_init;
+}
+
+DEFINE_MACHINE("xlnx-zcu102", xlnx_zcu102_machine_init)
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 4d504da643..308d6770c0 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -22,6 +22,8 @@
 #include "hw/arm/xlnx-zynqmp.h"
 #include "hw/intc/arm_gic_common.h"
 #include "exec/address-spaces.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
 
 #define GIC_NUM_SPI_INTR 160
 
@@ -83,6 +85,41 @@ static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index)
     return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index;
 }
 
+static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
+                                   Error **errp)
+{
+    Error *err = NULL;
+    int i;
+
+    for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
+        char *name;
+
+        object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
+                          "cortex-r5-" TYPE_ARM_CPU);
+        object_property_add_child(OBJECT(s), "rpu-cpu[*]",
+                                  OBJECT(&s->rpu_cpu[i]), &error_abort);
+
+        name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
+        if (strcmp(name, boot_cpu)) {
+            /* Secondary CPUs start in PSCI powered-down state */
+            object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true,
+                                     "start-powered-off", &error_abort);
+        } else {
+            s->boot_cpu_ptr = &s->rpu_cpu[i];
+        }
+        g_free(name);
+
+        object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
+                                 &error_abort);
+        object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
+                                 &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+}
+
 static void xlnx_zynqmp_init(Object *obj)
 {
     XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
@@ -95,19 +132,12 @@ static void xlnx_zynqmp_init(Object *obj)
                                   &error_abort);
     }
 
-    for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
-        object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
-                          "cortex-r5-" TYPE_ARM_CPU);
-        object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]),
-                                  &error_abort);
-    }
-
     object_property_add_link(obj, "ddr-ram", TYPE_MEMORY_REGION,
                              (Object **)&s->ddr_ram,
                              qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
 
-    object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
+    object_initialize(&s->gic, sizeof(s->gic), gic_class_name());
     qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
 
     for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) {
@@ -196,11 +226,42 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
     qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
     qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
     qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS);
+
+    /* Realize APUs before realizing the GIC. KVM requires this.  */
+    for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
+        char *name;
+
+        object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC,
+                                "psci-conduit", &error_abort);
+
+        name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i]));
+        if (strcmp(name, boot_cpu)) {
+            /* Secondary CPUs start in PSCI powered-down state */
+            object_property_set_bool(OBJECT(&s->apu_cpu[i]), true,
+                                     "start-powered-off", &error_abort);
+        } else {
+            s->boot_cpu_ptr = &s->apu_cpu[i];
+        }
+        g_free(name);
+
+        object_property_set_bool(OBJECT(&s->apu_cpu[i]),
+                                 s->secure, "has_el3", NULL);
+        object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
+                                "reset-cbar", &error_abort);
+        object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
+                                 &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+
     object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
         return;
     }
+
     assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS);
     for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) {
         SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic);
@@ -223,29 +284,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
 
     for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
         qemu_irq irq;
-        char *name;
-
-        object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC,
-                                "psci-conduit", &error_abort);
-
-        name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i]));
-        if (strcmp(name, boot_cpu)) {
-            /* Secondary CPUs start in PSCI powered-down state */
-            object_property_set_bool(OBJECT(&s->apu_cpu[i]), true,
-                                     "start-powered-off", &error_abort);
-        } else {
-            s->boot_cpu_ptr = &s->apu_cpu[i];
-        }
-        g_free(name);
-
-        object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
-                                "reset-cbar", &error_abort);
-        object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
-                                 &err);
-        if (err) {
-            error_propagate(errp, err);
-            return;
-        }
 
         sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
                            qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]),
@@ -258,23 +296,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
         qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq);
     }
 
-    for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
-        char *name;
-
-        name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
-        if (strcmp(name, boot_cpu)) {
-            /* Secondary CPUs start in PSCI powered-down state */
-            object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true,
-                                     "start-powered-off", &error_abort);
-        } else {
-            s->boot_cpu_ptr = &s->rpu_cpu[i];
-        }
-        g_free(name);
-
-        object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
-                                 &error_abort);
-        object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
-                                 &err);
+    if (s->has_rpu) {
+        xlnx_zynqmp_create_rpu(s, boot_cpu, &err);
         if (err) {
             error_propagate(errp, err);
             return;
@@ -308,6 +331,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
     }
 
     for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
+        qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
         object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
         if (err) {
             error_propagate(errp, err);
@@ -370,6 +394,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
 
 static Property xlnx_zynqmp_props[] = {
     DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu),
+    DEFINE_PROP_BOOL("secure", XlnxZynqMPState, secure, false),
+    DEFINE_PROP_BOOL("has_rpu", XlnxZynqMPState, has_rpu, false),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 442dac52cf..c856fc30b2 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -468,9 +468,6 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                           fifo_trigger_update, s);
 
-    /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
-    s->chr = qemu_char_get_next_serial();
-
     if (s->chr) {
         qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
                               uart_event, s);
@@ -517,6 +514,11 @@ static const VMStateDescription vmstate_cadence_uart = {
     }
 };
 
+static Property cadence_uart_properties[] = {
+    DEFINE_PROP_CHR("chardev", CadenceUARTState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void cadence_uart_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -524,9 +526,8 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data)
     dc->realize = cadence_uart_realize;
     dc->vmsd = &vmstate_cadence_uart;
     dc->reset = cadence_uart_reset;
-    /* Reason: realize() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
-}
+    dc->props = cadence_uart_properties;
+  }
 
 static const TypeInfo cadence_uart_info = {
     .name          = TYPE_CADENCE_UART,
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
index 0e44878405..c7604e6766 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -145,8 +145,6 @@ static void digic_uart_realize(DeviceState *dev, Error **errp)
 {
     DigicUartState *s = DIGIC_UART(dev);
 
-    /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
-    s->chr = qemu_char_get_next_serial();
     if (s->chr) {
         qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
     }
@@ -172,6 +170,11 @@ static const VMStateDescription vmstate_digic_uart = {
     }
 };
 
+static Property digic_uart_properties[] = {
+    DEFINE_PROP_CHR("chardev", DigicUartState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void digic_uart_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -179,8 +182,7 @@ static void digic_uart_class_init(ObjectClass *klass, void *data)
     dc->realize = digic_uart_realize;
     dc->reset = digic_uart_reset;
     dc->vmsd = &vmstate_digic_uart;
-    /* Reason: realize() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
+    dc->props = digic_uart_properties;
 }
 
 static const TypeInfo digic_uart_info = {
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 6876ea6eef..c0fbf8a874 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -274,6 +274,11 @@ static const VMStateDescription vmstate_pl011 = {
     }
 };
 
+static Property pl011_properties[] = {
+    DEFINE_PROP_CHR("chardev", PL011State, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void pl011_init(Object *obj)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -295,9 +300,6 @@ static void pl011_realize(DeviceState *dev, Error **errp)
 {
     PL011State *s = PL011(dev);
 
-    /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
-    s->chr = qemu_char_get_next_serial();
-
     if (s->chr) {
         qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
                               pl011_event, s);
@@ -310,8 +312,7 @@ static void pl011_class_init(ObjectClass *oc, void *data)
 
     dc->realize = pl011_realize;
     dc->vmsd = &vmstate_pl011;
-    /* Reason: realize() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
+    dc->props = pl011_properties;
 }
 
 static const TypeInfo pl011_arm_info = {
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 72305ec5d4..15657abda9 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -190,6 +190,11 @@ static const MemoryRegionOps stm32f2xx_usart_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static Property stm32f2xx_usart_properties[] = {
+    DEFINE_PROP_CHR("chardev", STM32F2XXUsartState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void stm32f2xx_usart_init(Object *obj)
 {
     STM32F2XXUsartState *s = STM32F2XX_USART(obj);
@@ -199,9 +204,11 @@ static void stm32f2xx_usart_init(Object *obj)
     memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s,
                           TYPE_STM32F2XX_USART, 0x2000);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
 
-    /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
-    s->chr = qemu_char_get_next_serial();
+static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp)
+{
+    STM32F2XXUsartState *s = STM32F2XX_USART(dev);
 
     if (s->chr) {
         qemu_chr_add_handlers(s->chr, stm32f2xx_usart_can_receive,
@@ -214,8 +221,8 @@ static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->reset = stm32f2xx_usart_reset;
-    /* Reason: instance_init() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
+    dc->props = stm32f2xx_usart_properties;
+    dc->realize = stm32f2xx_usart_realize;
 }
 
 static const TypeInfo stm32f2xx_usart_info = {
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index 911af4a0d7..4847efb29f 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -172,6 +172,11 @@ static const MemoryRegionOps uart_ops = {
     }
 };
 
+static Property xilinx_uartlite_properties[] = {
+    DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void uart_rx(void *opaque, const uint8_t *buf, int size)
 {
     XilinxUARTLite *s = opaque;
@@ -206,8 +211,6 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp)
 {
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
-    /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
-    s->chr = qemu_char_get_next_serial();
     if (s->chr)
         qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
 }
@@ -229,8 +232,7 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data)
 
     dc->reset = xilinx_uartlite_reset;
     dc->realize = xilinx_uartlite_realize;
-    /* Reason: realize() method uses qemu_char_get_next_serial() */
-    dc->cannot_instantiate_with_device_add_yet = true;
+    dc->props = xilinx_uartlite_properties;
 }
 
 static const TypeInfo xilinx_uartlite_info = {
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 153c835136..05b0c276eb 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -35,6 +35,9 @@ static void ptimer_trigger(ptimer_state *s)
 
 static void ptimer_reload(ptimer_state *s)
 {
+    uint32_t period_frac = s->period_frac;
+    uint64_t period = s->period;
+
     if (s->delta == 0) {
         ptimer_trigger(s);
         s->delta = s->limit;
@@ -45,10 +48,24 @@ static void ptimer_reload(ptimer_state *s)
         return;
     }
 
+    /*
+     * Artificially limit timeout rate to something
+     * achievable under QEMU.  Otherwise, QEMU spends all
+     * its time generating timer interrupts, and there
+     * is no forward progress.
+     * About ten microseconds is the fastest that really works
+     * on the current generation of host machines.
+     */
+
+    if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) {
+        period = 10000 / s->delta;
+        period_frac = 0;
+    }
+
     s->last_event = s->next_event;
-    s->next_event = s->last_event + s->delta * s->period;
-    if (s->period_frac) {
-        s->next_event += ((int64_t)s->period_frac * s->delta) >> 32;
+    s->next_event = s->last_event + s->delta * period;
+    if (period_frac) {
+        s->next_event += ((int64_t)period_frac * s->delta) >> 32;
     }
     timer_mod(s->timer, s->next_event);
 }
@@ -67,14 +84,16 @@ static void ptimer_tick(void *opaque)
 
 uint64_t ptimer_get_count(ptimer_state *s)
 {
-    int64_t now;
     uint64_t counter;
 
     if (s->enabled) {
-        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        int64_t next = s->next_event;
+        bool expired = (now - next >= 0);
+        bool oneshot = (s->enabled == 2);
+
         /* Figure out the current counter value.  */
-        if (now - s->next_event > 0
-            || s->period == 0) {
+        if (s->period == 0 || (expired && (oneshot || use_icount))) {
             /* Prevent timer underflowing if it should already have
                triggered.  */
             counter = 0;
@@ -83,6 +102,13 @@ uint64_t ptimer_get_count(ptimer_state *s)
             uint64_t div;
             int clz1, clz2;
             int shift;
+            uint32_t period_frac = s->period_frac;
+            uint64_t period = s->period;
+
+            if (!oneshot && (s->delta * period < 10000) && !use_icount) {
+                period = 10000 / s->delta;
+                period_frac = 0;
+            }
 
             /* We need to divide time by period, where time is stored in
                rem (64-bit integer) and period is stored in period/period_frac
@@ -94,8 +120,8 @@ uint64_t ptimer_get_count(ptimer_state *s)
                backwards.
             */
 
-            rem = s->next_event - now;
-            div = s->period;
+            rem = expired ? now - next : next - now;
+            div = period;
 
             clz1 = clz64(rem);
             clz2 = clz64(div);
@@ -104,16 +130,21 @@ uint64_t ptimer_get_count(ptimer_state *s)
             rem <<= shift;
             div <<= shift;
             if (shift >= 32) {
-                div |= ((uint64_t)s->period_frac << (shift - 32));
+                div |= ((uint64_t)period_frac << (shift - 32));
             } else {
                 if (shift != 0)
-                    div |= (s->period_frac >> (32 - shift));
+                    div |= (period_frac >> (32 - shift));
                 /* Look at remaining bits of period_frac and round div up if 
                    necessary.  */
-                if ((uint32_t)(s->period_frac << shift))
+                if ((uint32_t)(period_frac << shift))
                     div += 1;
             }
             counter = rem / div;
+
+            if (expired && counter != 0) {
+                /* Wrap around periodic counter.  */
+                counter = s->limit - (counter - 1) % s->limit;
+            }
         }
     } else {
         counter = s->delta;
@@ -132,16 +163,17 @@ void ptimer_set_count(ptimer_state *s, uint64_t count)
 
 void ptimer_run(ptimer_state *s, int oneshot)
 {
-    if (s->enabled) {
-        return;
-    }
-    if (s->period == 0) {
+    bool was_disabled = !s->enabled;
+
+    if (was_disabled && s->period == 0) {
         fprintf(stderr, "Timer with period zero, disabling\n");
         return;
     }
     s->enabled = oneshot ? 2 : 1;
-    s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    ptimer_reload(s);
+    if (was_disabled) {
+        s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        ptimer_reload(s);
+    }
 }
 
 /* Pause a timer.  Note that this may cause it to "lose" time, even if it
@@ -159,6 +191,7 @@ void ptimer_stop(ptimer_state *s)
 /* Set counter increment interval in nanoseconds.  */
 void ptimer_set_period(ptimer_state *s, int64_t period)
 {
+    s->delta = ptimer_get_count(s);
     s->period = period;
     s->period_frac = 0;
     if (s->enabled) {
@@ -170,6 +203,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
 /* Set counter frequency in Hz.  */
 void ptimer_set_freq(ptimer_state *s, uint32_t freq)
 {
+    s->delta = ptimer_get_count(s);
     s->period = 1000000000ll / freq;
     s->period_frac = (1000000000ll << 32) / freq;
     if (s->enabled) {
@@ -182,19 +216,6 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq)
    count = limit.  */
 void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
 {
-    /*
-     * Artificially limit timeout rate to something
-     * achievable under QEMU.  Otherwise, QEMU spends all
-     * its time generating timer interrupts, and there
-     * is no forward progress.
-     * About ten microseconds is the fastest that really works
-     * on the current generation of host machines.
-     */
-
-    if (!use_icount && limit * s->period < 10000 && s->period) {
-        limit = 10000 / s->period;
-    }
-
     s->limit = limit;
     if (reload)
         s->delta = limit;
@@ -204,6 +225,11 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
     }
 }
 
+uint64_t ptimer_get_limit(ptimer_state *s)
+{
+    return s->limit;
+}
+
 const VMStateDescription vmstate_ptimer = {
     .name = "ptimer",
     .version_id = 1,
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index aeb8f38d70..1fd54edf4c 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -5,4 +5,5 @@ common-obj-$(CONFIG_APM) += pm_smbus.o
 common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
 common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
 common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
 obj-$(CONFIG_OMAP) += omap_i2c.o
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
new file mode 100644
index 0000000000..ce5b1f0fa4
--- /dev/null
+++ b/hw/i2c/aspeed_i2c.c
@@ -0,0 +1,440 @@
+/*
+ * ARM Aspeed I2C controller
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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 "hw/sysbus.h"
+#include "qemu/log.h"
+#include "hw/i2c/aspeed_i2c.h"
+
+/* I2C Global Register */
+
+#define I2C_CTRL_STATUS         0x00        /* Device Interrupt Status */
+#define I2C_CTRL_ASSIGN         0x08        /* Device Interrupt Target
+                                               Assignment */
+
+/* I2C Device (Bus) Register */
+
+#define I2CD_FUN_CTRL_REG       0x00       /* I2CD Function Control  */
+#define   I2CD_BUFF_SEL_MASK               (0x7 << 20)
+#define   I2CD_BUFF_SEL(x)                 (x << 20)
+#define   I2CD_M_SDA_LOCK_EN               (0x1 << 16)
+#define   I2CD_MULTI_MASTER_DIS            (0x1 << 15)
+#define   I2CD_M_SCL_DRIVE_EN              (0x1 << 14)
+#define   I2CD_MSB_STS                     (0x1 << 9)
+#define   I2CD_SDA_DRIVE_1T_EN             (0x1 << 8)
+#define   I2CD_M_SDA_DRIVE_1T_EN           (0x1 << 7)
+#define   I2CD_M_HIGH_SPEED_EN             (0x1 << 6)
+#define   I2CD_DEF_ADDR_EN                 (0x1 << 5)
+#define   I2CD_DEF_ALERT_EN                (0x1 << 4)
+#define   I2CD_DEF_ARP_EN                  (0x1 << 3)
+#define   I2CD_DEF_GCALL_EN                (0x1 << 2)
+#define   I2CD_SLAVE_EN                    (0x1 << 1)
+#define   I2CD_MASTER_EN                   (0x1)
+
+#define I2CD_AC_TIMING_REG1     0x04       /* Clock and AC Timing Control #1 */
+#define I2CD_AC_TIMING_REG2     0x08       /* Clock and AC Timing Control #1 */
+#define I2CD_INTR_CTRL_REG      0x0c       /* I2CD Interrupt Control */
+#define I2CD_INTR_STS_REG       0x10       /* I2CD Interrupt Status */
+#define   I2CD_INTR_SDA_DL_TIMEOUT         (0x1 << 14)
+#define   I2CD_INTR_BUS_RECOVER_DONE       (0x1 << 13)
+#define   I2CD_INTR_SMBUS_ALERT            (0x1 << 12) /* Bus [0-3] only */
+#define   I2CD_INTR_SMBUS_ARP_ADDR         (0x1 << 11) /* Removed */
+#define   I2CD_INTR_SMBUS_DEV_ALERT_ADDR   (0x1 << 10) /* Removed */
+#define   I2CD_INTR_SMBUS_DEF_ADDR         (0x1 << 9)  /* Removed */
+#define   I2CD_INTR_GCALL_ADDR             (0x1 << 8)  /* Removed */
+#define   I2CD_INTR_SLAVE_MATCH            (0x1 << 7)  /* use RX_DONE */
+#define   I2CD_INTR_SCL_TIMEOUT            (0x1 << 6)
+#define   I2CD_INTR_ABNORMAL               (0x1 << 5)
+#define   I2CD_INTR_NORMAL_STOP            (0x1 << 4)
+#define   I2CD_INTR_ARBIT_LOSS             (0x1 << 3)
+#define   I2CD_INTR_RX_DONE                (0x1 << 2)
+#define   I2CD_INTR_TX_NAK                 (0x1 << 1)
+#define   I2CD_INTR_TX_ACK                 (0x1 << 0)
+
+#define I2CD_CMD_REG            0x14       /* I2CD Command/Status */
+#define   I2CD_SDA_OE                      (0x1 << 28)
+#define   I2CD_SDA_O                       (0x1 << 27)
+#define   I2CD_SCL_OE                      (0x1 << 26)
+#define   I2CD_SCL_O                       (0x1 << 25)
+#define   I2CD_TX_TIMING                   (0x1 << 24)
+#define   I2CD_TX_STATUS                   (0x1 << 23)
+
+#define   I2CD_TX_STATE_SHIFT              19 /* Tx State Machine */
+#define   I2CD_TX_STATE_MASK                  0xf
+#define     I2CD_IDLE                         0x0
+#define     I2CD_MACTIVE                      0x8
+#define     I2CD_MSTART                       0x9
+#define     I2CD_MSTARTR                      0xa
+#define     I2CD_MSTOP                        0xb
+#define     I2CD_MTXD                         0xc
+#define     I2CD_MRXACK                       0xd
+#define     I2CD_MRXD                         0xe
+#define     I2CD_MTXACK                       0xf
+#define     I2CD_SWAIT                        0x1
+#define     I2CD_SRXD                         0x4
+#define     I2CD_STXACK                       0x5
+#define     I2CD_STXD                         0x6
+#define     I2CD_SRXACK                       0x7
+#define     I2CD_RECOVER                      0x3
+
+#define   I2CD_SCL_LINE_STS                (0x1 << 18)
+#define   I2CD_SDA_LINE_STS                (0x1 << 17)
+#define   I2CD_BUS_BUSY_STS                (0x1 << 16)
+#define   I2CD_SDA_OE_OUT_DIR              (0x1 << 15)
+#define   I2CD_SDA_O_OUT_DIR               (0x1 << 14)
+#define   I2CD_SCL_OE_OUT_DIR              (0x1 << 13)
+#define   I2CD_SCL_O_OUT_DIR               (0x1 << 12)
+#define   I2CD_BUS_RECOVER_CMD_EN          (0x1 << 11)
+#define   I2CD_S_ALT_EN                    (0x1 << 10)
+#define   I2CD_RX_DMA_ENABLE               (0x1 << 9)
+#define   I2CD_TX_DMA_ENABLE               (0x1 << 8)
+
+/* Command Bit */
+#define   I2CD_M_STOP_CMD                  (0x1 << 5)
+#define   I2CD_M_S_RX_CMD_LAST             (0x1 << 4)
+#define   I2CD_M_RX_CMD                    (0x1 << 3)
+#define   I2CD_S_TX_CMD                    (0x1 << 2)
+#define   I2CD_M_TX_CMD                    (0x1 << 1)
+#define   I2CD_M_START_CMD                 (0x1)
+
+#define I2CD_DEV_ADDR_REG       0x18       /* Slave Device Address */
+#define I2CD_BUF_CTRL_REG       0x1c       /* Pool Buffer Control */
+#define I2CD_BYTE_BUF_REG       0x20       /* Transmit/Receive Byte Buffer */
+#define   I2CD_BYTE_BUF_TX_SHIFT           0
+#define   I2CD_BYTE_BUF_TX_MASK            0xff
+#define   I2CD_BYTE_BUF_RX_SHIFT           8
+#define   I2CD_BYTE_BUF_RX_MASK            0xff
+
+
+static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus)
+{
+    return bus->ctrl & I2CD_MASTER_EN;
+}
+
+static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus)
+{
+    return bus->ctrl & (I2CD_MASTER_EN | I2CD_SLAVE_EN);
+}
+
+static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus)
+{
+    bus->intr_status &= bus->intr_ctrl;
+    if (bus->intr_status) {
+        bus->controller->intr_status |= 1 << bus->id;
+        qemu_irq_raise(bus->controller->irq);
+    }
+}
+
+static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset,
+                                    unsigned size)
+{
+    AspeedI2CBus *bus = opaque;
+
+    switch (offset) {
+    case I2CD_FUN_CTRL_REG:
+        return bus->ctrl;
+    case I2CD_AC_TIMING_REG1:
+        return bus->timing[0];
+    case I2CD_AC_TIMING_REG2:
+        return bus->timing[1];
+    case I2CD_INTR_CTRL_REG:
+        return bus->intr_ctrl;
+    case I2CD_INTR_STS_REG:
+        return bus->intr_status;
+    case I2CD_BYTE_BUF_REG:
+        return bus->buf;
+    case I2CD_CMD_REG:
+        return bus->cmd | (i2c_bus_busy(bus->bus) << 16);
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
+        return -1;
+    }
+}
+
+static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)
+{
+    bus->cmd |= value & 0xFFFF;
+    bus->intr_status = 0;
+
+    if (bus->cmd & I2CD_M_START_CMD) {
+        if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7),
+                               extract32(bus->buf, 0, 1))) {
+            bus->intr_status |= I2CD_INTR_TX_NAK;
+        } else {
+            bus->intr_status |= I2CD_INTR_TX_ACK;
+        }
+
+    } else if (bus->cmd & I2CD_M_TX_CMD) {
+        if (i2c_send(bus->bus, bus->buf)) {
+            bus->intr_status |= (I2CD_INTR_TX_NAK | I2CD_INTR_ABNORMAL);
+            i2c_end_transfer(bus->bus);
+        } else {
+            bus->intr_status |= I2CD_INTR_TX_ACK;
+        }
+
+    } else if (bus->cmd & I2CD_M_RX_CMD) {
+        int ret = i2c_recv(bus->bus);
+        if (ret < 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__);
+            ret = 0xff;
+        } else {
+            bus->intr_status |= I2CD_INTR_RX_DONE;
+        }
+        bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT;
+    }
+
+    if (bus->cmd & (I2CD_M_STOP_CMD | I2CD_M_S_RX_CMD_LAST)) {
+        if (!i2c_bus_busy(bus->bus)) {
+            bus->intr_status |= I2CD_INTR_ABNORMAL;
+        } else {
+            i2c_end_transfer(bus->bus);
+            bus->intr_status |= I2CD_INTR_NORMAL_STOP;
+        }
+    }
+
+    /* command is handled, reset it and check for interrupts  */
+    bus->cmd &= ~0xFFFF;
+    aspeed_i2c_bus_raise_interrupt(bus);
+}
+
+static void aspeed_i2c_bus_write(void *opaque, hwaddr offset,
+                                 uint64_t value, unsigned size)
+{
+    AspeedI2CBus *bus = opaque;
+
+    switch (offset) {
+    case I2CD_FUN_CTRL_REG:
+        if (value & I2CD_SLAVE_EN) {
+            qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+                          __func__);
+            break;
+        }
+        bus->ctrl = value & 0x0071C3FF;
+        break;
+    case I2CD_AC_TIMING_REG1:
+        bus->timing[0] = value & 0xFFFFF0F;
+        break;
+    case I2CD_AC_TIMING_REG2:
+        bus->timing[1] = value & 0x7;
+        break;
+    case I2CD_INTR_CTRL_REG:
+        bus->intr_ctrl = value & 0x7FFF;
+        break;
+    case I2CD_INTR_STS_REG:
+        bus->intr_status &= ~(value & 0x7FFF);
+        bus->controller->intr_status &= ~(1 << bus->id);
+        qemu_irq_lower(bus->controller->irq);
+        break;
+    case I2CD_DEV_ADDR_REG:
+        qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+                      __func__);
+        break;
+    case I2CD_BYTE_BUF_REG:
+        bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT;
+        break;
+    case I2CD_CMD_REG:
+        if (!aspeed_i2c_bus_is_enabled(bus)) {
+            break;
+        }
+
+        if (!aspeed_i2c_bus_is_master(bus)) {
+            qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+                          __func__);
+            break;
+        }
+
+        aspeed_i2c_bus_handle_cmd(bus, value);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+    }
+}
+
+static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset,
+                                   unsigned size)
+{
+    AspeedI2CState *s = opaque;
+
+    switch (offset) {
+    case I2C_CTRL_STATUS:
+        return s->intr_status;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        break;
+    }
+
+    return -1;
+}
+
+static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset,
+                                  uint64_t value, unsigned size)
+{
+    switch (offset) {
+    case I2C_CTRL_STATUS:
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps aspeed_i2c_bus_ops = {
+    .read = aspeed_i2c_bus_read,
+    .write = aspeed_i2c_bus_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps aspeed_i2c_ctrl_ops = {
+    .read = aspeed_i2c_ctrl_read,
+    .write = aspeed_i2c_ctrl_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const VMStateDescription aspeed_i2c_bus_vmstate = {
+    .name = TYPE_ASPEED_I2C,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(id, AspeedI2CBus),
+        VMSTATE_UINT32(ctrl, AspeedI2CBus),
+        VMSTATE_UINT32_ARRAY(timing, AspeedI2CBus, 2),
+        VMSTATE_UINT32(intr_ctrl, AspeedI2CBus),
+        VMSTATE_UINT32(intr_status, AspeedI2CBus),
+        VMSTATE_UINT32(cmd, AspeedI2CBus),
+        VMSTATE_UINT32(buf, AspeedI2CBus),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription aspeed_i2c_vmstate = {
+    .name = TYPE_ASPEED_I2C,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(intr_status, AspeedI2CState),
+        VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState,
+                             ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate,
+                             AspeedI2CBus),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void aspeed_i2c_reset(DeviceState *dev)
+{
+    int i;
+    AspeedI2CState *s = ASPEED_I2C(dev);
+
+    s->intr_status = 0;
+
+    for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) {
+        s->busses[i].intr_ctrl = 0;
+        s->busses[i].intr_status = 0;
+        s->busses[i].cmd = 0;
+        s->busses[i].buf = 0;
+        i2c_end_transfer(s->busses[i].bus);
+    }
+}
+
+/*
+ * Address Definitions
+ *
+ *   0x000 ... 0x03F: Global Register
+ *   0x040 ... 0x07F: Device 1
+ *   0x080 ... 0x0BF: Device 2
+ *   0x0C0 ... 0x0FF: Device 3
+ *   0x100 ... 0x13F: Device 4
+ *   0x140 ... 0x17F: Device 5
+ *   0x180 ... 0x1BF: Device 6
+ *   0x1C0 ... 0x1FF: Device 7
+ *   0x200 ... 0x2FF: Buffer Pool  (unused in linux driver)
+ *   0x300 ... 0x33F: Device 8
+ *   0x340 ... 0x37F: Device 9
+ *   0x380 ... 0x3BF: Device 10
+ *   0x3C0 ... 0x3FF: Device 11
+ *   0x400 ... 0x43F: Device 12
+ *   0x440 ... 0x47F: Device 13
+ *   0x480 ... 0x4BF: Device 14
+ *   0x800 ... 0xFFF: Buffer Pool  (unused in linux driver)
+ */
+static void aspeed_i2c_realize(DeviceState *dev, Error **errp)
+{
+    int i;
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    AspeedI2CState *s = ASPEED_I2C(dev);
+
+    sysbus_init_irq(sbd, &s->irq);
+    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s,
+                          "aspeed.i2c", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) {
+        char name[16];
+        int offset = i < 7 ? 1 : 5;
+        snprintf(name, sizeof(name), "aspeed.i2c.%d", i);
+        s->busses[i].controller = s;
+        s->busses[i].id = i;
+        s->busses[i].bus = i2c_init_bus(dev, name);
+        memory_region_init_io(&s->busses[i].mr, OBJECT(dev),
+                              &aspeed_i2c_bus_ops, &s->busses[i], name, 0x40);
+        memory_region_add_subregion(&s->iomem, 0x40 * (i + offset),
+                                    &s->busses[i].mr);
+    }
+}
+
+static void aspeed_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &aspeed_i2c_vmstate;
+    dc->reset = aspeed_i2c_reset;
+    dc->realize = aspeed_i2c_realize;
+    dc->desc = "Aspeed I2C Controller";
+}
+
+static const TypeInfo aspeed_i2c_info = {
+    .name          = TYPE_ASPEED_I2C,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(AspeedI2CState),
+    .class_init    = aspeed_i2c_class_init,
+};
+
+static void aspeed_i2c_register_types(void)
+{
+    type_register_static(&aspeed_i2c_info);
+}
+
+type_init(aspeed_i2c_register_types)
+
+
+I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr)
+{
+    AspeedI2CState *s = ASPEED_I2C(dev);
+    I2CBus *bus = NULL;
+
+    if (busnr >= 0 && busnr < ASPEED_I2C_NR_BUSSES) {
+        bus = s->busses[busnr].bus;
+    }
+
+    return bus;
+}
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 06a22e0aaf..b30cc91745 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -661,6 +661,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
             goto bad_reg;
         res = 0;
         for (i = 0; i < 8; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             if (GIC_TEST_ENABLED(irq + i, cm)) {
                 res |= (1 << i);
             }
@@ -677,6 +682,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
         res = 0;
         mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             if (gic_test_pending(s, irq + i, mask)) {
                 res |= (1 << i);
             }
@@ -689,6 +699,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
         res = 0;
         mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
             }
@@ -722,6 +737,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
             goto bad_reg;
         res = 0;
         for (i = 0; i < 4; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             if (GIC_TEST_MODEL(irq + i))
                 res |= (1 << (i * 2));
             if (GIC_TEST_EDGE_TRIGGER(irq + i))
@@ -742,7 +762,12 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
             /* GICD_SPENDSGIRn */
         }
 
-        res = s->sgi_pending[irq][cpu];
+        if (s->security_extn && !attrs.secure &&
+            !GIC_TEST_GROUP(irq, 1 << cpu)) {
+            res = 0; /* Ignore Non-secure access of Group0 IRQ */
+        } else {
+            res = s->sgi_pending[irq][cpu];
+        }
     } else if (offset < 0xfd0) {
         goto bad_reg;
     } else if (offset < 0x1000) {
@@ -862,6 +887,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
                     (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq + i);
                 int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
+                if (s->security_extn && !attrs.secure &&
+                    !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                    continue; /* Ignore Non-secure access of Group0 IRQ */
+                }
+
                 if (!GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Enabled IRQ %d\n", irq + i);
                     trace_gic_enable_irq(irq + i);
@@ -889,6 +919,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
             if (value & (1 << i)) {
                 int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
+                if (s->security_extn && !attrs.secure &&
+                    !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                    continue; /* Ignore Non-secure access of Group0 IRQ */
+                }
+
                 if (GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Disabled IRQ %d\n", irq + i);
                     trace_gic_disable_irq(irq + i);
@@ -907,6 +942,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
 
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
+                if (s->security_extn && !attrs.secure &&
+                    !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                    continue; /* Ignore Non-secure access of Group0 IRQ */
+                }
+
                 GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i));
             }
         }
@@ -920,6 +960,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
         }
 
         for (i = 0; i < 8; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             /* ??? This currently clears the pending bit for all CPUs, even
                for per-CPU interrupts.  It's unclear whether this is the
                corect behavior.  */
@@ -960,6 +1005,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
         if (irq < GIC_NR_SGIS)
             value |= 0xaa;
         for (i = 0; i < 4; i++) {
+            if (s->security_extn && !attrs.secure &&
+                !GIC_TEST_GROUP(irq + i, 1 << cpu)) {
+                continue; /* Ignore Non-secure access of Group0 IRQ */
+            }
+
             if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
                 if (value & (1 << (i * 2))) {
                     GIC_SET_MODEL(irq + i);
@@ -983,9 +1033,12 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
         }
         irq = (offset - 0xf10);
 
-        s->sgi_pending[irq][cpu] &= ~value;
-        if (s->sgi_pending[irq][cpu] == 0) {
-            GIC_CLEAR_PENDING(irq, 1 << cpu);
+        if (!s->security_extn || attrs.secure ||
+            GIC_TEST_GROUP(irq, 1 << cpu)) {
+            s->sgi_pending[irq][cpu] &= ~value;
+            if (s->sgi_pending[irq][cpu] == 0) {
+                GIC_CLEAR_PENDING(irq, 1 << cpu);
+            }
         }
     } else if (offset < 0xf30) {
         /* GICD_SPENDSGIRn */
@@ -994,8 +1047,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
         }
         irq = (offset - 0xf20);
 
-        GIC_SET_PENDING(irq, 1 << cpu);
-        s->sgi_pending[irq][cpu] |= value;
+        if (!s->security_extn || attrs.secure ||
+            GIC_TEST_GROUP(irq, 1 << cpu)) {
+            GIC_SET_PENDING(irq, 1 << cpu);
+            s->sgi_pending[irq][cpu] |= value;
+        }
     } else {
         goto bad_reg;
     }
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index f821e1cfef..423bcd7f6c 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -36,6 +36,7 @@
 #include "hw/boards.h"
 #include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
+#include "hw/char/xilinx_uartlite.h"
 
 #include "boot.h"
 
@@ -103,8 +104,8 @@ petalogix_s3adsp1800_init(MachineState *machine)
         irq[i] = qdev_get_gpio_in(dev, i);
     }
 
-    sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR,
-                         irq[UARTLITE_IRQ]);
+    xilinx_uartlite_create(UARTLITE_BASEADDR, irq[UARTLITE_IRQ],
+                           serial_hds[0]);
 
     /* 2 timers at irq 2 @ 62 Mhz.  */
     dev = qdev_create(NULL, "xlnx.xps-timer");
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
index f16a1ed25c..c05ed53767 100644
--- a/include/hw/arm/ast2400.h
+++ b/include/hw/arm/ast2400.h
@@ -15,6 +15,7 @@
 #include "hw/arm/arm.h"
 #include "hw/intc/aspeed_vic.h"
 #include "hw/timer/aspeed_timer.h"
+#include "hw/i2c/aspeed_i2c.h"
 
 typedef struct AST2400State {
     /*< private >*/
@@ -25,6 +26,7 @@ typedef struct AST2400State {
     MemoryRegion iomem;
     AspeedVICState vic;
     AspeedTimerCtrlState timerctrl;
+    AspeedI2CState i2c;
 } AST2400State;
 
 #define TYPE_AST2400 "ast2400"
diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
index 2332596b40..68f6eb0c4d 100644
--- a/include/hw/arm/xlnx-zynqmp.h
+++ b/include/hw/arm/xlnx-zynqmp.h
@@ -84,6 +84,11 @@ typedef struct XlnxZynqMPState {
 
     char *boot_cpu;
     ARMCPU *boot_cpu_ptr;
+
+    /* Has the ARM Security extensions?  */
+    bool secure;
+    /* Has the RPU subsystem?  */
+    bool has_rpu;
 }  XlnxZynqMPState;
 
 #define XLNX_ZYNQMP_H
diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h
index 6310f52512..a12773c076 100644
--- a/include/hw/char/cadence_uart.h
+++ b/include/hw/char/cadence_uart.h
@@ -49,5 +49,22 @@ typedef struct {
     QEMUTimer *fifo_trigger_handle;
 } CadenceUARTState;
 
+static inline DeviceState *cadence_uart_create(hwaddr addr,
+                                        qemu_irq irq,
+                                        CharDriverState *chr)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, TYPE_CADENCE_UART);
+    s = SYS_BUS_DEVICE(dev);
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(s, 0, addr);
+    sysbus_connect_irq(s, 0, irq);
+
+    return dev;
+}
+
 #define CADENCE_UART_H
 #endif
diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
new file mode 100644
index 0000000000..93bd7ee83e
--- /dev/null
+++ b/include/hw/char/pl011.h
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef PL011_UART_H
+#define PL011_UART_H
+
+static inline DeviceState *pl011_create(hwaddr addr,
+                                        qemu_irq irq,
+                                        CharDriverState *chr)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, "pl011");
+    s = SYS_BUS_DEVICE(dev);
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(s, 0, addr);
+    sysbus_connect_irq(s, 0, irq);
+
+    return dev;
+}
+
+static inline DeviceState *pl011_luminary_create(hwaddr addr,
+                                                 qemu_irq irq,
+                                                 CharDriverState *chr)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, "pl011_luminary");
+    s = SYS_BUS_DEVICE(dev);
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(s, 0, addr);
+    sysbus_connect_irq(s, 0, irq);
+
+    return dev;
+}
+
+#endif
diff --git a/include/hw/char/xilinx_uartlite.h b/include/hw/char/xilinx_uartlite.h
new file mode 100644
index 0000000000..8b4fc54953
--- /dev/null
+++ b/include/hw/char/xilinx_uartlite.h
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef XILINX_UARTLITE_H
+#define XILINX_UARTLITE_H
+
+static inline DeviceState *xilinx_uartlite_create(hwaddr addr,
+                                        qemu_irq irq,
+                                        CharDriverState *chr)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, "xlnx.xps-uartlite");
+    s = SYS_BUS_DEVICE(dev);
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(s, 0, addr);
+    sysbus_connect_irq(s, 0, irq);
+
+    return dev;
+}
+
+#endif
diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h
new file mode 100644
index 0000000000..f9020acdef
--- /dev/null
+++ b/include/hw/i2c/aspeed_i2c.h
@@ -0,0 +1,62 @@
+/*
+ *  ASPEED AST2400 I2C Controller
+ *
+ *  Copyright (C) 2016 IBM Corp.
+ *
+ *  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, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef ASPEED_I2C_H
+#define ASPEED_I2C_H
+
+#include "hw/i2c/i2c.h"
+
+#define TYPE_ASPEED_I2C "aspeed.i2c"
+#define ASPEED_I2C(obj) \
+    OBJECT_CHECK(AspeedI2CState, (obj), TYPE_ASPEED_I2C)
+
+#define ASPEED_I2C_NR_BUSSES 14
+
+struct AspeedI2CState;
+
+typedef struct AspeedI2CBus {
+    struct AspeedI2CState *controller;
+
+    MemoryRegion mr;
+
+    I2CBus *bus;
+    uint8_t id;
+
+    uint32_t ctrl;
+    uint32_t timing[2];
+    uint32_t intr_ctrl;
+    uint32_t intr_status;
+    uint32_t cmd;
+    uint32_t buf;
+} AspeedI2CBus;
+
+typedef struct AspeedI2CState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    uint32_t intr_status;
+
+    AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES];
+} AspeedI2CState;
+
+I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr);
+
+#endif /* ASPEED_I2C_H */
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 8ebacbbda0..e397db5bdb 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -19,6 +19,7 @@ typedef void (*ptimer_cb)(void *opaque);
 ptimer_state *ptimer_init(QEMUBH *bh);
 void ptimer_set_period(ptimer_state *s, int64_t period);
 void ptimer_set_freq(ptimer_state *s, uint32_t freq);
+uint64_t ptimer_get_limit(ptimer_state *s);
 void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload);
 uint64_t ptimer_get_count(ptimer_state *s);
 void ptimer_set_count(ptimer_state *s, uint64_t count);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 307fd8fde4..372a6fd40b 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -403,7 +403,6 @@ void register_char_driver(const char *name, ChardevBackendKind kind,
 
 extern int term_escape_char;
 
-CharDriverState *qemu_char_get_next_serial(void);
 
 /* console.c */
 typedef CharDriverState *(VcHandler)(ChardevVC *vc, Error **errp);
diff --git a/qemu-char.c b/qemu-char.c
index b597ee19ca..b13ecbb025 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4088,22 +4088,6 @@ CharDriverState *qemu_chr_find(const char *name)
     return NULL;
 }
 
-/* Get a character (serial) device interface.  */
-CharDriverState *qemu_char_get_next_serial(void)
-{
-    static int next_serial;
-    CharDriverState *chr;
-
-    /* FIXME: This function needs to go away: use chardev properties!  */
-
-    while (next_serial < MAX_SERIAL_PORTS && serial_hds[next_serial]) {
-        chr = serial_hds[next_serial++];
-        qemu_chr_fe_claim_no_fail(chr);
-        return chr;
-    }
-    return NULL;
-}
-
 QemuOptsList qemu_chardev_opts = {
     .name = "chardev",
     .implied_opt_name = "backend",
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index c741b53ad4..401955f825 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -93,7 +93,19 @@
 #define ARM_CPU_VFIQ 3
 
 #define NB_MMU_MODES 7
-#define TARGET_INSN_START_EXTRA_WORDS 1
+/* ARM-specific extra insn start words:
+ * 1: Conditional execution bits
+ * 2: Partial exception syndrome for data aborts
+ */
+#define TARGET_INSN_START_EXTRA_WORDS 2
+
+/* The 2nd extra word holding syndrome info for data aborts does not use
+ * the upper 6 bits nor the lower 14 bits. We mask and shift it down to
+ * help the sleb128 encoder do a better job.
+ * When restoring the CPU state, we shift it back up.
+ */
+#define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1)
+#define ARM_INSN_START_WORD2_SHIFT 14
 
 /* We currently assume float and double are IEEE single and double
    precision respectively.
@@ -278,6 +290,7 @@ typedef struct CPUARMState {
             uint64_t far_el[4];
         };
         uint64_t hpfar_el2;
+        uint64_t hstr_el2;
         union { /* Translation result. */
             struct {
                 uint64_t _unused_par_0;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index e3ea26f8c8..862e7808a2 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3471,6 +3471,9 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
       .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4,
       .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any,
       .type = ARM_CP_CONST, .resetvalue = 0 },
+    { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH,
+      .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3,
+      .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
     REGINFO_SENTINEL
 };
 
@@ -3706,6 +3709,10 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
       .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4,
       .access = PL2_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.hpfar_el2) },
+    { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH,
+      .cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3,
+      .access = PL2_RW,
+      .fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) },
     REGINFO_SENTINEL
 };
 
@@ -6358,9 +6365,6 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
         env->elr_el[new_el] = env->pc;
     } else {
         env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env);
-        if (!env->thumb) {
-            env->cp15.esr_el[new_el] |= 1 << 25;
-        }
         env->elr_el[new_el] = env->regs[15];
 
         aarch64_sync_32_to_64(env);
@@ -7275,7 +7279,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     target_ulong page_size;
     uint32_t attrs;
     int32_t stride = 9;
-    int32_t va_size;
+    int32_t addrsize;
     int inputsize;
     int32_t tbi = 0;
     TCR *tcr = regime_tcr(env, mmu_idx);
@@ -7283,6 +7287,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     uint32_t el = regime_el(env, mmu_idx);
     bool ttbr1_valid = true;
     uint64_t descaddrmask;
+    bool aarch64 = arm_el_is_aa64(env, el);
 
     /* TODO:
      * This code does not handle the different format TCR for VTCR_EL2.
@@ -7290,9 +7295,9 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
      * Attribute and permission bit handling should also be checked when adding
      * support for those page table walks.
      */
-    if (arm_el_is_aa64(env, el)) {
+    if (aarch64) {
         level = 0;
-        va_size = 64;
+        addrsize = 64;
         if (el > 1) {
             if (mmu_idx != ARMMMUIdx_S2NS) {
                 tbi = extract64(tcr->raw_tcr, 20, 1);
@@ -7314,7 +7319,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         }
     } else {
         level = 1;
-        va_size = 32;
+        addrsize = 32;
         /* There is no TTBR1 for EL2 */
         if (el == 2) {
             ttbr1_valid = false;
@@ -7326,7 +7331,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
      * This is a Non-secure PL0/1 stage 1 translation, so controlled by
      * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32:
      */
-    if (va_size == 64) {
+    if (aarch64) {
         /* AArch64 translation.  */
         t0sz = extract32(tcr->raw_tcr, 0, 6);
         t0sz = MIN(t0sz, 39);
@@ -7338,7 +7343,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         /* AArch32 stage 2 translation.  */
         bool sext = extract32(tcr->raw_tcr, 4, 1);
         bool sign = extract32(tcr->raw_tcr, 3, 1);
-        t0sz = sextract32(tcr->raw_tcr, 0, 4);
+        /* Address size is 40-bit for a stage 2 translation,
+         * and t0sz can be negative (from -8 to 7),
+         * so we need to adjust it to use the TTBR selecting logic below.
+         */
+        addrsize = 40;
+        t0sz = sextract32(tcr->raw_tcr, 0, 4) + 8;
 
         /* If the sign-extend bit is not the same as t0sz[3], the result
          * is unpredictable. Flag this as a guest error.  */
@@ -7348,15 +7358,15 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         }
     }
     t1sz = extract32(tcr->raw_tcr, 16, 6);
-    if (va_size == 64) {
+    if (aarch64) {
         t1sz = MIN(t1sz, 39);
         t1sz = MAX(t1sz, 16);
     }
-    if (t0sz && !extract64(address, va_size - t0sz, t0sz - tbi)) {
+    if (t0sz && !extract64(address, addrsize - t0sz, t0sz - tbi)) {
         /* there is a ttbr0 region and we are in it (high bits all zero) */
         ttbr_select = 0;
     } else if (ttbr1_valid && t1sz &&
-               !extract64(~address, va_size - t1sz, t1sz - tbi)) {
+               !extract64(~address, addrsize - t1sz, t1sz - tbi)) {
         /* there is a ttbr1 region and we are in it (high bits all one) */
         ttbr_select = 1;
     } else if (!t0sz) {
@@ -7383,7 +7393,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         if (el < 2) {
             epd = extract32(tcr->raw_tcr, 7, 1);
         }
-        inputsize = va_size - t0sz;
+        inputsize = addrsize - t0sz;
 
         tg = extract32(tcr->raw_tcr, 14, 2);
         if (tg == 1) { /* 64KB pages */
@@ -7398,7 +7408,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
 
         ttbr = regime_ttbr(env, mmu_idx, 1);
         epd = extract32(tcr->raw_tcr, 23, 1);
-        inputsize = va_size - t1sz;
+        inputsize = addrsize - t1sz;
 
         tg = extract32(tcr->raw_tcr, 30, 2);
         if (tg == 3)  { /* 64KB pages */
@@ -7410,7 +7420,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     }
 
     /* Here we should have set up all the parameters for the translation:
-     * va_size, inputsize, ttbr, epd, stride, tbi
+     * inputsize, ttbr, epd, stride, tbi
      */
 
     if (epd) {
@@ -7441,7 +7451,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         uint32_t startlevel;
         bool ok;
 
-        if (va_size == 32 || stride == 9) {
+        if (!aarch64 || stride == 9) {
             /* AArch32 or 4KB pages */
             startlevel = 2 - sl0;
         } else {
@@ -7450,7 +7460,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         }
 
         /* Check that the starting level is valid. */
-        ok = check_s2_mmu_setup(cpu, va_size == 64, startlevel,
+        ok = check_s2_mmu_setup(cpu, aarch64, startlevel,
                                 inputsize, stride);
         if (!ok) {
             fault_type = translation_fault;
@@ -7471,7 +7481,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
      * up to bit 39 for AArch32, because we don't need other bits in that case
      * to construct next descriptor address (anyway they should be all zeroes).
      */
-    descaddrmask = ((1ull << (va_size == 64 ? 48 : 40)) - 1) &
+    descaddrmask = ((1ull << (aarch64 ? 48 : 40)) - 1) &
                    ~indexmask_grainsize;
 
     /* Secure accesses start with the page table in secure memory and
@@ -7554,7 +7564,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     } else {
         ns = extract32(attrs, 3, 1);
         pxn = extract32(attrs, 11, 1);
-        *prot = get_S1prot(env, mmu_idx, va_size == 64, ap, ns, xn, pxn);
+        *prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
     }
 
     fault_type = permission_fault;
diff --git a/target-arm/internals.h b/target-arm/internals.h
index a1258738d9..728ecbab6d 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -367,7 +367,7 @@ static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit)
 static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
 {
     return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
-        | (ea << 9) | (s1ptw << 7) | fsc;
+        | ARM_EL_IL | (ea << 9) | (s1ptw << 7) | fsc;
 }
 
 static inline uint32_t syn_data_abort_no_iss(int same_el,
@@ -396,13 +396,13 @@ static inline uint32_t syn_data_abort_with_iss(int same_el,
 static inline uint32_t syn_swstep(int same_el, int isv, int ex)
 {
     return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
-        | (isv << 24) | (ex << 6) | 0x22;
+        | ARM_EL_IL | (isv << 24) | (ex << 6) | 0x22;
 }
 
 static inline uint32_t syn_watchpoint(int same_el, int cm, int wnr)
 {
     return (EC_WATCHPOINT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
-        | (cm << 8) | (wnr << 6) | 0x22;
+        | ARM_EL_IL | (cm << 8) | (wnr << 6) | 0x22;
 }
 
 static inline uint32_t syn_breakpoint(int same_el)
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 0b29b9dbf2..35912a1192 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -76,6 +76,43 @@ uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def,
 
 #if !defined(CONFIG_USER_ONLY)
 
+static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
+                                            unsigned int target_el,
+                                            bool same_el,
+                                            bool s1ptw, int is_write,
+                                            int fsc)
+{
+    uint32_t syn;
+
+    /* ISV is only set for data aborts routed to EL2 and
+     * never for stage-1 page table walks faulting on stage 2.
+     *
+     * Furthermore, ISV is only set for certain kinds of load/stores.
+     * If the template syndrome does not have ISV set, we should leave
+     * it cleared.
+     *
+     * See ARMv8 specs, D7-1974:
+     * ISS encoding for an exception from a Data Abort, the
+     * ISV field.
+     */
+    if (!(template_syn & ARM_EL_ISV) || target_el != 2 || s1ptw) {
+        syn = syn_data_abort_no_iss(same_el,
+                                    0, 0, s1ptw, is_write == 1, fsc);
+    } else {
+        /* Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template
+         * syndrome created at translation time.
+         * Now we create the runtime syndrome with the remaining fields.
+         */
+        syn = syn_data_abort_with_iss(same_el,
+                                      0, 0, 0, 0, 0,
+                                      0, 0, s1ptw, is_write == 1, fsc,
+                                      false);
+        /* Merge the runtime syndrome with the template syndrome.  */
+        syn |= template_syn;
+    }
+    return syn;
+}
+
 /* try to fill the TLB and return an exception if error. If retaddr is
  * NULL, it means that the function was called in C code (i.e. not
  * from generated code or from helper.c)
@@ -116,8 +153,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
             syn = syn_insn_abort(same_el, 0, fi.s1ptw, syn);
             exc = EXCP_PREFETCH_ABORT;
         } else {
-            syn = syn_data_abort_no_iss(same_el,
-                                        0, 0, fi.s1ptw, is_write == 1, syn);
+            syn = merge_syn_data_abort(env->exception.syndrome, target_el,
+                                       same_el, fi.s1ptw, is_write, syn);
             if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) {
                 fsr |= (1 << 11);
             }
@@ -138,6 +175,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
     CPUARMState *env = &cpu->env;
     int target_el;
     bool same_el;
+    uint32_t syn;
 
     if (retaddr) {
         /* now we have a real cpu fault */
@@ -162,10 +200,9 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
         env->exception.fsr |= (1 << 11);
     }
 
-    raise_exception(env, EXCP_DATA_ABORT,
-                    syn_data_abort_no_iss(same_el,
-                                          0, 0, 0, is_write == 1, 0x21),
-                    target_el);
+    syn = merge_syn_data_abort(env->exception.syndrome, target_el,
+                               same_el, 0, is_write, 0x21);
+    raise_exception(env, EXCP_DATA_ABORT, syn, target_el);
 }
 
 #endif /* !defined(CONFIG_USER_ONLY) */
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index ce8141a442..f5e29d20a1 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -308,6 +308,20 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
     }
 }
 
+static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn)
+{
+    /* We don't need to save all of the syndrome so we mask and shift
+     * out uneeded bits to help the sleb128 encoder do a better job.
+     */
+    syn &= ARM_INSN_START_WORD2_MASK;
+    syn >>= ARM_INSN_START_WORD2_SHIFT;
+
+    /* We check and clear insn_start_idx to catch multiple updates.  */
+    assert(s->insn_start_idx != 0);
+    tcg_set_insn_param(s->insn_start_idx, 2, syn);
+    s->insn_start_idx = 0;
+}
+
 static void unallocated_encoding(DisasContext *s)
 {
     /* Unallocated and reserved encodings are uncategorized */
@@ -723,23 +737,47 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
  * Store from GPR register to memory.
  */
 static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source,
-                             TCGv_i64 tcg_addr, int size, int memidx)
+                             TCGv_i64 tcg_addr, int size, int memidx,
+                             bool iss_valid,
+                             unsigned int iss_srt,
+                             bool iss_sf, bool iss_ar)
 {
     g_assert(size <= 3);
     tcg_gen_qemu_st_i64(source, tcg_addr, memidx, s->be_data + size);
+
+    if (iss_valid) {
+        uint32_t syn;
+
+        syn = syn_data_abort_with_iss(0,
+                                      size,
+                                      false,
+                                      iss_srt,
+                                      iss_sf,
+                                      iss_ar,
+                                      0, 0, 0, 0, 0, false);
+        disas_set_insn_syndrome(s, syn);
+    }
 }
 
 static void do_gpr_st(DisasContext *s, TCGv_i64 source,
-                      TCGv_i64 tcg_addr, int size)
+                      TCGv_i64 tcg_addr, int size,
+                      bool iss_valid,
+                      unsigned int iss_srt,
+                      bool iss_sf, bool iss_ar)
 {
-    do_gpr_st_memidx(s, source, tcg_addr, size, get_mem_index(s));
+    do_gpr_st_memidx(s, source, tcg_addr, size, get_mem_index(s),
+                     iss_valid, iss_srt, iss_sf, iss_ar);
 }
 
 /*
  * Load from memory to GPR register
  */
-static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
-                             int size, bool is_signed, bool extend, int memidx)
+static void do_gpr_ld_memidx(DisasContext *s,
+                             TCGv_i64 dest, TCGv_i64 tcg_addr,
+                             int size, bool is_signed,
+                             bool extend, int memidx,
+                             bool iss_valid, unsigned int iss_srt,
+                             bool iss_sf, bool iss_ar)
 {
     TCGMemOp memop = s->be_data + size;
 
@@ -755,13 +793,30 @@ static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
         g_assert(size < 3);
         tcg_gen_ext32u_i64(dest, dest);
     }
+
+    if (iss_valid) {
+        uint32_t syn;
+
+        syn = syn_data_abort_with_iss(0,
+                                      size,
+                                      is_signed,
+                                      iss_srt,
+                                      iss_sf,
+                                      iss_ar,
+                                      0, 0, 0, 0, 0, false);
+        disas_set_insn_syndrome(s, syn);
+    }
 }
 
-static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
-                      int size, bool is_signed, bool extend)
+static void do_gpr_ld(DisasContext *s,
+                      TCGv_i64 dest, TCGv_i64 tcg_addr,
+                      int size, bool is_signed, bool extend,
+                      bool iss_valid, unsigned int iss_srt,
+                      bool iss_sf, bool iss_ar)
 {
     do_gpr_ld_memidx(s, dest, tcg_addr, size, is_signed, extend,
-                     get_mem_index(s));
+                     get_mem_index(s),
+                     iss_valid, iss_srt, iss_sf, iss_ar);
 }
 
 /*
@@ -1817,6 +1872,22 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
 }
 #endif
 
+/* Update the Sixty-Four bit (SF) registersize. This logic is derived
+ * from the ARMv8 specs for LDR (Shared decode for all encodings).
+ */
+static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc)
+{
+    int opc0 = extract32(opc, 0, 1);
+    int regsize;
+
+    if (is_signed) {
+        regsize = opc0 ? 32 : 64;
+    } else {
+        regsize = size == 3 ? 64 : 32;
+    }
+    return regsize == 64;
+}
+
 /* C3.3.6 Load/store exclusive
  *
  *  31 30 29         24  23  22   21  20  16  15  14   10 9    5 4    0
@@ -1868,10 +1939,15 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn)
         }
     } else {
         TCGv_i64 tcg_rt = cpu_reg(s, rt);
+        bool iss_sf = disas_ldst_compute_iss_sf(size, false, 0);
+
+        /* Generate ISS for non-exclusive accesses including LASR.  */
         if (is_store) {
-            do_gpr_st(s, tcg_rt, tcg_addr, size);
+            do_gpr_st(s, tcg_rt, tcg_addr, size,
+                      true, rt, iss_sf, is_lasr);
         } else {
-            do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false);
+            do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false,
+                      true, rt, iss_sf, is_lasr);
         }
     }
 }
@@ -1923,7 +1999,11 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn)
     if (is_vector) {
         do_fp_ld(s, rt, tcg_addr, size);
     } else {
-        do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+        /* Only unsigned 32bit loads target 32bit registers.  */
+        bool iss_sf = opc == 0 ? 32 : 64;
+
+        do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false,
+                  true, rt, iss_sf, false);
     }
     tcg_temp_free_i64(tcg_addr);
 }
@@ -2042,9 +2122,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
     } else {
         TCGv_i64 tcg_rt = cpu_reg(s, rt);
         if (is_load) {
-            do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+            do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false,
+                      false, 0, false, false);
         } else {
-            do_gpr_st(s, tcg_rt, tcg_addr, size);
+            do_gpr_st(s, tcg_rt, tcg_addr, size,
+                      false, 0, false, false);
         }
     }
     tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size);
@@ -2057,9 +2139,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
     } else {
         TCGv_i64 tcg_rt2 = cpu_reg(s, rt2);
         if (is_load) {
-            do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false);
+            do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false,
+                      false, 0, false, false);
         } else {
-            do_gpr_st(s, tcg_rt2, tcg_addr, size);
+            do_gpr_st(s, tcg_rt2, tcg_addr, size,
+                      false, 0, false, false);
         }
     }
 
@@ -2102,6 +2186,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
     bool is_store = false;
     bool is_extended = false;
     bool is_unpriv = (idx == 2);
+    bool iss_valid = !is_vector;
     bool post_index;
     bool writeback;
 
@@ -2169,12 +2254,15 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
     } else {
         TCGv_i64 tcg_rt = cpu_reg(s, rt);
         int memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s);
+        bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
 
         if (is_store) {
-            do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx);
+            do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx,
+                             iss_valid, rt, iss_sf, false);
         } else {
             do_gpr_ld_memidx(s, tcg_rt, tcg_addr, size,
-                             is_signed, is_extended, memidx);
+                             is_signed, is_extended, memidx,
+                             iss_valid, rt, iss_sf, false);
         }
     }
 
@@ -2272,10 +2360,14 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn,
         }
     } else {
         TCGv_i64 tcg_rt = cpu_reg(s, rt);
+        bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
         if (is_store) {
-            do_gpr_st(s, tcg_rt, tcg_addr, size);
+            do_gpr_st(s, tcg_rt, tcg_addr, size,
+                      true, rt, iss_sf, false);
         } else {
-            do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+            do_gpr_ld(s, tcg_rt, tcg_addr, size,
+                      is_signed, is_extended,
+                      true, rt, iss_sf, false);
         }
     }
 }
@@ -2352,10 +2444,13 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn,
         }
     } else {
         TCGv_i64 tcg_rt = cpu_reg(s, rt);
+        bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
         if (is_store) {
-            do_gpr_st(s, tcg_rt, tcg_addr, size);
+            do_gpr_st(s, tcg_rt, tcg_addr, size,
+                      true, rt, iss_sf, false);
         } else {
-            do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+            do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended,
+                      true, rt, iss_sf, false);
         }
     }
 }
@@ -11102,7 +11197,8 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb)
     tcg_clear_temp_count();
 
     do {
-        tcg_gen_insn_start(dc->pc, 0);
+        dc->insn_start_idx = tcg_op_buf_count();
+        tcg_gen_insn_start(dc->pc, 0, 0);
         num_insns++;
 
         if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index e525f1eb4e..6815bc1a79 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -11732,7 +11732,8 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
       }
     do {
         tcg_gen_insn_start(dc->pc,
-                           (dc->condexec_cond << 4) | (dc->condexec_mask >> 1));
+                           (dc->condexec_cond << 4) | (dc->condexec_mask >> 1),
+                           0);
         num_insns++;
 
 #ifdef CONFIG_USER_ONLY
@@ -12049,8 +12050,10 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb,
     if (is_a64(env)) {
         env->pc = data[0];
         env->condexec_bits = 0;
+        env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
     } else {
         env->regs[15] = data[0];
         env->condexec_bits = data[1];
+        env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
     }
 }
diff --git a/target-arm/translate.h b/target-arm/translate.h
index 6a18d7badc..dbd7ac83d5 100644
--- a/target-arm/translate.h
+++ b/target-arm/translate.h
@@ -59,6 +59,8 @@ typedef struct DisasContext {
     bool ss_same_el;
     /* Bottom two bits of XScale c15_cpar coprocessor access control reg */
     int c15_cpar;
+    /* TCG op index of the current insn_start.  */
+    int insn_start_idx;
 #define TMP_A64_MAX 16
     int tmp_a64_count;
     TCGv_i64 tmp_a64[TMP_A64_MAX];