summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure1
-rw-r--r--gdb-xml/s390-acr.xml26
-rw-r--r--gdb-xml/s390-fpr.xml27
-rw-r--r--gdb-xml/s390x-core64.xml28
-rw-r--r--hw/s390x/s390-virtio-ccw.c46
-rw-r--r--hw/s390x/s390-virtio.c15
-rw-r--r--hw/s390x/sclp.c289
-rw-r--r--include/hw/s390x/sclp.h20
-rw-r--r--pc-bios/s390-ccw.imgbin17752 -> 17752 bytes
-rw-r--r--pc-bios/s390-ccw/bootmap.c107
-rw-r--r--pc-bios/s390-ccw/bootmap.h2
-rw-r--r--pc-bios/s390-ccw/virtio.c48
-rw-r--r--pc-bios/s390-ccw/virtio.h2
-rw-r--r--qemu-options.hx3
-rw-r--r--target-s390x/cpu-qom.h1
-rw-r--r--target-s390x/cpu.c10
-rw-r--r--target-s390x/cpu.h73
-rw-r--r--target-s390x/gdbstub.c109
-rw-r--r--target-s390x/kvm.c28
-rw-r--r--target-s390x/misc_helper.c30
20 files changed, 692 insertions, 173 deletions
diff --git a/configure b/configure
index 2063cf6a3c..15201f980f 100755
--- a/configure
+++ b/configure
@@ -5093,6 +5093,7 @@ case "$target_name" in
     echo "TARGET_ABI32=y" >> $config_target_mak
   ;;
   s390x)
+    gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml"
   ;;
   unicore32)
   ;;
diff --git a/gdb-xml/s390-acr.xml b/gdb-xml/s390-acr.xml
new file mode 100644
index 0000000000..71dfb20528
--- /dev/null
+++ b/gdb-xml/s390-acr.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.acr">
+  <reg name="acr0" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr1" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr2" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr3" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr4" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr5" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr6" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr7" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr8" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr9" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr10" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr11" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr12" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr13" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr14" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr15" bitsize="32" type="uint32" group="access"/>
+</feature>
diff --git a/gdb-xml/s390-fpr.xml b/gdb-xml/s390-fpr.xml
new file mode 100644
index 0000000000..7de0c136ad
--- /dev/null
+++ b/gdb-xml/s390-fpr.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.fpr">
+  <reg name="fpc" bitsize="32" type="uint32" group="float"/>
+  <reg name="f0" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f1" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f2" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f3" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f4" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f5" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f6" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f7" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f8" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f9" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f10" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f11" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f12" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f13" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f14" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f15" bitsize="64" type="ieee_double" group="float"/>
+</feature>
diff --git a/gdb-xml/s390x-core64.xml b/gdb-xml/s390x-core64.xml
new file mode 100644
index 0000000000..15234378ee
--- /dev/null
+++ b/gdb-xml/s390x-core64.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.core">
+  <reg name="pswm" bitsize="64" type="uint64" group="psw"/>
+  <reg name="pswa" bitsize="64" type="uint64" group="psw"/>
+  <reg name="r0" bitsize="64" type="uint64" group="general"/>
+  <reg name="r1" bitsize="64" type="uint64" group="general"/>
+  <reg name="r2" bitsize="64" type="uint64" group="general"/>
+  <reg name="r3" bitsize="64" type="uint64" group="general"/>
+  <reg name="r4" bitsize="64" type="uint64" group="general"/>
+  <reg name="r5" bitsize="64" type="uint64" group="general"/>
+  <reg name="r6" bitsize="64" type="uint64" group="general"/>
+  <reg name="r7" bitsize="64" type="uint64" group="general"/>
+  <reg name="r8" bitsize="64" type="uint64" group="general"/>
+  <reg name="r9" bitsize="64" type="uint64" group="general"/>
+  <reg name="r10" bitsize="64" type="uint64" group="general"/>
+  <reg name="r11" bitsize="64" type="uint64" group="general"/>
+  <reg name="r12" bitsize="64" type="uint64" group="general"/>
+  <reg name="r13" bitsize="64" type="uint64" group="general"/>
+  <reg name="r14" bitsize="64" type="uint64" group="general"/>
+  <reg name="r15" bitsize="64" type="uint64" group="general"/>
+</feature>
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 004b2c20c5..e538b1f686 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -17,6 +17,7 @@
 #include "ioinst.h"
 #include "css.h"
 #include "virtio-ccw.h"
+#include "qemu/config-file.h"
 
 #define TYPE_S390_CCW_MACHINE               "s390-ccw-machine"
 
@@ -86,17 +87,35 @@ static void ccw_init(MachineState *machine)
     ram_addr_t my_ram_size = machine->ram_size;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
-    int shift = 0;
+    sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
     uint8_t *storage_keys;
     int ret;
     VirtualCssBus *css_bus;
-
-    /* s390x ram size detection needs a 16bit multiplier + an increment. So
-       guests > 64GB can be specified in 2MB steps etc. */
-    while ((my_ram_size >> (20 + shift)) > 65535) {
-        shift++;
+    QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
+    ram_addr_t pad_size = 0;
+    ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
+    ram_addr_t standby_mem_size = maxmem - my_ram_size;
+
+    /* The storage increment size is a multiple of 1M and is a power of 2.
+     * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
+     * The variable 'mhd->increment_size' is an exponent of 2 that can be
+     * used to calculate the size (in bytes) of an increment. */
+    mhd->increment_size = 20;
+    while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
+        mhd->increment_size++;
+    }
+    while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
+        mhd->increment_size++;
     }
-    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
+
+    /* The core and standby memory areas need to be aligned with
+     * the increment size.  In effect, this can cause the
+     * user-specified memory size to be rounded down to align
+     * with the nearest increment boundary. */
+    standby_mem_size = standby_mem_size >> mhd->increment_size
+                                        << mhd->increment_size;
+    my_ram_size = my_ram_size >> mhd->increment_size
+                              << mhd->increment_size;
 
     /* let's propagate the changed ram size into the global variable. */
     ram_size = my_ram_size;
