summary refs log tree commit diff stats
path: root/hw/s390x
diff options
context:
space:
mode:
Diffstat (limited to 'hw/s390x')
-rw-r--r--hw/s390x/css.c60
-rw-r--r--hw/s390x/ipl.c233
-rw-r--r--hw/s390x/ipl.h25
-rw-r--r--hw/s390x/s390-pci-inst.c37
-rw-r--r--hw/s390x/s390-virtio-ccw.c2
-rw-r--r--hw/s390x/s390-virtio.c8
-rw-r--r--hw/s390x/s390-virtio.h3
7 files changed, 279 insertions, 89 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index d0c5ddeece..9a13b006dd 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -584,7 +584,7 @@ static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src)
     }
 }
 
-int css_do_msch(SubchDev *sch, SCHIB *orig_schib)
+int css_do_msch(SubchDev *sch, const SCHIB *orig_schib)
 {
     SCSW *s = &sch->curr_status.scsw;
     PMCW *p = &sch->curr_status.pmcw;
@@ -801,7 +801,8 @@ out:
     return ret;
 }
 
-static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw)
+static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw,
+                              int *irb_len)
 {
     int i;
     uint16_t stctl = src->scsw.ctrl & SCSW_CTRL_MASK_STCTL;
@@ -815,6 +816,8 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw)
     for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) {
         dest->ecw[i] = cpu_to_be32(src->ecw[i]);
     }
+    *irb_len = sizeof(*dest) - sizeof(dest->emw);
+
     /* extended measurements enabled? */
     if ((src->scsw.flags & SCSW_FLAGS_MASK_ESWF) ||
         !(pmcw->flags & PMCW_FLAGS_MASK_TF) ||
@@ -832,26 +835,21 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw)
             dest->emw[i] = cpu_to_be32(src->emw[i]);
         }
     }
+    *irb_len = sizeof(*dest);
 }
 
-int css_do_tsch(SubchDev *sch, IRB *target_irb)
+int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len)
 {
     SCSW *s = &sch->curr_status.scsw;
     PMCW *p = &sch->curr_status.pmcw;
     uint16_t stctl;
-    uint16_t fctl;
-    uint16_t actl;
     IRB irb;
-    int ret;
 
     if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) {
-        ret = 3;
-        goto out;
+        return 3;
     }
 
     stctl = s->ctrl & SCSW_CTRL_MASK_STCTL;
-    fctl = s->ctrl & SCSW_CTRL_MASK_FCTL;
-    actl = s->ctrl & SCSW_CTRL_MASK_ACTL;
 
     /* Prepare the irb for the guest. */
     memset(&irb, 0, sizeof(IRB));
@@ -876,7 +874,22 @@ int css_do_tsch(SubchDev *sch, IRB *target_irb)
         }
     }
     /* Store the irb to the guest. */
-    copy_irb_to_guest(target_irb, &irb, p);
+    copy_irb_to_guest(target_irb, &irb, p, irb_len);
+
+    return ((stctl & SCSW_STCTL_STATUS_PEND) == 0);
+}
+
+void css_do_tsch_update_subch(SubchDev *sch)
+{
+    SCSW *s = &sch->curr_status.scsw;
+    PMCW *p = &sch->curr_status.pmcw;
+    uint16_t stctl;
+    uint16_t fctl;
+    uint16_t actl;
+
+    stctl = s->ctrl & SCSW_CTRL_MASK_STCTL;
+    fctl = s->ctrl & SCSW_CTRL_MASK_FCTL;
+    actl = s->ctrl & SCSW_CTRL_MASK_ACTL;
 
     /* Clear conditions on subchannel, if applicable. */
     if (stctl & SCSW_STCTL_STATUS_PEND) {
@@ -913,11 +926,6 @@ int css_do_tsch(SubchDev *sch, IRB *target_irb)
             memset(sch->sense_data, 0 , sizeof(sch->sense_data));
         }
     }
-
-    ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0);
-
-out:
-    return ret;
 }
 
 static void copy_crw_to_guest(CRW *dest, const CRW *src)
@@ -947,6 +955,26 @@ int css_do_stcrw(CRW *crw)
     return ret;
 }
 
