diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5561d807dc..5df450e00b 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -588,6 +588,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) { SCSW *s = &sch->curr_status.scsw; PMCW *p = &sch->curr_status.pmcw; + uint16_t oldflags; int ret; SCHIB schib; @@ -610,6 +611,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) copy_schib_from_guest(&schib, orig_schib); /* Only update the program-modifiable fields. */ p->intparm = schib.pmcw.intparm; + oldflags = p->flags; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP); @@ -625,6 +627,12 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); sch->curr_status.mba = schib.mba; + /* Has the channel been disabled? */ + if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0 + && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) { + sch->disable_cb(sch); + } + ret = 0; out: @@ -1464,6 +1472,21 @@ int subch_device_load(SubchDev *s, QEMUFile *f) } s->ccw_fmt_1 = qemu_get_byte(f); s->ccw_no_data_cnt = qemu_get_byte(f); + /* + * Hack alert. We don't migrate the channel subsystem status (no + * device!), but we need to find out if the guest enabled mss/mcss-e. + * If the subchannel is enabled, it certainly was able to access it, + * so adjust the max_ssid/max_cssid values for relevant ssid/cssid + * values. This is not watertight, but better than nothing. + */ + if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { + if (s->ssid) { + channel_subsys->max_ssid = MAX_SSID; + } + if (s->cssid != channel_subsys->default_cssid) { + channel_subsys->max_cssid = MAX_CSSID; + } + } return 0; } @@ -1483,6 +1506,10 @@ void css_reset_sch(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; + if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { + sch->disable_cb(sch); + } + p->intparm = 0; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 7e53148700..a09bb1f87c 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -81,6 +81,7 @@ struct SubchDev { uint8_t ccw_no_data_cnt; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); + void (*disable_cb)(SubchDev *); SenseId id; void *driver_data; }; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 132004ae4f..2e0a8b6e0c 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -218,7 +218,7 @@ static Property s390_ipl_properties[] = { * - -1 if no valid boot device was found * - ccw id of the boot device otherwise */ -static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) +static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) { DeviceState *dev_st; @@ -248,7 +248,7 @@ static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) return -1; out: - return ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno; + return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); } int s390_ipl_update_diag308(IplParameterBlock *iplb) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index c574988c36..3d20d6a0f7 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -204,9 +204,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - mc->name = "s390-ccw-virtio"; - mc->alias = "s390-ccw"; - mc->desc = "VirtIO-ccw based S390 machine"; mc->init = ccw_init; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; @@ -216,7 +213,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_sdcard = 1; mc->use_sclp = 1; mc->max_cpus = 255; - mc->is_default = 1; nc->nmi_monitor_handler = s390_nmi; } @@ -272,6 +268,7 @@ static inline void s390_machine_initfn(Object *obj) static const TypeInfo ccw_machine_info = { .name = TYPE_S390_CCW_MACHINE, .parent = TYPE_MACHINE, + .abstract = true, .instance_size = sizeof(S390CcwMachineState), .instance_init = s390_machine_initfn, .class_init = ccw_machine_class_init, @@ -281,9 +278,26 @@ static const TypeInfo ccw_machine_info = { }, }; +static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "s390-ccw-virtio-2.4"; + mc->alias = "s390-ccw-virtio"; + mc->desc = "VirtIO-ccw based S390 machine v2.4"; + mc->is_default = 1; +} + +static const TypeInfo ccw_machine_2_4_info = { + .name = TYPE_S390_CCW_MACHINE "2.4", + .parent = TYPE_S390_CCW_MACHINE, + .class_init = ccw_machine_2_4_class_init, +}; + static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); + type_register_static(&ccw_machine_2_4_info); } type_init(ccw_machine_register_types) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 9605bf40b9..d631337e11 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -21,6 +21,7 @@ #include "hw/sysbus.h" #include "qemu/bitops.h" #include "qemu/error-report.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-bus.h" #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" @@ -237,11 +238,20 @@ VirtualCssBus *virtual_css_bus_init(void) } /* Communication blocks used by several channel commands. */ -typedef struct VqInfoBlock { +typedef struct VqInfoBlockLegacy { uint64_t queue; uint32_t align; uint16_t index; uint16_t num; +} QEMU_PACKED VqInfoBlockLegacy; + +typedef struct VqInfoBlock { + uint64_t desc; + uint32_t res0; + uint16_t index; + uint16_t num; + uint64_t avail; + uint64_t used; } QEMU_PACKED VqInfoBlock; typedef struct VqConfigBlock { @@ -261,18 +271,27 @@ typedef struct VirtioThinintInfo { uint8_t isc; } QEMU_PACKED VirtioThinintInfo; +typedef struct VirtioRevInfo { + uint16_t revision; + uint16_t length; + uint8_t data[0]; +} QEMU_PACKED VirtioRevInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ -static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, - uint16_t index, uint16_t num) +static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, + VqInfoBlockLegacy *linfo) { VirtIODevice *vdev = virtio_ccw_get_vdev(sch); + uint16_t index = info ? info->index : linfo->index; + uint16_t num = info ? info->num : linfo->num; + uint64_t desc = info ? info->desc : linfo->queue; if (index >= VIRTIO_CCW_QUEUE_MAX) { return -EINVAL; } /* Current code in virtio.c relies on 4K alignment. */ - if (addr && (align != 4096)) { + if (linfo && desc && (linfo->align != 4096)) { return -EINVAL; } @@ -280,8 +299,12 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return -EINVAL; } - virtio_queue_set_addr(vdev, index, addr); - if (!addr) { + if (info) { + virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); + } else { + virtio_queue_set_addr(vdev, index, desc); + } + if (!desc) { virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); } else { /* Fail if we don't have a big enough queue. */ @@ -296,10 +319,98 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return 0; } -static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) +{ + virtio_ccw_stop_ioeventfd(dev); + virtio_reset(vdev); + if (dev->indicators) { + release_indicator(&dev->routes.adapter, dev->indicators); + dev->indicators = NULL; + } + if (dev->indicators2) { + release_indicator(&dev->routes.adapter, dev->indicators2); + dev->indicators2 = NULL; + } + if (dev->summary_indicator) { + release_indicator(&dev->routes.adapter, dev->summary_indicator); + dev->summary_indicator = NULL; + } + dev->sch->thinint_active = false; +} + +static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, + bool is_legacy) { int ret; VqInfoBlock info; + VqInfoBlockLegacy linfo; + size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); + + if (check_len) { + if (ccw.count != info_len) { + return -EINVAL; + } + } else if (ccw.count < info_len) { + /* Can't execute command. */ + return -EINVAL; + } + if (!ccw.cda) { + return -EFAULT; + } + if (is_legacy) { + linfo.queue = address_space_ldq_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + linfo.align = address_space_ldl_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue), + MEMTXATTRS_UNSPECIFIED, + NULL); + linfo.index = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align), + MEMTXATTRS_UNSPECIFIED, + NULL); + linfo.num = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align) + + sizeof(linfo.index), + MEMTXATTRS_UNSPECIFIED, + NULL); + ret = virtio_ccw_set_vqs(sch, NULL, &linfo); + } else { + info.desc = address_space_ldq_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + info.index = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0), + MEMTXATTRS_UNSPECIFIED, NULL); + info.num = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index), + MEMTXATTRS_UNSPECIFIED, NULL); + info.avail = address_space_ldq_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num), + MEMTXATTRS_UNSPECIFIED, NULL); + info.used = address_space_ldq_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num) + + sizeof(info.avail), + MEMTXATTRS_UNSPECIFIED, NULL); + ret = virtio_ccw_set_vqs(sch, &info, NULL); + } + sch->curr_status.scsw.count = 0; + return ret; +} + +static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +{ + int ret; + VirtioRevInfo revinfo; uint8_t status; VirtioFeatDesc features; void *config; @@ -323,44 +434,10 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) /* Look at the command. */ switch (ccw.cmd_code) { case CCW_CMD_SET_VQ: - if (check_len) { - if (ccw.count != sizeof(info)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(info)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - info.queue = address_space_ldq(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - info.align = address_space_ldl(&address_space_memory, - ccw.cda + sizeof(info.queue), - MEMTXATTRS_UNSPECIFIED, - NULL); - info.index = address_space_lduw(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align), - MEMTXATTRS_UNSPECIFIED, - NULL); - info.num = address_space_lduw(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align) - + sizeof(info.index), - MEMTXATTRS_UNSPECIFIED, - NULL); - ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, - info.num); - sch->curr_status.scsw.count = 0; - } + ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -383,7 +460,16 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) MEMTXATTRS_UNSPECIFIED, NULL); if (features.index == 0) { - features.features = vdev->host_features; + features.features = (uint32_t)vdev->host_features; + } else if (features.index == 1) { + features.features = (uint32_t)(vdev->host_features >> 32); + /* + * Don't offer version 1 to the guest if it did not + * negotiate at least revision 1. + */ + if (dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } } else { /* Return zeroes if the guest supports more feature bits. */ features.features = 0; @@ -419,7 +505,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) MEMTXATTRS_UNSPECIFIED, NULL); if (features.index == 0) { - virtio_set_features(vdev, features.features); + virtio_set_features(vdev, + (vdev->guest_features & 0xffffffff00000000ULL) | + features.features); + } else if (features.index == 1) { + /* + * The guest should not set version 1 if it didn't + * negotiate a revision >= 1. + */ + if (dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } + virtio_set_features(vdev, + (vdev->guest_features & 0x00000000ffffffffULL) | + ((uint64_t)features.features << 32)); } else { /* * If the guest supports more feature bits, assert that it @@ -500,7 +599,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } if (virtio_set_status(vdev, status) == 0) { if (vdev->status == 0) { - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -640,6 +739,40 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } } break; + case CCW_CMD_SET_VIRTIO_REV: + len = sizeof(revinfo); + if (ccw.count < len) { + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + break; + } + revinfo.revision = + address_space_lduw_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + revinfo.length = + address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(revinfo.revision), + MEMTXATTRS_UNSPECIFIED, NULL); + if (ccw.count < len + revinfo.length || + (check_len && ccw.count > len + revinfo.length)) { + ret = -EINVAL; + break; + } + /* + * Once we start to support revisions with additional data, we'll + * need to fetch it here. Nothing to do for now, though. + */ + if (dev->revision >= 0 || + revinfo.revision > virtio_ccw_rev_max(vdev)) { + ret = -ENOSYS; + break; + } + ret = 0; + dev->revision = revinfo.revision; + break; default: ret = -ENOSYS; break; @@ -647,6 +780,13 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) return ret; } +static void virtio_sch_disable_cb(SubchDev *sch) +{ + VirtioCcwDevice *dev = sch->driver_data; + + dev->revision = -1; +} + static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { unsigned int cssid = 0; @@ -766,12 +906,15 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); sch->ccw_cb = virtio_ccw_cb; + sch->disable_cb = virtio_sch_disable_cb; /* Build senseid data. */ memset(&sch->id, 0, sizeof(SenseId)); sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; + dev->revision = -1; + if (k->realize) { k->realize(dev, &err); } @@ -1081,21 +1224,8 @@ static void virtio_ccw_reset(DeviceState *d) VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); css_reset_sch(dev->sch); - if (dev->indicators) { - release_indicator(&dev->routes.adapter, dev->indicators); - dev->indicators = NULL; - } - if (dev->indicators2) { - release_indicator(&dev->routes.adapter, dev->indicators2); - dev->indicators2 = NULL; - } - if (dev->summary_indicator) { - release_indicator(&dev->routes.adapter, dev->summary_indicator); - dev->summary_indicator = NULL; - } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1342,6 +1472,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) qemu_put_be16(f, vdev->config_vector); qemu_put_be64(f, dev->routes.adapter.ind_offset); qemu_put_byte(f, dev->thinint_isc); + qemu_put_be32(f, dev->revision); } static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) @@ -1382,6 +1513,7 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) dev->thinint_isc, true, false, &dev->routes.adapter.adapter_id); } + dev->revision = qemu_get_be32(f); return 0; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index d729263960..692ddd7318 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -41,6 +41,7 @@ #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 #define CCW_CMD_SET_IND_ADAPTER 0x73 +#define CCW_CMD_SET_VIRTIO_REV 0x83 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -86,6 +87,7 @@ struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; char *bus_id; + int revision; VirtioBusState bus; bool ioeventfd_started; bool ioeventfd_disabled; @@ -99,6 +101,12 @@ struct VirtioCcwDevice { uint64_t ind_bit; }; +/* The maximum virtio revision we support. */ +static inline int virtio_ccw_rev_max(VirtIODevice *vdev) +{ + return 0; +} + /* virtual css bus type */ typedef struct VirtualCssBus { BusState parent_obj; diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index 3c6b01fc8e..f64380a972 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 9b3868bd6e..5484c2a45c 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -47,6 +47,7 @@ typedef unsigned long long __u64; /* start.s */ void disabled_wait(void); +void consume_sclp_int(void); /* main.c */ void virtio_panic(const char *string); diff --git a/pc-bios/s390-ccw/sclp-ascii.c b/pc-bios/s390-ccw/sclp-ascii.c index 761fb44ff5..dc1c3e4f4d 100644 --- a/pc-bios/s390-ccw/sclp-ascii.c +++ b/pc-bios/s390-ccw/sclp-ascii.c @@ -24,6 +24,7 @@ static int sclp_service_call(unsigned int command, void *sccb) " srl %0,28" : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) : "cc", "memory"); + consume_sclp_int(); if (cc == 3) return -EIO; if (cc == 2) diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index 5d5df0d616..b6dd8c2fbe 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -28,6 +28,38 @@ disabled_wait: larl %r1,disabled_wait_psw lpswe 0(%r1) + +/* + * void consume_sclp_int(void) + * + * eats one sclp interrupt + */ + .globl consume_sclp_int +consume_sclp_int: + /* enable service interrupts in cr0 */ + stctg 0,0,0(15) + oi 6(15), 0x2 + lctlg 0,0,0(15) + /* prepare external call handler */ + larl %r1, external_new_code + stg %r1, 0x1b8 + larl %r1, external_new_mask + mvc 0x1b0(8),0(%r1) + /* load enabled wait PSW */ + larl %r1, enabled_wait_psw + lpswe 0(%r1) + +external_new_code: + /* disable service interrupts in cr0 */ + stctg 0,0,0(15) + ni 6(15), 0xfd + lctlg 0,0,0(15) + br 14 + .align 8 disabled_wait_psw: .quad 0x0002000180000000,0x0000000000000000 +enabled_wait_psw: + .quad 0x0302000180000000,0x0000000000000000 +external_new_mask: + .quad 0x0000000180000000 diff --git a/target-s390x/gdbstub.c b/target-s390x/gdbstub.c index ddc14a6cd4..31f204964f 100644 --- a/target-s390x/gdbstub.c +++ b/target-s390x/gdbstub.c @@ -92,6 +92,7 @@ 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); + cpu_synchronize_post_init(ENV_GET_CPU(env)); return 4; default: return 0;