@@ -111,11 +130,22 @@ static void ccw_init(MachineState *machine)
     /* register hypercalls */
     virtio_ccw_register_hcalls();
 
-    /* allocate RAM */
+    /* allocate RAM for core */
     memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size);
     vmstate_register_ram_global(ram);
     memory_region_add_subregion(sysmem, 0, ram);
 
+    /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
+       calculate the pad size necessary to force this boundary. */
+    if (standby_mem_size) {
+        if (my_ram_size % MEM_SECTION_SIZE) {
+            pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE;
+        }
+        my_ram_size += standby_mem_size + pad_size;
+        mhd->pad_size = pad_size;
+        mhd->standby_mem_size = standby_mem_size;
+    }
+
     /* allocate storage keys */
     storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
 
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 1a75a1cf81..4ca52b7190 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -230,18 +230,21 @@ static void s390_init(MachineState *machine)
     ram_addr_t my_ram_size = machine->ram_size;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
-    int shift = 0;
+    int increment_size = 20;
     uint8_t *storage_keys;
     void *virtio_region;
     hwaddr virtio_region_len;
     hwaddr virtio_region_start;
 
-    /* s390x ram size detection needs a 16bit multiplier + an increment. So
-       guests > 64GB can be specified in 2MB steps etc. */
-    while ((my_ram_size >> (20 + shift)) > 65535) {
-        shift++;
+    /*
+     * The storage increment size is a multiple of 1M and is a power of 2.
+     * The number of storage increments must be MAX_STORAGE_INCREMENTS or
+     * fewer.
+     */
+    while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
+        increment_size++;
     }
-    my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
+    my_ram_size = my_ram_size >> increment_size << increment_size;
 
     /* let's propagate the changed ram size into the global variable. */
     ram_size = my_ram_size;
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index d8ddf35e58..02b3275132 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -16,7 +16,8 @@
 #include "sysemu/kvm.h"
 #include "exec/memory.h"
 #include "sysemu/sysemu.h"
-
+#include "exec/address-spaces.h"
+#include "qemu/config-file.h"
 #include "hw/s390x/sclp.h"
 #include "hw/s390x/event-facility.h"
 
@@ -33,10 +34,19 @@ static inline SCLPEventFacility *get_event_facility(void)
 static void read_SCP_info(SCCB *sccb)
 {
     ReadInfo *read_info = (ReadInfo *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
     CPUState *cpu;
-    int shift = 0;
     int cpu_count = 0;
     int i = 0;
+    int increment_size = 20;
+    int rnsize, rnmax;
+    QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
+    int slots = qemu_opt_get_number(opts, "slots", 0);
+    int max_avail_slots = s390_get_memslot_count(kvm_state);
+
+    if (slots > max_avail_slots) {
+        slots = max_avail_slots;
+    }
 
     CPU_FOREACH(cpu) {
         cpu_count++;
@@ -54,14 +64,235 @@ static void read_SCP_info(SCCB *sccb)
 
     read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO);
 
-    while ((ram_size >> (20 + shift)) > 65535) {
-        shift++;
+    /*
+     * The storage increment size is a multiple of 1M and is a power of 2.
+     * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
+     */
+    while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
+        increment_size++;
+    }
+    rnmax = ram_size >> increment_size;
+
+    /* Memory Hotplug is only supported for the ccw machine type */
+    if (mhd) {
+        while ((mhd->standby_mem_size >> increment_size) >
+               MAX_STORAGE_INCREMENTS) {
+            increment_size++;
+        }
+        assert(increment_size == mhd->increment_size);
+
+        mhd->standby_subregion_size = MEM_SECTION_SIZE;
+        /* Deduct the memory slot already used for core */
+        if (slots > 0) {
+            while ((mhd->standby_subregion_size * (slots - 1)
+                    < mhd->standby_mem_size)) {
+                mhd->standby_subregion_size = mhd->standby_subregion_size << 1;
+            }
+        }
+        /*
+         * Initialize mapping of guest standby memory sections indicating which
+         * are and are not online. Assume all standby memory begins offline.
+         */
+        if (mhd->standby_state_map == 0) {
+            if (mhd->standby_mem_size % mhd->standby_subregion_size) {
+                mhd->standby_state_map = g_malloc0((mhd->standby_mem_size /
+                                             mhd->standby_subregion_size + 1) *
+                                             (mhd->standby_subregion_size /
+                                             MEM_SECTION_SIZE));
+            } else {
+                mhd->standby_state_map = g_malloc0(mhd->standby_mem_size /
+                                                   MEM_SECTION_SIZE);
+            }
+        }
+        mhd->padded_ram_size = ram_size + mhd->pad_size;
+        mhd->rzm = 1 << mhd->increment_size;
+        rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size)
+             >> mhd->increment_size);
+
+        read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR);
+    }
+
+    rnsize = 1 << (increment_size - 20);
+    if (rnsize <= 128) {
+        read_info->rnsize = rnsize;
+    } else {
+        read_info->rnsize = 0;
+        read_info->rnsize2 = cpu_to_be32(rnsize);
+    }
+
+    if (rnmax < 0x10000) {
+        read_info->rnmax = cpu_to_be16(rnmax);
+    } else {
+        read_info->rnmax = cpu_to_be16(0);
+        read_info->rnmax2 = cpu_to_be64(rnmax);
     }
-    read_info->rnmax = cpu_to_be16(ram_size >> (20 + shift));
-    read_info->rnsize = 1 << shift;
+
     sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
 }
 