+static void copy_crw_from_guest(CRW *dest, const CRW *src)
+{
+    dest->flags = be16_to_cpu(src->flags);
+    dest->rsid = be16_to_cpu(src->rsid);
+}
+
+void css_undo_stcrw(CRW *crw)
+{
+    CrwContainer *crw_cont;
+
+    crw_cont = g_try_malloc0(sizeof(CrwContainer));
+    if (!crw_cont) {
+        channel_subsys->crws_lost = true;
+        return;
+    }
+    copy_crw_from_guest(&crw_cont->crw, crw);
+
+    QTAILQ_INSERT_HEAD(&channel_subsys->pending_crws, crw_cont, sibling);
+}
+
 int css_do_tpi(IOIntCode *int_code, int lowcore)
 {
     /* No pending interrupts for !KVM. */
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 4ba8409668..b57adbd99e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -18,6 +18,7 @@
 #include "hw/sysbus.h"
 #include "hw/s390x/virtio-ccw.h"
 #include "hw/s390x/css.h"
+#include "ipl.h"
 
 #define KERN_IMAGE_START                0x010000UL
 #define KERN_PARM_AREA                  0x010480UL
@@ -50,14 +51,49 @@ typedef struct S390IPLState {
     /*< private >*/
     SysBusDevice parent_obj;
     uint64_t start_addr;
+    uint64_t bios_start_addr;
+    bool enforce_bios;
+    IplParameterBlock iplb;
+    bool iplb_valid;
+    bool reipl_requested;
 
     /*< public >*/
     char *kernel;
     char *initrd;
     char *cmdline;
     char *firmware;
+    uint8_t cssid;
+    uint8_t ssid;
+    uint16_t devno;
 } S390IPLState;
 
+static const VMStateDescription vmstate_iplb = {
+    .name = "ipl/iplb",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110),
+        VMSTATE_UINT16(devno, IplParameterBlock),
+        VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ipl = {
+    .name = "ipl",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(start_addr, S390IPLState),
+        VMSTATE_UINT64(bios_start_addr, S390IPLState),
+        VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock),
+        VMSTATE_BOOL(iplb_valid, S390IPLState),
+        VMSTATE_UINT8(cssid, S390IPLState),
+        VMSTATE_UINT8(ssid, S390IPLState),
+        VMSTATE_UINT16(devno, S390IPLState),
+        VMSTATE_END_OF_LIST()
+     }
+};
 
 static int s390_ipl_init(SysBusDevice *dev)
 {
@@ -65,11 +101,14 @@ static int s390_ipl_init(SysBusDevice *dev)
     uint64_t pentry = KERN_IMAGE_START;
     int kernel_size;
 
-    if (!ipl->kernel) {
-        int bios_size;
-        char *bios_filename;
+    int bios_size;
+    char *bios_filename;
 
-        /* Load zipl bootloader */
+    /*
+     * Always load the bios if it was enforced,
+     * even if an external kernel has been defined.
+     */
+    if (!ipl->kernel || ipl->enforce_bios) {
         if (bios_name == NULL) {
             bios_name = ipl->firmware;
         }
@@ -79,12 +118,12 @@ static int s390_ipl_init(SysBusDevice *dev)
             hw_error("could not find stage1 bootloader\n");
         }
 
-        bios_size = load_elf(bios_filename, NULL, NULL, &ipl->start_addr, NULL,
-                             NULL, 1, ELF_MACHINE, 0);
+        bios_size = load_elf(bios_filename, NULL, NULL, &ipl->bios_start_addr,
+                             NULL, NULL, 1, ELF_MACHINE, 0);
         if (bios_size < 0) {
             bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
                                             4096);
-            ipl->start_addr = ZIPL_IMAGE_START;
+            ipl->bios_start_addr = ZIPL_IMAGE_START;
             if (bios_size > 4096) {
                 hw_error("stage1 bootloader is > 4k\n");
             }
@@ -94,52 +133,59 @@ static int s390_ipl_init(SysBusDevice *dev)
         if (bios_size == -1) {
             hw_error("could not load bootloader '%s'\n", bios_name);
         }
-        return 0;
-    }
 
-    kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
-                           NULL, 1, ELF_MACHINE, 0);
-    if (kernel_size < 0) {
-        kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
-    }
-    if (kernel_size < 0) {
-        fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
-        return -1;
+        /* default boot target is the bios */
+        ipl->start_addr = ipl->bios_start_addr;
     }