+static void read_storage_element0_info(SCCB *sccb)
+{
+    int i, assigned;
+    int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID;
+    ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
+
+    assert(mhd);
+
+    if ((ram_size >> mhd->increment_size) >= 0x10000) {
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
+        return;
+    }
+
+    /* Return information regarding core memory */
+    storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0);
+    assigned = ram_size >> mhd->increment_size;
+    storage_info->assigned = cpu_to_be16(assigned);
+
+    for (i = 0; i < assigned; i++) {
+        storage_info->entries[i] = cpu_to_be32(subincrement_id);
+        subincrement_id += SCLP_INCREMENT_UNIT;
+    }
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
+}
+
+static void read_storage_element1_info(SCCB *sccb)
+{
+    ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
+
+    assert(mhd);
+
+    if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) {
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
+        return;
+    }
+
+    /* Return information regarding standby memory */
+    storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0);
+    storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >>
+                                         mhd->increment_size);
+    storage_info->standby = cpu_to_be16(mhd->standby_mem_size >>
+                                        mhd->increment_size);
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION);
+}
+
+static void attach_storage_element(SCCB *sccb, uint16_t element)
+{
+    int i, assigned, subincrement_id;
+    AttachStorageElement *attach_info = (AttachStorageElement *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
+
+    assert(mhd);
+
+    if (element != 1) {
+        sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+        return;
+    }
+
+    assigned = mhd->standby_mem_size >> mhd->increment_size;
+    attach_info->assigned = cpu_to_be16(assigned);
+    subincrement_id = ((ram_size >> mhd->increment_size) << 16)
+                      + SCLP_STARTING_SUBINCREMENT_ID;
+    for (i = 0; i < assigned; i++) {
+        attach_info->entries[i] = cpu_to_be32(subincrement_id);
+        subincrement_id += SCLP_INCREMENT_UNIT;
+    }
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+}
+
+static void assign_storage(SCCB *sccb)
+{
+    MemoryRegion *mr = NULL;
+    uint64_t this_subregion_size;
+    AssignStorage *assign_info = (AssignStorage *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
+    assert(mhd);
+    ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm;
+    MemoryRegion *sysmem = get_system_memory();
+
+    if ((assign_addr % MEM_SECTION_SIZE == 0) &&
+        (assign_addr >= mhd->padded_ram_size)) {
+        /* Re-use existing memory region if found */
+        mr = memory_region_find(sysmem, assign_addr, 1).mr;
+        if (!mr) {
+
+            MemoryRegion *standby_ram = g_new(MemoryRegion, 1);
+
+            /* offset to align to standby_subregion_size for allocation */
+            ram_addr_t offset = assign_addr -
+                                (assign_addr - mhd->padded_ram_size)
+                                % mhd->standby_subregion_size;
+
+            /* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) +  NULL */
+            char id[16];
+            snprintf(id, 16, "standby.ram%d",
+                     (int)((offset - mhd->padded_ram_size) /
+                     mhd->standby_subregion_size) + 1);
+
+            /* Allocate a subregion of the calculated standby_subregion_size */
+            if (offset + mhd->standby_subregion_size >
+                mhd->padded_ram_size + mhd->standby_mem_size) {
+                this_subregion_size = mhd->padded_ram_size +
+                  mhd->standby_mem_size - offset;
+            } else {
+                this_subregion_size = mhd->standby_subregion_size;
+            }
+
+            memory_region_init_ram(standby_ram, NULL, id, this_subregion_size);
+            vmstate_register_ram_global(standby_ram);
+            memory_region_add_subregion(sysmem, offset, standby_ram);
+        }
+        /* The specified subregion is no longer in standby */
+        mhd->standby_state_map[(assign_addr - mhd->padded_ram_size)
+                               / MEM_SECTION_SIZE] = 1;
+    }
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+}
+
+static void unassign_storage(SCCB *sccb)
+{
+    MemoryRegion *mr = NULL;
+    AssignStorage *assign_info = (AssignStorage *) sccb;
+    sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
+    assert(mhd);
+    ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+    MemoryRegion *sysmem = get_system_memory();
+
+    /* if the addr is a multiple of 256 MB */
+    if ((unassign_addr % MEM_SECTION_SIZE == 0) &&
+        (unassign_addr >= mhd->padded_ram_size)) {
+        mhd->standby_state_map[(unassign_addr -
+                           mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0;
+
+        /* find the specified memory region and destroy it */
+        mr = memory_region_find(sysmem, unassign_addr, 1).mr;
+        if (mr) {
+            int i;
+            int is_removable = 1;
+            ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size -
+                                     (unassign_addr - mhd->padded_ram_size)
+                                     % mhd->standby_subregion_size);
+            /* Mark all affected subregions as 'standby' once again */
+            for (i = 0;
+                 i < (mhd->standby_subregion_size / MEM_SECTION_SIZE);
+                 i++) {
+
+                if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) {
+                    is_removable = 0;
+                    break;
+                }
+            }
+            if (is_removable) {
+                memory_region_del_subregion(sysmem, mr);
+                object_unparent(OBJECT(mr));
+                g_free(mr);
+            }
+        }
+    }
+    sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+}
+
 /* Provide information about the CPU */
 static void sclp_read_cpu_info(SCCB *sccb)
 {
@@ -103,6 +334,22 @@ static void sclp_execute(SCCB *sccb, uint32_t code)
     case SCLP_CMDW_READ_CPU_INFO:
         sclp_read_cpu_info(sccb);
         break;
+    case SCLP_READ_STORAGE_ELEMENT_INFO:
+        if (code & 0xff00) {
+            read_storage_element1_info(sccb);
+        } else {
+            read_storage_element0_info(sccb);
+        }
+        break;
+    case SCLP_ATTACH_STORAGE_ELEMENT:
+        attach_storage_element(sccb, (code & 0xff00) >> 8);
+        break;
+    case SCLP_ASSIGN_STORAGE:
+        assign_storage(sccb);
+        break;
+    case SCLP_UNASSIGN_STORAGE:
+        unassign_storage(sccb);
+        break;
     default:
         efc->command_handler(ef, sccb, code);
         break;
@@ -183,3 +430,33 @@ void s390_sclp_init(void)
                               OBJECT(dev), NULL);
     qdev_init_nofail(dev);
 }
+
+sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void)
+{
+    DeviceState *dev;
+    dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV);
+    object_property_add_child(qdev_get_machine(),
+                              TYPE_SCLP_MEMORY_HOTPLUG_DEV,
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+    return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path(
+                                   TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL));
+}
+
+sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void)
+{
+    return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path(
+                                   TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL));
+}
+
+static TypeInfo sclp_memory_hotplug_dev_info = {
+    .name = TYPE_SCLP_MEMORY_HOTPLUG_DEV,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(sclpMemoryHotplugDev),
+};
+
+static void register_types(void)
+{
+    type_register_static(&sclp_memory_hotplug_dev_info);
+}
+type_init(register_types);
diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
index 7ef16226df..5c435749e1 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -37,6 +37,7 @@
 #define SCLP_STARTING_SUBINCREMENT_ID           0x10001
 #define SCLP_INCREMENT_UNIT                     0x10000
 #define MAX_AVAIL_SLOTS                         32
+#define MAX_STORAGE_INCREMENTS                  1020
 
 /* CPU hotplug SCLP codes */
 #define SCLP_HAS_CPU_INFO                       0x0C00000000000000ULL
@@ -156,6 +157,23 @@ typedef struct SCCB {
     char data[SCCB_DATA_LEN];
  } QEMU_PACKED SCCB;
 
+typedef struct sclpMemoryHotplugDev sclpMemoryHotplugDev;
+
+#define TYPE_SCLP_MEMORY_HOTPLUG_DEV "sclp-memory-hotplug-dev"
+#define SCLP_MEMORY_HOTPLUG_DEV(obj) \
+  OBJECT_CHECK(sclpMemoryHotplugDev, (obj), TYPE_SCLP_MEMORY_HOTPLUG_DEV)
+
+struct sclpMemoryHotplugDev {
+    SysBusDevice parent;
+    ram_addr_t standby_mem_size;
+    ram_addr_t padded_ram_size;
+    ram_addr_t pad_size;
+    ram_addr_t standby_subregion_size;
+    ram_addr_t rzm;
+    int increment_size;
+    char *standby_state_map;
+};
+
 static inline int sccb_data_len(SCCB *sccb)
 {
     return be16_to_cpu(sccb->h.length) - sizeof(sccb->h);
@@ -163,6 +181,8 @@ static inline int sccb_data_len(SCCB *sccb)
 
 
 void s390_sclp_init(void);
+sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void);
+sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void);
 void sclp_service_interrupt(uint32_t sccb);
 void raise_irq_cpu_hotplug(void);
 
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index e3ea0d5664..44873ad181 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differdiff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index f1756796df..115d8bbac6 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -40,11 +40,6 @@ static void jump_to_IPL_2(void)
     ResetInfo *current = 0;
 
     void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue;
-    debug_print_addr("set IPL addr to", ipl);
-
-    /* Ensure the guest output starts fresh */
-    sclp_print("\n");
-
     *current = save;
     ipl(); /* should not return */
 }
@@ -64,6 +59,11 @@ static void jump_to_IPL_code(uint64_t address)
     current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2;
     current->ipl_continue = address & 0x7fffffff;
 
+    debug_print_int("set IPL addr to", current->ipl_continue);
+
+    /* Ensure the guest output starts fresh */
+    sclp_print("\n");
+
     /*
      * HACK ALERT.
      * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2
@@ -93,11 +93,23 @@ static inline void verify_boot_info(BootInfo *bip)
                "Bad block size in zIPL section of the 1st record.");
 }
 
-static bool eckd_valid_address(BootMapPointer *p)
+static block_number_t eckd_block_num(BootMapPointer *p)
 {
+    const uint64_t sectors = virtio_get_sectors();
+    const uint64_t heads = virtio_get_heads();
     const uint64_t cylinder = p->eckd.cylinder
                             + ((p->eckd.head & 0xfff0) << 12);
     const uint64_t head = p->eckd.head & 0x000f;
+    const block_number_t block = sectors * heads * cylinder
+                               + sectors * head
+                               + p->eckd.sector
+                               - 1; /* block nr starts with zero */
+    return block;
+}
+
+static bool eckd_valid_address(BootMapPointer *p)
+{
+    const uint64_t head = p->eckd.head & 0x000f;
 
     if (head >= virtio_get_heads()
         ||  p->eckd.sector > virtio_get_sectors()
@@ -105,27 +117,14 @@ static bool eckd_valid_address(BootMapPointer *p)
         return false;
     }
 
-    if (!virtio_guessed_disk_nature() && cylinder >= virtio_get_cylinders()) {
+    if (!virtio_guessed_disk_nature() &&
+        eckd_block_num(p) >= virtio_get_blocks()) {
         return false;
     }
 
     return true;
 }
 
-static block_number_t eckd_block_num(BootMapPointer *p)
-{
-    const uint64_t sectors = virtio_get_sectors();
-    const uint64_t heads = virtio_get_heads();
-    const uint64_t cylinder = p->eckd.cylinder
-                            + ((p->eckd.head & 0xfff0) << 12);
-    const uint64_t head = p->eckd.head & 0x000f;
-    const block_number_t block = sectors * heads * cylinder
-                               + sectors * head
-                               + p->eckd.sector
-                               - 1; /* block nr starts with zero */
-    return block;
-}
-
 static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
 {
     block_number_t block_nr;
@@ -223,7 +222,6 @@ static void ipl_eckd_cdl(void)
 
     memset(sec, FREE_SPACE_FILLER, sizeof(sec));
     read_block(1, ipl2, "Cannot read IPL2 record at block 1");
-    IPL_assert(magic_match(ipl2, IPL2_MAGIC), "No IPL2 record");
 
     mbr = &ipl2->u.x.mbr;
     IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record.");
@@ -247,12 +245,10 @@ static void ipl_eckd_cdl(void)
     /* no return */
 }
 
-static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
+static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
 {
     LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */
     char msg[4] = { '?', '.', '\n', '\0' };
-    block_number_t block_nr;
-    BootInfo *bip;
 
     sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL");
     sclp_print(" version ");
@@ -272,12 +268,27 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
     }
     sclp_print(msg);
     print_volser(vlbl->volser);
+}
+
+static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
+{
+    block_number_t block_nr;
+    BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */
+
+    if (mode != ECKD_LDL_UNLABELED) {
+        print_eckd_ldl_msg(mode);
+    }
 
     /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */
 
     memset(sec, FREE_SPACE_FILLER, sizeof(sec));
-    read_block(0, sec, "Cannot read block 0");
-    bip = (void *)(sec + 0x70); /* "boot info" is "eckd mbr" for LDL */
+    read_block(0, sec, "Cannot read block 0 to grab boot info.");
+    if (mode == ECKD_LDL_UNLABELED) {
+        if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+            return; /* not applicable layout */
+        }
+        sclp_print("unlabeled LDL.\n");
+    }
     verify_boot_info(bip);
 
     block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr));