-    /*
-     * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
-     * kernel parameters here as well. Note: For old kernels (up to 3.2)
-     * we can not rely on the ELF entry point - it was 0x800 (the SALIPL
-     * loader) and it won't work. For this case we force it to 0x10000, too.
-     */
-    if (pentry == KERN_IMAGE_START || pentry == 0x800) {
-        ipl->start_addr = KERN_IMAGE_START;
-        /* Overwrite parameters in the kernel image, which are "rom" */
-        strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
-    } else {
-        ipl->start_addr = pentry;
-    }
-
-    if (ipl->initrd) {
-        ram_addr_t initrd_offset;
-        int initrd_size;
 
-        initrd_offset = INITRD_START;
-        while (kernel_size + 0x100000 > initrd_offset) {
-            initrd_offset += 0x100000;
+    if (ipl->kernel) {
+        kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
+                               NULL, 1, ELF_MACHINE, 0);
+        if (kernel_size < 0) {
+            kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
+        }
+        if (kernel_size < 0) {
+            fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
+            return -1;
         }
-        initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
-                                          ram_size - initrd_offset);
-        if (initrd_size == -1) {
-            fprintf(stderr, "qemu: could not load initrd '%s'\n", ipl->initrd);
-            exit(1);
+        /*
+         * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
+         * kernel parameters here as well. Note: For old kernels (up to 3.2)
+         * we can not rely on the ELF entry point - it was 0x800 (the SALIPL
+         * loader) and it won't work. For this case we force it to 0x10000, too.
+         */
+        if (pentry == KERN_IMAGE_START || pentry == 0x800) {
+            ipl->start_addr = KERN_IMAGE_START;
+            /* Overwrite parameters in the kernel image, which are "rom" */
+            strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
+        } else {
+            ipl->start_addr = pentry;
         }
 
-        /* we have to overwrite values in the kernel image, which are "rom" */
-        stq_p(rom_ptr(INITRD_PARM_START), initrd_offset);
-        stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
-    }
+        if (ipl->initrd) {
+            ram_addr_t initrd_offset;
+            int initrd_size;
+
+            initrd_offset = INITRD_START;
+            while (kernel_size + 0x100000 > initrd_offset) {
+                initrd_offset += 0x100000;
+            }
+            initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
+                                              ram_size - initrd_offset);
+            if (initrd_size == -1) {
+                fprintf(stderr, "qemu: could not load initrd '%s'\n",
+                        ipl->initrd);
+                exit(1);
+            }
 
+            /*
+             * we have to overwrite values in the kernel image,
+             * which are "rom"
+             */
+            stq_p(rom_ptr(INITRD_PARM_START), initrd_offset);
+            stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
+        }
+    }
     return 0;
 }
 