@@ -285,17 +296,23 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
     /* no return */
 }
 
-static void ipl_eckd(ECKD_IPL_mode_t mode)
+static void print_eckd_msg(void)
 {
-    switch (mode) {
-    case ECKD_CDL:
-        ipl_eckd_cdl(); /* no return */
-    case ECKD_CMS:
-    case ECKD_LDL:
-        ipl_eckd_ldl(mode); /* no return */
-    default:
-        virtio_panic("\n! Unknown ECKD IPL mode !\n");
+    char msg[] = "Using ECKD scheme (block size *****), ";
+    char *p = &msg[34], *q = &msg[30];
+    int n = virtio_get_block_size();
+
+    /* Fill in the block size and show up the message */
+    if (n > 0 && n <= 99999) {
+        while (n) {
+            *p-- = '0' + (n % 10);
+            n /= 10;
+        }
+        while (p >= q) {
+            *p-- = ' ';
+        }
     }
+    sclp_print(msg);
 }
 
 /***********************************************************************
@@ -447,14 +464,13 @@ void zipl_load(void)
     }
 
     /* We have failed to follow the SCSI scheme, so */
-    sclp_print("Using ECKD scheme.\n");
     if (virtio_guessed_disk_nature()) {
         sclp_print("Using guessed DASD geometry.\n");
         virtio_assume_eckd();
     }
-
+    print_eckd_msg();
     if (magic_match(mbr->magic, IPL1_MAGIC)) {
-        ipl_eckd(ECKD_CDL); /* no return */
+        ipl_eckd_cdl(); /* no return */
     }
 
     /* LDL/CMS? */
@@ -462,11 +478,18 @@ void zipl_load(void)
     read_block(2, vlbl, "Cannot read block 2");
 
     if (magic_match(vlbl->magic, CMS1_MAGIC)) {
-        ipl_eckd(ECKD_CMS); /* no return */
+        ipl_eckd_ldl(ECKD_CMS); /* no return */
     }
     if (magic_match(vlbl->magic, LNX1_MAGIC)) {
-        ipl_eckd(ECKD_LDL); /* no return */
+        ipl_eckd_ldl(ECKD_LDL); /* no return */
     }
 
-    virtio_panic("\n* invalid MBR magic *\n");
+    ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */
+    /*
+     * Ok, it is not a LDL by any means.
+     * It still might be a CDL with zero record keys for IPL1 and IPL2
+     */
+    ipl_eckd_cdl();
+
+    virtio_panic("\n* this can never happen *\n");
 }
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 30ef22fe61..6a4823d544 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -257,9 +257,9 @@ typedef struct IplVolumeLabel {
 
 typedef enum {
     ECKD_NO_IPL,
-    ECKD_CDL,
     ECKD_CMS,
     ECKD_LDL,
+    ECKD_LDL_UNLABELED,
 } ECKD_IPL_mode_t;
 
 /* utility code below */
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 31b23b086c..c0540d1cd4 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -275,12 +275,14 @@ void virtio_assume_scsi(void)
 {
     guessed_disk_nature = true;
     blk_cfg.blk_size = 512;
+    blk_cfg.physical_block_exp = 0;
 }
 
 void virtio_assume_eckd(void)
 {
     guessed_disk_nature = true;
     blk_cfg.blk_size = 4096;
+    blk_cfg.physical_block_exp = 0;
 
     /* this must be here to calculate code segment position */
     blk_cfg.geometry.heads = 15;
@@ -290,36 +292,52 @@ void virtio_assume_eckd(void)
 bool virtio_disk_is_scsi(void)
 {
     if (guessed_disk_nature) {
-        return (blk_cfg.blk_size  == 512);
+        return (virtio_get_block_size()  == 512);
     }
     return (blk_cfg.geometry.heads == 255)
         && (blk_cfg.geometry.sectors == 63)
-        && (blk_cfg.blk_size  == 512);
+        && (virtio_get_block_size()  == 512);
+}
+
+/*
+ * Other supported value pairs, if any, would need to be added here.
+ * Note: head count is always 15.
+ */
+static inline u8 virtio_eckd_sectors_for_block_size(int size)
+{
+    switch (size) {
+    case 512:
+        return 49;
+    case 1024:
+        return 33;
+    case 2048:
+        return 21;
+    case 4096:
+        return 12;
+    }
+    return 0;
 }
 
 bool virtio_disk_is_eckd(void)
 {
+    const int block_size = virtio_get_block_size();
+
     if (guessed_disk_nature) {
-        return (blk_cfg.blk_size  == 4096);
+        return (block_size  == 4096);
     }
     return (blk_cfg.geometry.heads == 15)
-        && (blk_cfg.geometry.sectors == 12)
-        && (blk_cfg.blk_size  == 4096);
+        && (blk_cfg.geometry.sectors ==
+            virtio_eckd_sectors_for_block_size(block_size));
 }
 
 bool virtio_ipl_disk_is_valid(void)
 {
-    return blk_cfg.blk_size && (virtio_disk_is_scsi() || virtio_disk_is_eckd());
+    return virtio_disk_is_scsi() || virtio_disk_is_eckd();
 }
 
 int virtio_get_block_size(void)
 {
-    return blk_cfg.blk_size;
-}
-
-uint16_t virtio_get_cylinders(void)
-{
-    return blk_cfg.geometry.cylinders;
+    return blk_cfg.blk_size << blk_cfg.physical_block_exp;
 }
 
 uint8_t virtio_get_heads(void)
@@ -332,6 +350,12 @@ uint8_t virtio_get_sectors(void)
     return blk_cfg.geometry.sectors;
 }
 
+uint64_t virtio_get_blocks(void)
+{
+    return blk_cfg.capacity /
+           (virtio_get_block_size() / VIRTIO_SECTOR_SIZE);
+}
+
 void virtio_setup_block(struct subchannel_id schid)
 {
     struct vq_info_block info;
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index f1fb1b08fa..c23466b8db 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -192,9 +192,9 @@ extern bool virtio_disk_is_scsi(void);
 extern bool virtio_disk_is_eckd(void);
 extern bool virtio_ipl_disk_is_valid(void);
 extern int virtio_get_block_size(void);
-extern uint16_t virtio_get_cylinders(void);
 extern uint8_t virtio_get_heads(void);
 extern uint8_t virtio_get_sectors(void);
+extern uint64_t virtio_get_blocks(void);
 extern int virtio_read_many(ulong sector, void *load_addr, int sec_num);
 
 #define VIRTIO_SECTOR_SIZE 512
diff --git a/qemu-options.hx b/qemu-options.hx
index 5479cf54f4..ecd0e34269 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -225,7 +225,8 @@ DEF("m", HAS_ARG, QEMU_OPTION_m,
     "                size: initial amount of guest memory (default: "
     stringify(DEFAULT_RAM_SIZE) "MiB)\n"
     "                slots: number of hotplug slots (default: none)\n"
-    "                maxmem: maximum amount of guest memory (default: none)\n",
+    "                maxmem: maximum amount of guest memory (default: none)\n"
+    "NOTE: Some architectures might enforce a specific granularity\n",
     QEMU_ARCH_ALL)
 STEXI
 @item -m [size=]@var{megs}
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index f9c96d13a9..80dd74142c 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -89,5 +89,6 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
 int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
 int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+void s390_cpu_gdb_init(CPUState *cs);
 
 #endif
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index c3082b73c5..97a92168a8 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -165,7 +165,7 @@ static void s390_cpu_machine_reset_cb(void *opaque)
 {
     S390CPU *cpu = opaque;
 
-    cpu_reset(CPU(cpu));
+    run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, CPU(cpu));
 }
 #endif
 
@@ -174,8 +174,13 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp)
     CPUState *cs = CPU(dev);
     S390CPUClass *scc = S390_CPU_GET_CLASS(dev);
 
+    s390_cpu_gdb_init(cs);
     qemu_init_vcpu(cs);
+#if !defined(CONFIG_USER_ONLY)
+    run_on_cpu(cs, s390_do_cpu_full_reset, cs);
+#else
     cpu_reset(cs);
+#endif
 
     scc->parent_realize(dev, errp);
 }
@@ -259,7 +264,8 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
     cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote;
 #endif
     dc->vmsd = &vmstate_s390_cpu;
-    cc->gdb_num_core_regs = S390_NUM_REGS;
+    cc->gdb_num_core_regs = S390_NUM_CORE_REGS;
+    cc->gdb_core_xml_file = "s390x-core64.xml";
 }
 
 static const TypeInfo s390_cpu_type_info = {
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index b13761d925..62940c398a 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -353,6 +353,21 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
 /* Base/displacement are at the same locations. */
 #define decode_basedisp_rs decode_basedisp_s
 
+/* helper functions for run_on_cpu() */
+static inline void s390_do_cpu_reset(void *arg)
+{
+    CPUState *cs = arg;
+    S390CPUClass *scc = S390_CPU_GET_CLASS(cs);
+
+    scc->cpu_reset(cs);
+}
+static inline void s390_do_cpu_full_reset(void *arg)
+{
+    CPUState *cs = arg;
+
+    cpu_reset(cs);
+}
+
 void s390x_tod_timer(void *opaque);
 void s390x_cpu_timer(void *opaque);
 
@@ -551,44 +566,8 @@ void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 #define S390_R13_REGNUM 15
 #define S390_R14_REGNUM 16
 #define S390_R15_REGNUM 17
-/* Access Registers.  */
-#define S390_A0_REGNUM 18
-#define S390_A1_REGNUM 19
-#define S390_A2_REGNUM 20
-#define S390_A3_REGNUM 21
-#define S390_A4_REGNUM 22
-#define S390_A5_REGNUM 23
-#define S390_A6_REGNUM 24
-#define S390_A7_REGNUM 25
-#define S390_A8_REGNUM 26
-#define S390_A9_REGNUM 27
-#define S390_A10_REGNUM 28
-#define S390_A11_REGNUM 29
-#define S390_A12_REGNUM 30
-#define S390_A13_REGNUM 31
-#define S390_A14_REGNUM 32
-#define S390_A15_REGNUM 33
-/* Floating Point Control Word.  */
-#define S390_FPC_REGNUM 34
-/* Floating Point Registers.  */
-#define S390_F0_REGNUM 35
-#define S390_F1_REGNUM 36
-#define S390_F2_REGNUM 37
-#define S390_F3_REGNUM 38
-#define S390_F4_REGNUM 39
-#define S390_F5_REGNUM 40
-#define S390_F6_REGNUM 41
-#define S390_F7_REGNUM 42
-#define S390_F8_REGNUM 43
-#define S390_F9_REGNUM 44
-#define S390_F10_REGNUM 45
-#define S390_F11_REGNUM 46
-#define S390_F12_REGNUM 47
-#define S390_F13_REGNUM 48
-#define S390_F14_REGNUM 49
-#define S390_F15_REGNUM 50
-/* Total.  */
-#define S390_NUM_REGS 51
+/* Total Core Registers. */
+#define S390_NUM_CORE_REGS 18
 
 /* CC optimization */
 
@@ -1045,6 +1024,10 @@ static inline void cpu_inject_crw_mchk(S390CPU *cpu)
     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
 }
 