@@ -148,9 +194,82 @@ static Property s390_ipl_properties[] = {
     DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
     DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
     DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
+    DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
+/*
+ * In addition to updating the iplstate, this function returns:
+ * - 0 if system was ipled with external kernel
+ * - -1 if no valid boot device was found
+ * - ccw id of the boot device otherwise
+ */
+static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
+{
+    DeviceState *dev_st;
+
+    if (ipl->iplb_valid) {
+        ipl->cssid = 0;
+        ipl->ssid = 0;
+        ipl->devno = ipl->iplb.devno;
+        goto out;
+    }
+
+    if (ipl->kernel) {
+        return 0;
+    }
+
+    dev_st = get_boot_device(0);
+    if (dev_st) {
+        VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast(
+            OBJECT(qdev_get_parent_bus(dev_st)->parent),
+                TYPE_VIRTIO_CCW_DEVICE);
+        if (ccw_dev) {
+            ipl->cssid = ccw_dev->sch->cssid;
+            ipl->ssid = ccw_dev->sch->ssid;
+            ipl->devno = ccw_dev->sch->devno;
+            goto out;
+        }
+    }
+
+    return -1;
+out:
+    return ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno;
+}
+
+int s390_ipl_update_diag308(IplParameterBlock *iplb)
+{
+    S390IPLState *ipl;
+
+    ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
+    if (ipl) {
+        ipl->iplb = *iplb;
+        ipl->iplb_valid = true;
+        return 0;
+    }
+    return -1;
+}
+
+IplParameterBlock *s390_ipl_get_iplb(void)
+{
+    S390IPLState *ipl;
+
+    ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
+    if (!ipl || !ipl->iplb_valid) {
+        return NULL;
+    }
+    return &ipl->iplb;
+}
+
+void s390_reipl_request(void)
+{
+    S390IPLState *ipl;
+
+    ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
+    ipl->reipl_requested = true;
+    qemu_system_reset_request();
+}
+
 static void s390_ipl_reset(DeviceState *dev)
 {
     S390IPLState *ipl = S390_IPL(dev);
@@ -160,21 +279,14 @@ static void s390_ipl_reset(DeviceState *dev)
     env->psw.addr = ipl->start_addr;
     env->psw.mask = IPL_PSW_MASK;
 
-    if (!ipl->kernel) {
-        /* Tell firmware, if there is a preferred boot device */
-        env->regs[7] = -1;
-        DeviceState *dev_st = get_boot_device(0);
-        if (dev_st) {
-            VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast(
-                OBJECT(qdev_get_parent_bus(dev_st)->parent),
-                TYPE_VIRTIO_CCW_DEVICE);
+    if (!ipl->reipl_requested) {
+        ipl->iplb_valid = false;
+    }
+    ipl->reipl_requested = false;
 
-            if (ccw_dev) {
-                env->regs[7] = ccw_dev->sch->cssid << 24 |
-                               ccw_dev->sch->ssid << 16 |
-                               ccw_dev->sch->devno;
-            }
-        }
+    if (!ipl->kernel || ipl->iplb_valid) {
+        env->psw.addr = ipl->bios_start_addr;
+        env->regs[7] = s390_update_iplstate(env, ipl);
     }
 
     s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
@@ -188,6 +300,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
     k->init = s390_ipl_init;
     dc->props = s390_ipl_properties;
     dc->reset = s390_ipl_reset;
+    dc->vmsd = &vmstate_ipl;
 }
 
 static const TypeInfo s390_ipl_info = {
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
new file mode 100644
index 0000000000..70497bc65f
--- /dev/null
+++ b/hw/s390x/ipl.h
@@ -0,0 +1,25 @@
+/*
+ * s390 IPL device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Zhang Fan <bjfanzh@cn.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef HW_S390_IPL_H
+#define HW_S390_IPL_H
+
+typedef struct IplParameterBlock {
+      uint8_t  reserved1[110];
+      uint16_t devno;
+      uint8_t  reserved2[88];
+} IplParameterBlock;
+
+int s390_ipl_update_diag308(IplParameterBlock *iplb);
+IplParameterBlock *s390_ipl_get_iplb(void);
+void s390_reipl_request(void);
+
+#endif
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 9e5bc5b899..08d8aa6b4b 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -155,7 +155,9 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
         return 0;
     }
 
-    cpu_physical_memory_read(env->regs[r2], buffer, sizeof(*reqh));
+    if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer, sizeof(*reqh))) {
+        return 0;
+    }
     reqh = (ClpReqHdr *)buffer;
     req_len = lduw_p(&reqh->len);
     if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) {
@@ -163,7 +165,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
         return 0;
     }
 
-    cpu_physical_memory_read(env->regs[r2], buffer, req_len + sizeof(*resh));
+    if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer,
+                               req_len + sizeof(*resh))) {
+        return 0;
+    }
     resh = (ClpRspHdr *)(buffer + req_len);
     res_len = lduw_p(&resh->len);
     if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) {
@@ -175,7 +180,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
         return 0;
     }
 
-    cpu_physical_memory_read(env->regs[r2], buffer, req_len + res_len);
+    if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer,
+                               req_len + res_len)) {
+        return 0;
+    }
 
     if (req_len != 32) {
         stw_p(&resh->rsp, CLP_RC_LEN);
@@ -269,7 +277,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
     }
 
 out:
-    cpu_physical_memory_write(env->regs[r2], buffer, req_len + res_len);
+    if (s390_cpu_virt_mem_write(cpu, env->regs[r2], buffer,
+                                req_len + res_len)) {
+        return 0;
+    }
     setcc(cpu, cc);
     return 0;
 }
@@ -539,10 +550,10 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
     S390PCIBusDevice *pbdev;
     MemoryRegion *mr;
     int i;
-    uint64_t val;
     uint32_t fh;
     uint8_t pcias;
     uint8_t len;
+    uint8_t buffer[128];
 
     if (env->psw.mask & PSW_MASK_PSTATE) {
         program_interrupt(env, PGM_PRIVILEGED, 6);
@@ -590,9 +601,12 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
         return 0;
     }
 
+    if (s390_cpu_virt_mem_read(cpu, gaddr, buffer, len)) {
+        return 0;
+    }
+
     for (i = 0; i < len / 8; i++) {
-        val = ldq_phys(&address_space_memory, gaddr + i * 8);
-        io_mem_write(mr, env->regs[r3] + i * 8, val, 8);
+        io_mem_write(mr, env->regs[r3] + i * 8, ldq_p(buffer + i * 8), 8);
     }
 
     setcc(cpu, ZPCI_PCI_LS_OK);
@@ -709,7 +723,9 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
         return 0;
     }
 
-    cpu_physical_memory_read(fiba, (uint8_t *)&fib, sizeof(fib));
+    if (s390_cpu_virt_mem_read(cpu, fiba, (uint8_t *)&fib, sizeof(fib))) {
+        return 0;
+    }
 
     switch (oc) {
     case ZPCI_MOD_FC_REG_INT:
@@ -809,7 +825,10 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
         fib.fc |= 0x10;
     }
 
-    cpu_physical_memory_write(fiba, (uint8_t *)&fib, sizeof(fib));
+    if (s390_cpu_virt_mem_write(cpu, fiba, (uint8_t *)&fib, sizeof(fib))) {
+        return 0;
+    }
+
     setcc(cpu, cc);
     return 0;
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 71bafe06ee..8f0ae59b5f 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -126,7 +126,7 @@ static void ccw_init(MachineState *machine)
     css_bus = virtual_css_bus_init();
     s390_sclp_init();
     s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
-                      machine->initrd_filename, "s390-ccw.img");
+                      machine->initrd_filename, "s390-ccw.img", true);
     s390_flic_init();
 
     dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE);
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index c215cd80e3..412e49ba33 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -128,7 +128,8 @@ static void s390_virtio_register_hcalls(void)
 void s390_init_ipl_dev(const char *kernel_filename,
                        const char *kernel_cmdline,
                        const char *initrd_filename,
-                       const char *firmware)
+                       const char *firmware,
+                       bool enforce_bios)
 {
     DeviceState *dev;
 
@@ -141,6 +142,9 @@ void s390_init_ipl_dev(const char *kernel_filename,
     }
     qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
     qdev_prop_set_string(dev, "firmware", firmware);
+    qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
+    object_property_add_child(qdev_get_machine(), "s390-ipl",
+                              OBJECT(dev), NULL);
     qdev_init_nofail(dev);
 }
 
@@ -221,7 +225,7 @@ static void s390_init(MachineState *machine)
     s390_bus = s390_virtio_bus_init(&my_ram_size);
     s390_sclp_init();
     s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
-                      machine->initrd_filename, ZIPL_FILENAME);
+                      machine->initrd_filename, ZIPL_FILENAME, false);
     s390_flic_init();
 
     /* register hypercalls */
diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h
index 33847aefcf..75b67eda20 100644
--- a/hw/s390x/s390-virtio.h
+++ b/hw/s390x/s390-virtio.h
@@ -26,7 +26,8 @@ void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys);
 void s390_init_ipl_dev(const char *kernel_filename,
                        const char *kernel_cmdline,
                        const char *initrd_filename,
-                       const char *firmware);
+                       const char *firmware,
+                       bool enforce_bios);
 void s390_create_virtio_net(BusState *bus, const char *name);
 void s390_nmi(NMIState *n, int cpu_index, Error **errp);
 #endif