+/* from s390-virtio-ccw */
+#define MEM_SECTION_SIZE             0x10000000UL
+#define MAX_AVAIL_SLOTS              32
+
 /* fpu_helper.c */
 uint32_t set_cc_nz_f32(float32 v);
 uint32_t set_cc_nz_f64(float64 v);
@@ -1067,6 +1050,7 @@ void kvm_s390_enable_css_support(S390CPU *cpu);
 int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
                                     int vq, bool assign);
 int kvm_s390_cpu_restart(S390CPU *cpu);
+int kvm_s390_get_memslot_count(KVMState *s);
 void kvm_s390_clear_cmma_callback(void *opaque);
 #else
 static inline void kvm_s390_io_interrupt(uint16_t subchannel_id,
@@ -1094,6 +1078,10 @@ static inline int kvm_s390_cpu_restart(S390CPU *cpu)
 static inline void kvm_s390_clear_cmma_callback(void *opaque)
 {
 }
+static inline int kvm_s390_get_memslot_count(KVMState *s)
+{
+  return MAX_AVAIL_SLOTS;
+}
 #endif
 
 static inline void cmma_reset(S390CPU *cpu)
@@ -1112,6 +1100,15 @@ static inline int s390_cpu_restart(S390CPU *cpu)
     return -ENOSYS;
 }
 
+static inline int s390_get_memslot_count(KVMState *s)
+{
+    if (kvm_enabled()) {
+        return kvm_s390_get_memslot_count(s);
+    } else {
+        return MAX_AVAIL_SLOTS;
+    }
+}
+
 void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
                        uint32_t io_int_parm, uint32_t io_int_word);
 void s390_crw_mchk(void);
diff --git a/target-s390x/gdbstub.c b/target-s390x/gdbstub.c
index a129742e2f..8945f0271d 100644
--- a/target-s390x/gdbstub.c
+++ b/target-s390x/gdbstub.c
@@ -31,21 +31,18 @@ int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
 
     switch (n) {
     case S390_PSWM_REGNUM:
-        cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);
-        val = deposit64(env->psw.mask, 44, 2, cc_op);
-        return gdb_get_regl(mem_buf, val);
+        if (tcg_enabled()) {
+            cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst,
+                            env->cc_vr);
+            val = deposit64(env->psw.mask, 44, 2, cc_op);
+            return gdb_get_regl(mem_buf, val);
+        }
+        return gdb_get_regl(mem_buf, env->psw.mask);
     case S390_PSWA_REGNUM:
         return gdb_get_regl(mem_buf, env->psw.addr);
     case S390_R0_REGNUM ... S390_R15_REGNUM:
-        return gdb_get_regl(mem_buf, env->regs[n-S390_R0_REGNUM]);
-    case S390_A0_REGNUM ... S390_A15_REGNUM:
-        return gdb_get_reg32(mem_buf, env->aregs[n-S390_A0_REGNUM]);
-    case S390_FPC_REGNUM:
-        return gdb_get_reg32(mem_buf, env->fpc);
-    case S390_F0_REGNUM ... S390_F15_REGNUM:
-        return gdb_get_reg64(mem_buf, env->fregs[n-S390_F0_REGNUM].ll);
+        return gdb_get_regl(mem_buf, env->regs[n - S390_R0_REGNUM]);
     }
-
     return 0;
 }
 
@@ -53,36 +50,94 @@ int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
 {
     S390CPU *cpu = S390_CPU(cs);
     CPUS390XState *env = &cpu->env;
-    target_ulong tmpl;
-    uint32_t tmp32;
-    int r = 8;
-    tmpl = ldtul_p(mem_buf);
-    tmp32 = ldl_p(mem_buf);
+    target_ulong tmpl = ldtul_p(mem_buf);
 
     switch (n) {
     case S390_PSWM_REGNUM:
         env->psw.mask = tmpl;
-        env->cc_op = extract64(tmpl, 44, 2);
+        if (tcg_enabled()) {
+            env->cc_op = extract64(tmpl, 44, 2);
+        }
         break;
     case S390_PSWA_REGNUM:
         env->psw.addr = tmpl;
         break;
     case S390_R0_REGNUM ... S390_R15_REGNUM:
-        env->regs[n-S390_R0_REGNUM] = tmpl;
+        env->regs[n - S390_R0_REGNUM] = tmpl;
         break;
+    default:
+        return 0;
+    }
+    return 8;
+}
+
+/* the values represent the positions in s390-acr.xml */
+#define S390_A0_REGNUM 0
+#define S390_A15_REGNUM 15
+/* total number of registers in s390-acr.xml */
+#define S390_NUM_AC_REGS 16
+
+static int cpu_read_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n)
+{
+    switch (n) {
     case S390_A0_REGNUM ... S390_A15_REGNUM:
-        env->aregs[n-S390_A0_REGNUM] = tmp32;
-        r = 4;
-        break;
+        return gdb_get_reg32(mem_buf, env->aregs[n]);
+    default:
+        return 0;
+    }
+}
+
+static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n)
+{
+    switch (n) {
+    case S390_A0_REGNUM ... S390_A15_REGNUM:
+        env->aregs[n] = ldl_p(mem_buf);
+        return 4;
+    default:
+        return 0;
+    }
+}
+
+/* the values represent the positions in s390-fpr.xml */
+#define S390_FPC_REGNUM 0
+#define S390_F0_REGNUM 1
+#define S390_F15_REGNUM 16
+/* total number of registers in s390-fpr.xml */
+#define S390_NUM_FP_REGS 17
+
+static int cpu_read_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n)
+{
+    switch (n) {
     case S390_FPC_REGNUM:
-        env->fpc = tmp32;
-        r = 4;
-        break;
+        return gdb_get_reg32(mem_buf, env->fpc);
     case S390_F0_REGNUM ... S390_F15_REGNUM:
-        env->fregs[n-S390_F0_REGNUM].ll = tmpl;
-        break;
+        return gdb_get_reg64(mem_buf, env->fregs[n - S390_F0_REGNUM].ll);
     default:
         return 0;
     }
-    return r;
+}
+
+static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n)
+{
+    switch (n) {
+    case S390_FPC_REGNUM:
+        env->fpc = ldl_p(mem_buf);
+        return 4;
+    case S390_F0_REGNUM ... S390_F15_REGNUM:
+        env->fregs[n - S390_F0_REGNUM].ll = ldtul_p(mem_buf);
+        return 8;
+    default:
+        return 0;
+    }
+}
+
+void s390_cpu_gdb_init(CPUState *cs)
+{
+    gdb_register_coprocessor(cs, cpu_read_ac_reg,
+                             cpu_write_ac_reg,
+                             S390_NUM_AC_REGS, "s390-acr.xml", 0);
+
+    gdb_register_coprocessor(cs, cpu_read_fp_reg,
+                             cpu_write_fp_reg,
+                             S390_NUM_FP_REGS, "s390-fpr.xml", 0);
 }
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index a32d91aa01..a85a480c6a 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -916,23 +916,30 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
     return r;
 }
 
-static int kvm_s390_cpu_start(S390CPU *cpu)
+static void sigp_cpu_start(void *arg)
 {
+    CPUState *cs = arg;
+    S390CPU *cpu = S390_CPU(cs);
+
     s390_add_running_cpu(cpu);
-    qemu_cpu_kick(CPU(cpu));
     DPRINTF("DONE: KVM cpu start: %p\n", &cpu->env);
-    return 0;
 }
 
-int kvm_s390_cpu_restart(S390CPU *cpu)
+static void sigp_cpu_restart(void *arg)
 {
+    CPUState *cs = arg;
+    S390CPU *cpu = S390_CPU(cs);
     struct kvm_s390_irq irq = {
         .type = KVM_S390_RESTART,
     };
 
     kvm_s390_vcpu_interrupt(cpu, &irq);
     s390_add_running_cpu(cpu);
-    qemu_cpu_kick(CPU(cpu));
+}
+
+int kvm_s390_cpu_restart(S390CPU *cpu)
+{
+    run_on_cpu(CPU(cpu), sigp_cpu_restart, CPU(cpu));
     DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
     return 0;
 }
@@ -980,10 +987,12 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
 
     switch (order_code) {
     case SIGP_START:
-        cc = kvm_s390_cpu_start(target_cpu);
+        run_on_cpu(CPU(target_cpu), sigp_cpu_start, CPU(target_cpu));
+        cc = 0;
         break;
     case SIGP_RESTART:
-        cc = kvm_s390_cpu_restart(target_cpu);
+        run_on_cpu(CPU(target_cpu), sigp_cpu_restart, CPU(target_cpu));
+        cc = 0;
         break;
     case SIGP_SET_ARCH:
         *statusreg &= 0xffffffff00000000UL;
@@ -1306,3 +1315,8 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
     }
     return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
 }
+
+int kvm_s390_get_memslot_count(KVMState *s)
+{
+    return kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS);
+}
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index 0b625826ef..ef9758a96a 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -114,33 +114,16 @@ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
 }
 
 #ifndef CONFIG_USER_ONLY
-static void cpu_reset_all(void)
-{
-    CPUState *cs;
-    S390CPUClass *scc;
-
-    CPU_FOREACH(cs) {
-        scc = S390_CPU_GET_CLASS(cs);
-        scc->cpu_reset(cs);
-    }
-}
-
-static void cpu_full_reset_all(void)
-{
-    CPUState *cpu;
-
-    CPU_FOREACH(cpu) {
-        cpu_reset(cpu);
-    }
-}
-
 static int modified_clear_reset(S390CPU *cpu)
 {
     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
+    CPUState *t;
 
     pause_all_vcpus();
     cpu_synchronize_all_states();
-    cpu_full_reset_all();
+    CPU_FOREACH(t) {
+        run_on_cpu(t, s390_do_cpu_full_reset, t);
+    }
     cmma_reset(cpu);
     io_subsystem_reset();
     scc->load_normal(CPU(cpu));
@@ -152,10 +135,13 @@ static int modified_clear_reset(S390CPU *cpu)
 static int load_normal_reset(S390CPU *cpu)
 {
     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
+    CPUState *t;
 
     pause_all_vcpus();
     cpu_synchronize_all_states();
-    cpu_reset_all();
+    CPU_FOREACH(t) {
+        run_on_cpu(t, s390_do_cpu_reset, t);
+    }
     cmma_reset(cpu);
     io_subsystem_reset();
     scc->initial_cpu_reset(CPU(cpu));