From b39b7718dc23e9890ea7e770f543e6f2d17cb31b Mon Sep 17 00:00:00 2001 From: "Eugene (jno) Dvurechenski" Date: Wed, 1 Jun 2016 15:25:51 +0200 Subject: [PATCH 01/25] pc-bios/s390-ccw: Pass selected SCSI device to IPL There is ,bootindex=%d argument to specify the lookup order of boot devices. If a bootindex assigned to the device, then IPL Parameter Info Block is created for that device when it is IPLed from. If it is a mere SCSI device (not FCP), then IPIB is created with a special SCSI type and its fields are used to store SCSI address of the device. This new ipl block is private to qemu for now. If the device to IPL from is specified this way, then SCSI bus lookup is bypassed and prescribed devices uses the address specified. Signed-off-by: Eugene (jno) Dvurechenski Signed-off-by: Alexander Yarygin Signed-off-by: Cornelia Huck --- pc-bios/s390-ccw/iplb.h | 12 ++++++++++++ pc-bios/s390-ccw/main.c | 12 ++++++++++++ pc-bios/s390-ccw/virtio-scsi.c | 11 +++++++++++ pc-bios/s390-ccw/virtio.h | 2 ++ 4 files changed, 37 insertions(+) diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 1cf509f49746..86abc56a90e8 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -43,6 +43,16 @@ struct IplBlockFcp { } __attribute__ ((packed)); typedef struct IplBlockFcp IplBlockFcp; +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} __attribute__ ((packed)); +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + struct IplParameterBlock { uint32_t len; uint8_t reserved0[3]; @@ -55,6 +65,7 @@ struct IplParameterBlock { union { IplBlockCcw ccw; IplBlockFcp fcp; + IplBlockQemuScsi scsi; }; } __attribute__ ((packed)); typedef struct IplParameterBlock IplParameterBlock; @@ -63,6 +74,7 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 +#define S390_IPL_TYPE_QEMU_SCSI 0xff static inline bool store_iplb(IplParameterBlock *iplb) { diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 9446ecc235a6..345b84875278 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -84,6 +84,18 @@ static void virtio_setup(void) debug_print_int("ssid ", blk_schid.ssid); found = find_dev(&schib, dev_no); break; + case S390_IPL_TYPE_QEMU_SCSI: + { + VDev *vdev = virtio_get_device(); + + vdev->scsi_device_selected = true; + vdev->selected_scsi_device.channel = iplb.scsi.channel; + vdev->selected_scsi_device.target = iplb.scsi.target; + vdev->selected_scsi_device.lun = iplb.scsi.lun; + blk_schid.ssid = iplb.scsi.ssid & 0x3; + found = find_dev(&schib, iplb.scsi.devno); + break; + } default: panic("List-directed IPL not supported yet!\n"); } diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index 3bb48e917ea5..d850a8deed5b 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -204,6 +204,17 @@ static void virtio_scsi_locate_device(VDev *vdev) debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); + if (vdev->scsi_device_selected) { + sdev->channel = vdev->selected_scsi_device.channel; + sdev->target = vdev->selected_scsi_device.target; + sdev->lun = vdev->selected_scsi_device.lun; + + IPL_check(sdev->channel == 0, "non-zero channel requested"); + IPL_check(sdev->target <= vdev->config.scsi.max_target, "target# high"); + IPL_check(sdev->lun <= vdev->config.scsi.max_lun, "LUN# high"); + return; + } + for (target = 0; target <= vdev->config.scsi.max_target; target++) { sdev->channel = channel; sdev->target = target; /* sdev->lun will be 0 here */ diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 3c6e91510ef6..eb35ea5faf64 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -274,6 +274,8 @@ struct VDev { uint64_t scsi_last_block; uint32_t scsi_dev_cyls; uint8_t scsi_dev_heads; + bool scsi_device_selected; + ScsiDevice selected_scsi_device; }; typedef struct VDev VDev; From 07c6f329bdf37595777ddd334de9bbd24d4eed7c Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 4 Jul 2016 09:59:15 +0200 Subject: [PATCH 02/25] pc-bios/s390-ccw.img: rebuild image Contains: - pc-bios/s390-ccw: Pass selected SCSI device to IPL Signed-off-by: Cornelia Huck --- pc-bios/s390-ccw.img | Bin 26424 -> 26440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index ea5fd9a4797530e79e88f591f66c4de8e93a604e..089f6ba5e924c17796cccac4ecbfa7a78b00ba22 100644 GIT binary patch delta 7745 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 REf^VXXR#0svS_aL{{VwpXafKM From e468b6730cb36a475fd4e71f57b020ca37ebd8ae Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Thu, 9 Jun 2016 15:54:10 +0300 Subject: [PATCH 03/25] s390x/ipl: Support IPL from selected SCSI device If bootindex is specified for a device, we need to IPL from it. Currently it works for ccw devices, but not for SCSI. To be able to IPL from the specific device, pc-bios needs to know its address. For this reason we add special QEMU_SCSI IPL type into the IPLB structure, that contains the scsi device address. We enhance the ipl block with a currently qemu-only parameter block that allows us to specify a concrete scsi device. Signed-off-by: Alexander Yarygin Reviewed-by: Eric Farman Signed-off-by: Cornelia Huck --- hw/s390x/ipl.c | 18 ++++++++++++++++++ hw/s390x/ipl.h | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index e6bf7cf7c01e..78998cdbad7d 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -217,6 +217,8 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( OBJECT(qdev_get_parent_bus(dev_st)->parent), TYPE_VIRTIO_CCW_DEVICE); + SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st), + TYPE_SCSI_DEVICE); if (ccw_dev) { ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); ipl->iplb.blk0_len = @@ -225,6 +227,22 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; return true; + } else if (sd) { + SCSIBus *bus = scsi_bus_from_device(sd); + VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); + VirtioCcwDevice *ccw = &scsi_ccw->parent_obj; + + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); + ipl->iplb.blk0_len = + cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); + ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; + ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); + ipl->iplb.scsi.target = cpu_to_be16(sd->id); + ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); + ipl->iplb.scsi.devno = cpu_to_be16(ccw->sch->devno); + ipl->iplb.scsi.ssid = ccw->sch->ssid & 3; + return true; } } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 9aa4d942a740..ed3f2c8a4364 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -46,6 +46,16 @@ struct IplBlockFcp { } QEMU_PACKED; typedef struct IplBlockFcp IplBlockFcp; +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} QEMU_PACKED; +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + union IplParameterBlock { struct { uint32_t len; @@ -59,6 +69,7 @@ union IplParameterBlock { union { IplBlockCcw ccw; IplBlockFcp fcp; + IplBlockQemuScsi scsi; }; } QEMU_PACKED; struct { @@ -102,10 +113,12 @@ typedef struct S390IPLState S390IPLState; #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 +#define S390_IPL_TYPE_QEMU_SCSI 0xff #define S390_IPLB_HEADER_LEN 8 #define S390_IPLB_MIN_CCW_LEN 200 #define S390_IPLB_MIN_FCP_LEN 384 +#define S390_IPLB_MIN_QEMU_SCSI_LEN 200 static inline bool iplb_valid_len(IplParameterBlock *iplb) { From bb0995468a39f14077ceaa8ed5afdca849f00c7c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 9 Jun 2016 15:36:41 +0200 Subject: [PATCH 04/25] s390x/ipl: fix reboots for migration from different bios When migrating from a different QEMU version, the start_address and bios_start_address may differ. During migration these values are migrated and overwrite the values that were detected by QEMU itself. On a reboot, QEMU will reload its own BIOS, but use the migrated start addresses, which does not work if the values differ. Fix this by not relying on the migrated values anymore, but still provide them during migration, so existing QEMUs continue to work. Signed-off-by: David Hildenbrand Cc: qemu-stable@nongnu.org Signed-off-by: Cornelia Huck --- hw/s390x/ipl.c | 11 +++++++++-- hw/s390x/ipl.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 78998cdbad7d..a54284c74ac4 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -69,8 +69,8 @@ static const VMStateDescription vmstate_ipl = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT64(start_addr, S390IPLState), - VMSTATE_UINT64(bios_start_addr, S390IPLState), + VMSTATE_UINT64(compat_start_addr, S390IPLState), + VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), VMSTATE_BOOL(iplb_valid, S390IPLState), VMSTATE_UINT8(cssid, S390IPLState), @@ -192,6 +192,13 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); } } + /* + * Don't ever use the migrated values, they could come from a different + * BIOS and therefore don't work. But still migrate the values, so + * QEMUs relying on it don't break. + */ + ipl->compat_start_addr = ipl->start_addr; + ipl->compat_bios_start_addr = ipl->bios_start_addr; qemu_register_reset(qdev_reset_all_fn, dev); error: error_propagate(errp, err); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index ed3f2c8a4364..c89109585acd 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -93,7 +93,9 @@ struct S390IPLState { /*< private >*/ DeviceState parent_obj; uint64_t start_addr; + uint64_t compat_start_addr; uint64_t bios_start_addr; + uint64_t compat_bios_start_addr; bool enforce_bios; IplParameterBlock iplb; bool iplb_valid; From cf2499350aa4f2b6bdd30792cdf0b7f264fbcb36 Mon Sep 17 00:00:00 2001 From: Sascha Silbe Date: Wed, 15 Jun 2016 17:16:05 +0200 Subject: [PATCH 05/25] s390x/css: factor out some generic code from virtio_ccw_device_realize() A lot of what virtio_ccw_device_realize() does isn't specific to virtio; it would apply to emulated CCW as well. Factor it out to make it easier to implement emulated CCW devices later on. Signed-off-by: Sascha Silbe Reviewed-by: Dong Jia Shi Reviewed-by: Halil Pasic Signed-off-by: Cornelia Huck --- hw/s390x/css.c | 143 +++++++++++++++++++++++++++++++++++++++++ hw/s390x/virtio-ccw.c | 122 ++++------------------------------- hw/s390x/virtio-ccw.h | 2 - include/hw/s390x/css.h | 18 ++++++ 4 files changed, 175 insertions(+), 110 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 76668814da50..54991f5d6f80 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1340,6 +1340,116 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid]; } +/** + * Return free device number in subchannel set. + * + * Return index of the first free device number in the subchannel set + * identified by @p cssid and @p ssid, beginning the search at @p + * start and wrapping around at MAX_DEVNO. Return a value exceeding + * MAX_SCHID if there are no free device numbers in the subchannel + * set. + */ +static uint32_t css_find_free_devno(uint8_t cssid, uint8_t ssid, + uint16_t start) +{ + uint32_t round; + + for (round = 0; round <= MAX_DEVNO; round++) { + uint16_t devno = (start + round) % MAX_DEVNO; + + if (!css_devno_used(cssid, ssid, devno)) { + return devno; + } + } + return MAX_DEVNO + 1; +} + +/** + * Return first free subchannel (id) in subchannel set. + * + * Return index of the first free subchannel in the subchannel set + * identified by @p cssid and @p ssid, if there is any. Return a value + * exceeding MAX_SCHID if there are no free subchannels in the + * subchannel set. + */ +static uint32_t css_find_free_subch(uint8_t cssid, uint8_t ssid) +{ + uint32_t schid; + + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + return schid; + } + } + return MAX_SCHID + 1; +} + +/** + * Return first free subchannel (id) in subchannel set for a device number + * + * Verify the device number @p devno is not used yet in the subchannel + * set identified by @p cssid and @p ssid. Set @p schid to the index + * of the first free subchannel in the subchannel set, if there is + * any. Return true if everything succeeded and false otherwise. + */ +static bool css_find_free_subch_for_devno(uint8_t cssid, uint8_t ssid, + uint16_t devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid; + + assert(schid); + if (css_devno_used(cssid, ssid, devno)) { + error_setg(errp, "Device %x.%x.%04x already exists", + cssid, ssid, devno); + return false; + } + free_schid = css_find_free_subch(cssid, ssid); + if (free_schid > MAX_SCHID) { + error_setg(errp, "No free subchannel found for %x.%x.%04x", + cssid, ssid, devno); + return false; + } + *schid = free_schid; + return true; +} + +/** + * Return first free subchannel (id) and device number + * + * Locate the first free subchannel and first free device number in + * any of the subchannel sets of the channel subsystem identified by + * @p cssid. Return false if no free subchannel / device number could + * be found. Otherwise set @p ssid, @p devno and @p schid to identify + * the available subchannel and device number and return true. + * + * May modify @p ssid, @p devno and / or @p schid even if no free + * subchannel / device number could be found. + */ +static bool css_find_free_subch_and_devno(uint8_t cssid, uint8_t *ssid, + uint16_t *devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid, free_devno; + + assert(ssid && devno && schid); + for (*ssid = 0; *ssid <= MAX_SSID; (*ssid)++) { + free_schid = css_find_free_subch(cssid, *ssid); + if (free_schid > MAX_SCHID) { + continue; + } + free_devno = css_find_free_devno(cssid, *ssid, free_schid); + if (free_devno > MAX_DEVNO) { + continue; + } + *schid = free_schid; + *devno = free_devno; + return true; + } + error_setg(errp, "Virtual channel subsystem is full!"); + return false; +} + bool css_subch_visible(SubchDev *sch) { if (sch->ssid > channel_subsys.max_ssid) { @@ -1762,3 +1872,36 @@ PropertyInfo css_devid_propinfo = { .get = get_css_devid, .set = set_css_devid, }; + +SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) +{ + uint16_t schid = 0; + SubchDev *sch; + + if (bus_id.valid) { + /* Enforce use of virtual cssid. */ + if (bus_id.cssid != VIRTUAL_CSSID) { + error_setg(errp, "cssid %hhx not valid for virtual devices", + bus_id.cssid); + return NULL; + } + if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid, + bus_id.devid, &schid, errp)) { + return NULL; + } + } else { + bus_id.cssid = VIRTUAL_CSSID; + if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid, + &bus_id.devid, &schid, errp)) { + return NULL; + } + } + + sch = g_malloc0(sizeof(*sch)); + sch->cssid = bus_id.cssid; + sch->ssid = bus_id.ssid; + sch->devno = bus_id.devid; + sch->schid = schid; + css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch); + return sch; +} diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8b709e362ea6..67d7867c56b9 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -703,116 +703,27 @@ static void virtio_sch_disable_cb(SubchDev *sch) static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { - unsigned int schid; - bool found = false; - SubchDev *sch; - Error *err = NULL; VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + SubchDev *sch = css_create_virtual_sch(dev->bus_id, errp); + Error *err = NULL; - sch = g_malloc0(sizeof(SubchDev)); - - sch->driver_data = dev; - dev->sch = sch; - - dev->indicators = NULL; - - /* Initialize subchannel structure. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - sch->thinint_active = false; - /* - * Use a device number if provided. Otherwise, fall back to subchannel - * number. - */ - if (dev->bus_id.valid) { - /* Enforce use of virtual cssid. */ - if (dev->bus_id.cssid != VIRTUAL_CSSID) { - error_setg(errp, "cssid %x not valid for virtio devices", - dev->bus_id.cssid); - goto out_err; - } - if (css_devno_used(dev->bus_id.cssid, dev->bus_id.ssid, - dev->bus_id.devid)) { - error_setg(errp, "Device %x.%x.%04x already exists", - dev->bus_id.cssid, dev->bus_id.ssid, - dev->bus_id.devid); - goto out_err; - } - sch->cssid = dev->bus_id.cssid; - sch->ssid = dev->bus_id.ssid; - sch->devno = dev->bus_id.devid; - - /* Find the next free id. */ - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, sch->cssid, sch->ssid, schid)) { - sch->schid = schid; - css_subch_assign(sch->cssid, sch->ssid, sch->schid, - sch->devno, sch); - found = true; - break; - } - } - if (!found) { - error_setg(errp, "No free subchannel found for %x.%x.%04x", - sch->cssid, sch->ssid, sch->devno); - goto out_err; - } - trace_virtio_ccw_new_device(sch->cssid, sch->ssid, sch->schid, - sch->devno, "user-configured"); - } else { - unsigned int cssid = VIRTUAL_CSSID, ssid, devno; - - for (ssid = 0; ssid <= MAX_SSID; ssid++) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->cssid = cssid; - sch->ssid = ssid; - sch->schid = schid; - devno = schid; - /* - * If the devno is already taken, look further in this - * subchannel set. - */ - while (css_devno_used(cssid, ssid, devno)) { - if (devno == MAX_SCHID) { - devno = 0; - } else if (devno == schid - 1) { - error_setg(errp, "No free devno found"); - goto out_err; - } else { - devno++; - } - } - sch->devno = devno; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (found) { - break; - } - } - if (!found) { - error_setg(errp, "Virtual channel subsystem is full!"); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "auto-configured"); + if (!sch) { + return; } - /* Build initial schib. */ - css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); - + sch->driver_data = dev; 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->sch = sch; + dev->indicators = NULL; dev->revision = -1; + css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); + + trace_virtio_ccw_new_device( + sch->cssid, sch->ssid, sch->schid, sch->devno, + dev->bus_id.valid ? "user-configured" : "auto-configured"); if (k->realize) { k->realize(dev, &err); @@ -820,14 +731,9 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (err) { error_propagate(errp, err); css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - goto out_err; + dev->sch = NULL; + g_free(sch); } - - return; - -out_err: - dev->sch = NULL; - g_free(sch); } static int virtio_ccw_exit(VirtioCcwDevice *dev) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 0bfb5d93c6ce..7243fb08beda 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -27,8 +27,6 @@ #include #include -#define VIRTUAL_CSSID 0xfe - #define VIRTIO_CCW_CU_TYPE 0x3832 #define VIRTIO_CCW_CHPID_TYPE 0x32 diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 38f4d777794e..1da63e361ddb 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -17,6 +17,7 @@ #include "hw/s390x/ioinst.h" /* Channel subsystem constants. */ +#define MAX_DEVNO 65535 #define MAX_SCHID 65535 #define MAX_SSID 3 #define MAX_CSSID 254 /* 255 is reserved */ @@ -24,6 +25,8 @@ #define MAX_CIWS 62 +#define VIRTUAL_CSSID 0xfe + typedef struct CIW { uint8_t type; uint8_t command; @@ -169,4 +172,19 @@ extern PropertyInfo css_devid_propinfo; #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId) +/** + * Create a subchannel for the given bus id. + * + * If @p bus_id is valid, verify that it uses the virtual channel + * subsystem id and is not already in use, and find a free subchannel + * id for it. If @p bus_id is not valid, find a free subchannel id and + * device number across all subchannel sets. If either of the former + * actions succeed, allocate a subchannel structure, initialise it + * with the bus id, subchannel id and device number, register it with + * the CSS and return it. Otherwise return NULL. + * + * The caller becomes owner of the returned subchannel structure and + * is responsible for unregistering and freeing it. + */ +SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp); #endif From 3f9e4859640c69618446eb95176c31ae6a77c06f Mon Sep 17 00:00:00 2001 From: Sascha Silbe Date: Mon, 4 Jul 2016 15:46:20 +0200 Subject: [PATCH 06/25] s390x/css: use define for "virtual-css-bridge" literal Introduce a TYPE_* define (like we already use for a couple of other QOM types) for the name of the virtual CSS bridge QOM type instead of sprinkling the same string literal over several source files. Signed-off-by: Sascha Silbe Signed-off-by: Cornelia Huck --- hw/s390x/s390-virtio-ccw.c | 2 +- hw/s390x/virtio-ccw.c | 4 ++-- hw/s390x/virtio-ccw.h | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 52f079a88415..3b79e96b09d5 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -29,7 +29,7 @@ #include "hw/s390x/s390-virtio-ccw.h" static const char *const reset_dev_types[] = { - "virtual-css-bridge", + TYPE_VIRTUAL_CSS_BRIDGE, "s390-sclp-event-facility", "s390-flic", "diag288", diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 67d7867c56b9..0afc0d36a6d2 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -130,7 +130,7 @@ VirtualCssBus *virtual_css_bus_init(void) DeviceState *dev; /* Create bridge device */ - dev = qdev_create(NULL, "virtual-css-bridge"); + dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); qdev_init_nofail(dev); /* Create bus on bridge device */ @@ -1626,7 +1626,7 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) } static const TypeInfo virtual_css_bridge_info = { - .name = "virtual-css-bridge", + .name = TYPE_VIRTUAL_CSS_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), .class_init = virtual_css_bridge_class_init, diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 7243fb08beda..61446254167a 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -101,6 +101,9 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } +/* virtual css bridge type */ +#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" + /* virtual css bus type */ typedef struct VirtualCssBus { BusState parent_obj; From dd70bd0d4cf2e10b0437fc75da50423612871101 Mon Sep 17 00:00:00 2001 From: Jing Liu Date: Fri, 6 Nov 2015 12:32:40 +0100 Subject: [PATCH 07/25] s390x/css: Factor out virtual css bridge and bus Currently, common base layers virtual css bridge and bus are defined in hw/s390x/virtio-ccw.c(h). In order to support multiple types of devices in the virtual channel subsystem, especially non virtio-ccw, refactoring work needs to be done. This work is just a pure code move without any functional change except dropping an empty function virtual_css_bridge_init() and virtio_ccw_busdev_unplug() changing. virtio_ccw_busdev_unplug() is specific to virtio-ccw but gets referenced from the common virtual css bridge code. To keep the functional changes to a minimum we export this function from virtio-ccw.c and continue to reference it inside virtual_css_bridge_class_init() (now living in hw/s390x/css-bridge.c). A follow-up patch will clean this up. Signed-off-by: Jing Liu Reviewed-by: Sascha Silbe Reviewed-by: Dong Jia Shi Signed-off-by: Cornelia Huck --- hw/s390x/Makefile.objs | 1 + hw/s390x/css-bridge.c | 89 +++++++++++++++++++++++++++++++++++ hw/s390x/s390-virtio-ccw.c | 1 + hw/s390x/virtio-ccw.c | 79 ++----------------------------- hw/s390x/virtio-ccw.h | 14 +----- include/hw/s390x/css-bridge.h | 31 ++++++++++++ 6 files changed, 127 insertions(+), 88 deletions(-) create mode 100644 hw/s390x/css-bridge.c create mode 100644 include/hw/s390x/css-bridge.h diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 220361782df0..141ce1a710b5 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -8,6 +8,7 @@ obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o +obj-y += css-bridge.o obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c new file mode 100644 index 000000000000..e74cc1cfdabc --- /dev/null +++ b/hw/s390x/css-bridge.c @@ -0,0 +1,89 @@ +/* + * css bridge implementation + * + * Copyright 2012,2016 IBM Corp. + * Author(s): Cornelia Huck + * Pierre Morel + * + * 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. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "virtio-ccw.h" +#include "hw/hotplug.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" + +static void virtual_css_bus_reset(BusState *qbus) +{ + /* This should actually be modelled via the generic css */ + css_reset(); +} + +static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->reset = virtual_css_bus_reset; +} + +static const TypeInfo virtual_css_bus_info = { + .name = TYPE_VIRTUAL_CSS_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(VirtualCssBus), + .class_init = virtual_css_bus_class_init, +}; + +VirtualCssBus *virtual_css_bus_init(void) +{ + VirtualCssBus *cbus; + BusState *bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + cbus = VIRTUAL_CSS_BUS(bus); + + /* Enable hotplugging */ + qbus_set_hotplug_handler(bus, dev, &error_abort); + + return cbus; + } + +/***************** Virtual-css Bus Bridge Device ********************/ + +static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +{ + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + hc->unplug = virtio_ccw_busdev_unplug; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo virtual_css_bridge_info = { + .name = TYPE_VIRTUAL_CSS_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = virtual_css_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void virtual_css_register(void) +{ + type_register_static(&virtual_css_bridge_info); + type_register_static(&virtual_css_bus_info); +} + +type_init(virtual_css_register) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 3b79e96b09d5..caf0a682a700 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -27,6 +27,7 @@ #include "hw/compat.h" #include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/css-bridge.h" static const char *const reset_dev_types[] = { TYPE_VIRTUAL_CSS_BRIDGE, diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 0afc0d36a6d2..8f1a0e824f24 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -33,31 +33,11 @@ #include "hw/s390x/css.h" #include "virtio-ccw.h" #include "trace.h" +#include "hw/s390x/css-bridge.h" static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); -static void virtual_css_bus_reset(BusState *qbus) -{ - /* This should actually be modelled via the generic css */ - css_reset(); -} - - -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->reset = virtual_css_bus_reset; -} - -static const TypeInfo virtual_css_bus_info = { - .name = TYPE_VIRTUAL_CSS_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtualCssBus), - .class_init = virtual_css_bus_class_init, -}; - VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) { VirtIODevice *vdev = NULL; @@ -123,26 +103,6 @@ static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); } -VirtualCssBus *virtual_css_bus_init(void) -{ - VirtualCssBus *cbus; - BusState *bus; - DeviceState *dev; - - /* Create bridge device */ - dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); - qdev_init_nofail(dev); - - /* Create bus on bridge device */ - bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); - cbus = VIRTUAL_CSS_BUS(bus); - - /* Enable hotplugging */ - qbus_set_hotplug_handler(bus, dev, &error_abort); - - return cbus; -} - /* Communication blocks used by several channel commands. */ typedef struct VqInfoBlockLegacy { uint64_t queue; @@ -1565,8 +1525,8 @@ static int virtio_ccw_busdev_exit(DeviceState *dev) return _info->exit(_dev); } -static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; SubchDev *sch = _dev->sch; @@ -1605,37 +1565,6 @@ static const TypeInfo virtio_ccw_device_info = { .abstract = true, }; -/***************** Virtual-css Bus Bridge Device ********************/ -/* Only required to have the virtio bus as child in the system bus */ - -static int virtual_css_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = virtual_css_bridge_init; - hc->unplug = virtio_ccw_busdev_unplug; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo virtual_css_bridge_info = { - .name = TYPE_VIRTUAL_CSS_BRIDGE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - /* virtio-ccw-bus */ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, @@ -1730,7 +1659,6 @@ static const TypeInfo virtio_ccw_9p_info = { static void virtio_ccw_register(void) { type_register_static(&virtio_ccw_bus_info); - type_register_static(&virtual_css_bus_info); type_register_static(&virtio_ccw_device_info); type_register_static(&virtio_ccw_serial); type_register_static(&virtio_ccw_blk); @@ -1741,7 +1669,6 @@ static void virtio_ccw_register(void) type_register_static(&vhost_ccw_scsi); #endif type_register_static(&virtio_ccw_rng); - type_register_static(&virtual_css_bridge_info); #ifdef CONFIG_VIRTFS type_register_static(&virtio_ccw_9p_info); #endif diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 61446254167a..11023aabb778 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -101,17 +101,8 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } -/* virtual css bridge type */ -#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" - -/* virtual css bus type */ -typedef struct VirtualCssBus { - BusState parent_obj; -} VirtualCssBus; - -#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" -#define VIRTUAL_CSS_BUS(obj) \ - OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) +void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); /* virtio-scsi-ccw */ @@ -192,7 +183,6 @@ typedef struct VirtIORNGCcw { VirtIORNG vdev; } VirtIORNGCcw; -VirtualCssBus *virtual_css_bus_init(void); void virtio_ccw_device_update_status(SubchDev *sch); VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); diff --git a/include/hw/s390x/css-bridge.h b/include/hw/s390x/css-bridge.h new file mode 100644 index 000000000000..ad73c1faf6dd --- /dev/null +++ b/include/hw/s390x/css-bridge.h @@ -0,0 +1,31 @@ +/* + * virtual css bridge definition + * + * Copyright 2012,2016 IBM Corp. + * Author(s): Cornelia Huck + * Pierre Morel + * + * 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_S390X_CSS_BRIDGE_H +#define HW_S390X_CSS_BRIDGE_H +#include "qom/object.h" +#include "hw/qdev-core.h" + +/* virtual css bridge */ +#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" + +/* virtual css bus type */ +typedef struct VirtualCssBus { + BusState parent_obj; +} VirtualCssBus; + +#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" +#define VIRTUAL_CSS_BUS(obj) \ + OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) +VirtualCssBus *virtual_css_bus_init(void); + +#endif From b804e8a62a25b82637b19aea68ab7d5a7a1ce0fb Mon Sep 17 00:00:00 2001 From: Jing Liu Date: Fri, 26 Feb 2016 06:46:12 +0100 Subject: [PATCH 08/25] s390x/css: Unplug handler of virtual css bridge The previous patch moved virtual css bridge and bus out from virtio-ccw, but kept the direct reference of virtio-ccw specific unplug function inside css-bridge.c. To make the virtual css bus and bridge useful for non-virtio devices, this introduces a common unplug function pointer "unplug" to call specific virtio-ccw unplug parts. Thus, the tight coupling to virtio-ccw can be removed. This unplug pointer is a member of CCWDeviceClass, which is introduced as an abstract device layer called "ccw-device". This layer is between DeviceState and specific devices which are plugged in virtual css bus, like virtio-ccw device. The specific unplug handlers should be assigned to "unplug" during initialization. Signed-off-by: Jing Liu Reviewed-by: Sascha Silbe Reviewed-by: Dong Jia Shi Reviewed-by: Yi Min Zhao Signed-off-by: Cornelia Huck --- hw/s390x/Makefile.objs | 1 + hw/s390x/ccw-device.c | 27 +++++++++++++ hw/s390x/ccw-device.h | 43 +++++++++++++++++++++ hw/s390x/css-bridge.c | 39 ++++++++++++++++++- hw/s390x/ipl.c | 14 ++++--- hw/s390x/virtio-ccw.c | 86 +++++++++++++++++++++--------------------- hw/s390x/virtio-ccw.h | 11 ++---- 7 files changed, 164 insertions(+), 57 deletions(-) create mode 100644 hw/s390x/ccw-device.c create mode 100644 hw/s390x/ccw-device.h diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 141ce1a710b5..41ac4ec32544 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -9,6 +9,7 @@ obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o obj-y += css-bridge.o +obj-y += ccw-device.o obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c new file mode 100644 index 000000000000..28ea20440e54 --- /dev/null +++ b/hw/s390x/ccw-device.c @@ -0,0 +1,27 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu + * + * 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. + */ +#include "qemu/osdep.h" +#include "ccw-device.h" + +static const TypeInfo ccw_device_info = { + .name = TYPE_CCW_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CcwDevice), + .class_size = sizeof(CCWDeviceClass), + .abstract = true, +}; + +static void ccw_device_register(void) +{ + type_register_static(&ccw_device_info); +} + +type_init(ccw_device_register) diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h new file mode 100644 index 000000000000..59ba01b6c549 --- /dev/null +++ b/hw/s390x/ccw-device.h @@ -0,0 +1,43 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu + * + * 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_S390X_CCW_DEVICE_H +#define HW_S390X_CCW_DEVICE_H +#include "qom/object.h" +#include "hw/qdev-core.h" +#include "hw/s390x/css.h" + +typedef struct CcwDevice { + DeviceState parent_obj; + SubchDev *sch; + /* .. */ + CssDevId bus_id; +} CcwDevice; + +typedef struct CCWDeviceClass { + DeviceClass parent_class; + void (*unplug)(HotplugHandler *, DeviceState *, Error **); +} CCWDeviceClass; + +static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) +{ + return container_of(d, CcwDevice, parent_obj); +} + +#define TYPE_CCW_DEVICE "ccw-device" + +#define CCW_DEVICE(obj) OBJECT_CHECK(CcwDevice, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(CCWDeviceClass, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(CCWDeviceClass, (klass), TYPE_CCW_DEVICE) + +#endif diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index e74cc1cfdabc..e4c24e21f376 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -11,13 +11,48 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "virtio-ccw.h" #include "hw/hotplug.h" #include "hw/sysbus.h" #include "qemu/bitops.h" #include "hw/s390x/css.h" +#include "ccw-device.h" #include "hw/s390x/css-bridge.h" +/* + * Invoke device-specific unplug handler, disable the subchannel + * (including sending a channel report to the guest) and remove the + * device from the virtual css bus. + */ +static void ccw_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CcwDevice *ccw_dev = CCW_DEVICE(dev); + CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev); + SubchDev *sch = ccw_dev->sch; + Error *err = NULL; + + if (k->unplug) { + k->unplug(hotplug_dev, dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + /* + * We should arrive here only for device_del, since we don't support + * direct hot(un)plug of channels. + */ + assert(sch != NULL); + /* Subchannel is now disabled and no longer valid. */ + sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_DNV); + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); + + object_unparent(OBJECT(dev)); +} + static void virtual_css_bus_reset(BusState *qbus) { /* This should actually be modelled via the generic css */ @@ -65,7 +100,7 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - hc->unplug = virtio_ccw_busdev_unplug; + hc->unplug = ccw_device_unplug; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index a54284c74ac4..2e2664f22ea1 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -221,12 +221,14 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) dev_st = get_boot_device(0); if (dev_st) { - VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( - OBJECT(qdev_get_parent_bus(dev_st)->parent), + VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) + object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), TYPE_VIRTIO_CCW_DEVICE); SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st), TYPE_SCSI_DEVICE); - if (ccw_dev) { + if (virtio_ccw_dev) { + CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev); + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); ipl->iplb.blk0_len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); @@ -238,7 +240,7 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) SCSIBus *bus = scsi_bus_from_device(sd); VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); - VirtioCcwDevice *ccw = &scsi_ccw->parent_obj; + CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw); ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); ipl->iplb.blk0_len = @@ -247,8 +249,8 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); ipl->iplb.scsi.target = cpu_to_be16(sd->id); ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); - ipl->iplb.scsi.devno = cpu_to_be16(ccw->sch->devno); - ipl->iplb.scsi.ssid = ccw->sch->ssid & 3; + ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); + ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; return true; } } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8f1a0e824f24..a554a24d0669 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -97,7 +97,8 @@ static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, int n, bool assign) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); @@ -194,6 +195,8 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) { + CcwDevice *ccw_dev = CCW_DEVICE(dev); + virtio_ccw_stop_ioeventfd(dev); virtio_reset(vdev); if (dev->indicators) { @@ -208,7 +211,7 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) release_indicator(&dev->routes.adapter, dev->summary_indicator); dev->summary_indicator = NULL; } - dev->sch->thinint_active = false; + ccw_dev->sch->thinint_active = false; } static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, @@ -664,7 +667,8 @@ static void virtio_sch_disable_cb(SubchDev *sch) static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - SubchDev *sch = css_create_virtual_sch(dev->bus_id, errp); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp); Error *err = NULL; if (!sch) { @@ -676,14 +680,14 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) sch->disable_cb = virtio_sch_disable_cb; sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; - dev->sch = sch; + ccw_dev->sch = sch; dev->indicators = NULL; dev->revision = -1; css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); trace_virtio_ccw_new_device( sch->cssid, sch->ssid, sch->schid, sch->devno, - dev->bus_id.valid ? "user-configured" : "auto-configured"); + ccw_dev->bus_id.valid ? "user-configured" : "auto-configured"); if (k->realize) { k->realize(dev, &err); @@ -691,14 +695,15 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (err) { error_propagate(errp, err); css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - dev->sch = NULL; + ccw_dev->sch = NULL; g_free(sch); } } static int virtio_ccw_exit(VirtioCcwDevice *dev) { - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); @@ -879,7 +884,9 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) */ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) { - return container_of(d, VirtioCcwDevice, parent_obj); + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + + return container_of(ccw_dev, VirtioCcwDevice, parent_obj); } static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, @@ -908,7 +915,8 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + SubchDev *sch = ccw_dev->sch; uint64_t indicators; /* queue indicators + secondary indicators */ @@ -966,9 +974,10 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + CcwDevice *ccw_dev = CCW_DEVICE(d); virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(dev->sch); + css_reset_sch(ccw_dev->sch); } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -984,7 +993,7 @@ static void virtio_ccw_vmstate_change(DeviceState *d, bool running) static bool virtio_ccw_query_guest_notifiers(DeviceState *d) { - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + CcwDevice *dev = CCW_DEVICE(d); return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); } @@ -992,8 +1001,9 @@ static bool virtio_ccw_query_guest_notifiers(DeviceState *d) static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) { int r; + CcwDevice *ccw_dev = CCW_DEVICE(dev); - if (!dev->sch->thinint_active) { + if (!ccw_dev->sch->thinint_active) { return -EINVAL; } @@ -1115,7 +1125,8 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); + CcwDevice *ccw_dev = CCW_DEVICE(d); + bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); int r, n; if (with_irqfd && assigned) { @@ -1174,7 +1185,8 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); subch_device_save(s, f); @@ -1208,7 +1220,8 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); int len; @@ -1253,7 +1266,8 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *sch = ccw_dev->sch; int n = virtio_get_num_queues(vdev); if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) { @@ -1297,7 +1311,7 @@ static void virtio_ccw_device_unplugged(DeviceState *d) /**************** Virtio-ccw Bus Device Descriptions *******************/ static Property virtio_ccw_net_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1326,7 +1340,7 @@ static const TypeInfo virtio_ccw_net = { }; static Property virtio_ccw_blk_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1355,7 +1369,7 @@ static const TypeInfo virtio_ccw_blk = { }; static Property virtio_ccw_serial_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1384,7 +1398,7 @@ static const TypeInfo virtio_ccw_serial = { }; static Property virtio_ccw_balloon_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1413,7 +1427,7 @@ static const TypeInfo virtio_ccw_balloon = { }; static Property virtio_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1443,7 +1457,7 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1481,7 +1495,7 @@ static void virtio_ccw_rng_instance_init(Object *obj) } static Property virtio_ccw_rng_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1525,32 +1539,20 @@ static int virtio_ccw_busdev_exit(DeviceState *dev) return _info->exit(_dev); } -void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - SubchDev *sch = _dev->sch; + VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); virtio_ccw_stop_ioeventfd(_dev); - - /* - * We should arrive here only for device_del, since we don't support - * direct hot(un)plug of channels, but only through virtio. - */ - assert(sch != NULL); - /* Subchannel is now disabled and no longer valid. */ - sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_DNV); - - css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - - object_unparent(OBJECT(dev)); } static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); + k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->exit = virtio_ccw_busdev_exit; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; @@ -1558,7 +1560,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) static const TypeInfo virtio_ccw_device_info = { .name = TYPE_VIRTIO_CCW_DEVICE, - .parent = TYPE_DEVICE, + .parent = TYPE_CCW_DEVICE, .instance_size = sizeof(VirtioCcwDevice), .class_init = virtio_ccw_device_class_init, .class_size = sizeof(VirtIOCCWDeviceClass), @@ -1610,7 +1612,7 @@ static const TypeInfo virtio_ccw_bus_info = { #ifdef CONFIG_VIRTFS static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 11023aabb778..3f76443fbaad 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -26,6 +26,8 @@ #include #include +#include "ccw-device.h" +#include "hw/s390x/css-bridge.h" #define VIRTIO_CCW_CU_TYPE 0x3832 #define VIRTIO_CCW_CHPID_TYPE 0x32 @@ -65,7 +67,7 @@ typedef struct VirtioBusClass VirtioCcwBusClass; typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { - DeviceClass parent_class; + CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); int (*exit)(VirtioCcwDevice *dev); } VirtIOCCWDeviceClass; @@ -76,9 +78,7 @@ typedef struct VirtIOCCWDeviceClass { #define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) struct VirtioCcwDevice { - DeviceState parent_obj; - SubchDev *sch; - CssDevId bus_id; + CcwDevice parent_obj; int revision; uint32_t max_rev; VirtioBusState bus; @@ -101,9 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } -void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp); - /* virtio-scsi-ccw */ #define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" From f7c40aa1e7feb50bc4d4bc171fa811bdd9a93e51 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Sun, 19 Jun 2016 19:20:46 +0800 Subject: [PATCH 09/25] s390x/pci: fix failures of dma map/unmap In commit d78c19b5cf4821d0c198f4132a085bdbf19dda4c, vfio code stores the IOMMU's offset_within_address_space and adjusts the IOVA before calling vfio_dma_map/vfio_dma_unmap. But s390_translate_iommu already considers the base address of an IOMMU memory region. Thus we use pal as the size and 0x0 as the base address to initialize IOMMU memory subregion. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index a77c10ce9e72..8f03b82469bd 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -498,11 +498,9 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) { - uint64_t size = pbdev->pal - pbdev->pba + 1; - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), - &s390_iommu_ops, "iommu-s390", size); - memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); + &s390_iommu_ops, "iommu-s390", pbdev->pal + 1); + memory_region_add_subregion(&pbdev->mr, 0, &pbdev->iommu_mr); pbdev->iommu_enabled = true; } From e7d336959b7c01699702dcda4b54a822972d74a8 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 13 May 2016 14:58:14 +0800 Subject: [PATCH 10/25] s390x/pci: acceleration for getting S390pciState There are a number of places where the code needs to get the instance of S390pciState. It calls object_resolve_path() every time. This wastes a lot of time and leads to low performance. Thus we add s390_get_phb() to improve it. Because we always have a phb, we remove all return checkings in the callers and add an assert in s390_get_phb() to make sure that phb is getted successfully. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 53 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 8f03b82469bd..ba637c9a95e8 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -29,6 +29,19 @@ do { } while (0) #endif +static S390pciState *s390_get_phb(void) +{ + static S390pciState *phb; + + if (!phb) { + phb = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + assert(phb != NULL); + } + + return phb; +} + int chsc_sei_nt2_get_event(void *res) { ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res; @@ -36,12 +49,7 @@ int chsc_sei_nt2_get_event(void *res) PciCcdfErr *eccdf; int rc = 1; SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return rc; - } + S390pciState *s = s390_get_phb(); sei_cont = QTAILQ_FIRST(&s->pending_sei); if (sei_cont) { @@ -76,12 +84,7 @@ int chsc_sei_nt2_get_event(void *res) int chsc_sei_nt2_have_event(void) { - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return 0; - } + S390pciState *s = s390_get_phb(); return !QTAILQ_EMPTY(&s->pending_sei); } @@ -90,12 +93,7 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) { S390PCIBusDevice *pbdev; int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } + S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { pbdev = &s->pbdev[i]; @@ -180,12 +178,7 @@ S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) S390PCIBusDevice *pbdev; int i; int j = 0; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } + S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { pbdev = &s->pbdev[i]; @@ -207,10 +200,9 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) { S390PCIBusDevice *pbdev; int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + S390pciState *s = s390_get_phb(); - if (!s || !fh) { + if (!fh) { return NULL; } @@ -228,12 +220,7 @@ static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e) { SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return; - } + S390pciState *s = s390_get_phb(); sei_cont = g_malloc0(sizeof(SeiContainer)); sei_cont->fh = fh; From 67aad508decdbbce59faa27072daae0d1d31b1ad Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Mon, 13 Jun 2016 19:28:38 +0800 Subject: [PATCH 11/25] s390x/pci: write fid in CLP_QUERY_PCI_FN We forgot to write the fid; fix that. Signed-off-by: Yi Min Zhao Acked-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-inst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 479375f65d5b..2cd7d1486a1d 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -256,6 +256,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) stq_p(&resquery->sdma, ZPCI_SDMA_ADDR); stq_p(&resquery->edma, ZPCI_EDMA_ADDR); + stl_p(&resquery->fid, pbdev->fid); stw_p(&resquery->pchid, 0); stw_p(&resquery->ug, 1); stl_p(&resquery->uid, pbdev->fid); From c188e30315dc44cf428f2008df4ea2a9788ac8df Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 13 May 2016 16:16:30 +0800 Subject: [PATCH 12/25] s390x/pci: unify FH_ macros Present code uses some macros to structure PCI Function Handle. But their names don't have a uniform format. Let's use FH_MASK_ as the unified prefix. While we're at it, differentiate the SHM bits: use different bits for vfio and emulated devices. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 6 +++--- hw/s390x/s390-pci-bus.h | 9 ++++++--- hw/s390x/s390-pci-inst.c | 18 +++++++++--------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index ba637c9a95e8..7111587a7713 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -170,7 +170,7 @@ static uint32_t s390_pci_get_pfid(PCIDevice *pdev) static uint32_t s390_pci_get_pfh(PCIDevice *pdev) { - return PCI_SLOT(pdev->devfn) | FH_VIRT; + return PCI_SLOT(pdev->devfn) | FH_SHM_VFIO; } S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) @@ -345,7 +345,7 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, }; if (!pbdev->configured || !pbdev->pdev || - !(pbdev->fh & FH_ENABLED) || !pbdev->iommu_enabled) { + !(pbdev->fh & FH_MASK_ENABLE) || !pbdev->iommu_enabled) { return ret; } @@ -456,7 +456,7 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, return; } - if (!(pbdev->fh & FH_ENABLED)) { + if (!(pbdev->fh & FH_MASK_ENABLE)) { return; } diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 2c852d47fae7..5cf792613568 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -21,9 +21,12 @@ #include "hw/s390x/css.h" #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" -#define FH_VIRT 0x00ff0000 -#define ENABLE_BIT_OFFSET 31 -#define FH_ENABLED (1 << ENABLE_BIT_OFFSET) +#define FH_MASK_ENABLE 0x80000000 +#define FH_MASK_INSTANCE 0x7f000000 +#define FH_MASK_SHM 0x00ff0000 +#define FH_MASK_INDEX 0x0000001f +#define FH_SHM_VFIO 0x00010000 +#define FH_SHM_EMUL 0x00020000 #define S390_PCIPT_ADAPTER 2 #define S390_PCI_HOST_BRIDGE(obj) \ diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 2cd7d1486a1d..8b6882497b2b 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -92,7 +92,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stl_p(&rrb->response.fmt, 0); stq_p(&rrb->response.reserved1, 0); stq_p(&rrb->response.reserved2, 0); - stl_p(&rrb->response.mdd, FH_VIRT); + stl_p(&rrb->response.mdd, FH_MASK_SHM); stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); rrb->response.entry_size = sizeof(ClpFhListEntry); finish = 0; @@ -212,12 +212,12 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: - pbdev->fh = pbdev->fh | FH_ENABLED; + pbdev->fh |= FH_MASK_ENABLE; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: - pbdev->fh = pbdev->fh & ~FH_ENABLED; + pbdev->fh &= ~FH_MASK_ENABLE; pbdev->error_state = false; pbdev->lgstg_blocked = false; stl_p(&ressetpci->fh, pbdev->fh); @@ -318,7 +318,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -435,7 +435,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -526,7 +526,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); goto out; @@ -590,7 +590,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -743,7 +743,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -873,7 +873,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; stl_p(&fib.data, data); - if (pbdev->fh & FH_ENABLED) { + if (pbdev->fh & FH_MASK_ENABLE) { fib.fc |= 0x80; } From 06a96dae11a70f3c519a706639e200a635a2cc55 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Thu, 12 May 2016 14:27:25 +0800 Subject: [PATCH 13/25] s390x/pci: refactor s390_pci_find_dev_by_fh Because this function is called very frequently, we should use a more effective way to find the zpci device. So we use the FH's index to get the device directly. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 7111587a7713..1b8377241a0e 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -198,19 +198,12 @@ S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) { - S390PCIBusDevice *pbdev; - int i; S390pciState *s = s390_get_phb(); + S390PCIBusDevice *pbdev; - if (!fh) { - return NULL; - } - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if (pbdev->fh == fh) { - return pbdev; - } + pbdev = &s->pbdev[fh & FH_MASK_INDEX]; + if (pbdev->fh != 0 && pbdev->fh == fh) { + return pbdev; } return NULL; From 5d1abf234462d13bef3617cc2c55b6815703ddf2 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Tue, 19 Apr 2016 15:03:13 +0800 Subject: [PATCH 14/25] s390x/pci: enforce zPCI state checking Current code uses some fields combinatorially to indicate the state of a s390 pci device. This patch introduces device states in order to make the code more readable and more logical. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 104 ++++++++++++------------ hw/s390x/s390-pci-bus.h | 34 +++++++- hw/s390x/s390-pci-inst.c | 168 ++++++++++++++++++++++++++++++++------- hw/s390x/s390-pci-inst.h | 5 ++ include/hw/s390x/sclp.h | 1 + 5 files changed, 230 insertions(+), 82 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 1b8377241a0e..0f6fcefe96c1 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -116,16 +116,22 @@ void s390_pci_sclp_configure(SCCB *sccb) goto out; } - if (pbdev) { - if (pbdev->configured) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - pbdev->configured = true; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { + if (!pbdev) { DPRINTF("sclp config no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + pbdev->state = ZPCI_FS_DISABLED; + rc = SCLP_RC_NORMAL_COMPLETION; + break; + default: + rc = SCLP_RC_NO_ACTION_REQUIRED; } out: psccb->header.response_code = cpu_to_be16(rc); @@ -142,22 +148,28 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) goto out; } - if (pbdev) { - if (!pbdev->configured) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - if (pbdev->summary_ind) { - pci_dereg_irqs(pbdev); - } - if (pbdev->iommu_enabled) { - pci_dereg_ioat(pbdev); - } - pbdev->configured = false; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { + if (!pbdev) { DPRINTF("sclp deconfig no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + rc = SCLP_RC_NO_ACTION_REQUIRED; + break; + default: + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + pbdev->state = ZPCI_FS_STANDBY; + rc = SCLP_RC_NORMAL_COMPLETION; } out: psccb->header.response_code = cpu_to_be16(rc); @@ -183,7 +195,7 @@ S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) for (i = 0; i < PCI_SLOT_MAX; i++) { pbdev = &s->pbdev[i]; - if (pbdev->fh == 0) { + if (pbdev->state == ZPCI_FS_RESERVED) { continue; } @@ -233,9 +245,8 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh, s390_pci_generate_event(2, pec, fh, fid, 0, 0); } -static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, - uint32_t fid, uint64_t faddr, - uint32_t e) +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e) { s390_pci_generate_event(1, pec, fh, fid, faddr, e); } @@ -337,8 +348,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (!pbdev->configured || !pbdev->pdev || - !(pbdev->fh & FH_MASK_ENABLE) || !pbdev->iommu_enabled) { + switch (pbdev->state) { + case ZPCI_FS_ENABLED: + case ZPCI_FS_BLOCKED: + if (!pbdev->iommu_enabled) { + return ret; + } + break; + default: return ret; } @@ -357,30 +374,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } - if (!pbdev->g_iota) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, - addr, 0); - return ret; - } - if (addr < pbdev->pba || addr > pbdev->pal) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, - addr, 0); return ret; } pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), addr); - if (!pte) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, - addr, ERR_EVENT_Q_BIT); return ret; } @@ -449,7 +449,7 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, return; } - if (!(pbdev->fh & FH_MASK_ENABLE)) { + if (pbdev->state != ZPCI_FS_ENABLED) { return; } @@ -571,7 +571,7 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, pbdev->fid = s390_pci_get_pfid(pci_dev); pbdev->pdev = pci_dev; - pbdev->configured = true; + pbdev->state = ZPCI_FS_DISABLED; pbdev->fh = s390_pci_get_pfh(pci_dev); s390_pcihost_setup_msix(pbdev); @@ -592,8 +592,12 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, ->qbus.parent); S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; - if (pbdev->configured) { - pbdev->configured = false; + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + goto out; + case ZPCI_FS_STANDBY: + break; + default: s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, pbdev->fh, pbdev->fid); } @@ -603,6 +607,8 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, pbdev->fh = 0; pbdev->fid = 0; pbdev->pdev = NULL; + pbdev->state = ZPCI_FS_RESERVED; +out: object_unparent(OBJECT(pci_dev)); } diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 5cf792613568..e332f6a212ae 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -153,6 +153,34 @@ enum ZpciIoatDtype { #define ZPCI_TABLE_VALID_MASK 0x20 #define ZPCI_TABLE_PROT_MASK 0x200 +/* PCI Function States + * + * reserved: default; device has just been plugged or is in progress of being + * unplugged + * standby: device is present but not configured; transition from any + * configured state/to this state via sclp configure/deconfigure + * + * The following states make up the "configured" meta-state: + * disabled: device is configured but not enabled; transition between this + * state and enabled via clp enable/disable + * enbaled: device is ready for use; transition to disabled via clp disable; + * may enter an error state + * blocked: ignore all DMA and interrupts; transition back to enabled or from + * error state via mpcifc + * error: an error occured; transition back to enabled via mpcifc + * permanent error: an unrecoverable error occured; transition to standby via + * sclp deconfigure + */ +typedef enum { + ZPCI_FS_RESERVED, + ZPCI_FS_STANDBY, + ZPCI_FS_DISABLED, + ZPCI_FS_ENABLED, + ZPCI_FS_BLOCKED, + ZPCI_FS_ERROR, + ZPCI_FS_PERMANENT_ERROR, +} ZpciState; + typedef struct SeiContainer { QTAILQ_ENTRY(SeiContainer) link; uint32_t fid; @@ -219,9 +247,7 @@ typedef struct S390MsixInfo { typedef struct S390PCIBusDevice { PCIDevice *pdev; - bool configured; - bool error_state; - bool lgstg_blocked; + ZpciState state; bool iommu_enabled; uint32_t fh; uint32_t fid; @@ -255,6 +281,8 @@ void s390_pci_sclp_configure(SCCB *sccb); void s390_pci_sclp_deconfigure(SCCB *sccb); void s390_pci_iommu_enable(S390PCIBusDevice *pbdev); void s390_pci_iommu_disable(S390PCIBusDevice *pbdev); +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e); S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 8b6882497b2b..744f43587c31 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -108,8 +108,9 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); + /* Ignore RESERVED devices. */ stl_p(&rrb->response.fh_list[idx - resume_token].config, - pbdev->configured << 31); + pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31); stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); @@ -213,13 +214,13 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: pbdev->fh |= FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: pbdev->fh &= ~FH_MASK_ENABLE; - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; @@ -318,16 +319,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { + if (!pbdev) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } if (pcias < 6) { @@ -435,16 +445,25 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { + if (!pbdev) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } data = env->regs[r1]; @@ -526,18 +545,55 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { + if (!pbdev) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); goto out; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_MOD_ST_ERROR_RECOVER); + return 0; + default: + break; + } + + if (!pbdev->g_iota) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + + if (end < pbdev->pba || start > pbdev->pal) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + mr = &pbdev->iommu_mr; while (start < end) { entry = mr->iommu_ops->translate(mr, start, 0); if (!entry.translated_addr) { + pbdev->state = ZPCI_FS_ERROR; setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, + start, ERR_EVENT_Q_BIT); goto out; } @@ -590,16 +646,25 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { + if (!pbdev) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } mr = pbdev->pdev->io_regions[pcias].memory; @@ -743,12 +808,23 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) { + if (!pbdev) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + default: + break; + } + if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } @@ -815,11 +891,25 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } break; case ZPCI_MOD_FC_RESET_ERROR: - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_BLOCKED: + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_ENABLED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_RESET_BLOCK: - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_BLOCKED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_SET_MEASURE: pbdev->fmb_addr = ldq_p(&fib.fmb_addr); @@ -861,6 +951,39 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } memset(&fib, 0, sizeof(fib)); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_DISABLED: + if (fh & FH_MASK_ENABLE) { + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + goto out; + /* BLOCKED bit is set to one coincident with the setting of ERROR bit. + * FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */ + case ZPCI_FS_ERROR: + fib.fc |= 0x20; + case ZPCI_FS_BLOCKED: + fib.fc |= 0x40; + case ZPCI_FS_ENABLED: + fib.fc |= 0x80; + if (pbdev->iommu_enabled) { + fib.fc |= 0x10; + } + if (!(fh & FH_MASK_ENABLE)) { + env->regs[r1] |= 1ULL << 63; + } + break; + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_PERM_ERROR); + return 0; + } + stq_p(&fib.pba, pbdev->pba); stq_p(&fib.pal, pbdev->pal); stq_p(&fib.iota, pbdev->g_iota); @@ -873,22 +996,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; stl_p(&fib.data, data); - if (pbdev->fh & FH_MASK_ENABLE) { - fib.fc |= 0x80; - } - - if (pbdev->error_state) { - fib.fc |= 0x40; - } - - if (pbdev->lgstg_blocked) { - fib.fc |= 0x20; - } - - if (pbdev->g_iota) { - fib.fc |= 0x10; - } - +out: if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index b084f2346bc6..c35f3371f4bb 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -249,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp { #define ZPCI_MOD_FC_RESET_BLOCK 9 #define ZPCI_MOD_FC_SET_MEASURE 10 +/* Store PCI Function Controls status codes */ +#define ZPCI_STPCIFC_ST_PERM_ERROR 8 +#define ZPCI_STPCIFC_ST_INVAL_DMAAS 28 +#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40 + /* FIB function controls */ #define ZPCI_FIB_FC_ENABLED 0x80 #define ZPCI_FIB_FC_ERROR 0x40 diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index b0c71b5550ba..fbf357d2ed4a 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -58,6 +58,7 @@ #define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340 #define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 #define SCLP_RC_STANDBY_READ_COMPLETION 0x0410 +#define SCLP_RC_ADAPTER_IN_RESERVED_STATE 0x05f0 #define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED 0x09f0 #define SCLP_RC_INVALID_FUNCTION 0x40f0 #define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 From 90a0f9afec5e148065f4f48b9dbe4422b0bfd131 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Thu, 14 Apr 2016 19:02:39 +0800 Subject: [PATCH 15/25] s390x/pci: introduce S390PCIBus To enable S390PCIBusDevice as qdev, there should be a new bus to plug and manage all instances of S390PCIBusDevice. Due to this, S390PCIBus is introduced. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 10 ++++++++++ hw/s390x/s390-pci-bus.h | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 0f6fcefe96c1..0c67c1e7aa20 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -527,6 +527,9 @@ static int s390_pcihost_init(SysBusDevice *dev) bus = BUS(b); qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); phb->bus = b; + + s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); + QTAILQ_INIT(&s->pending_sei); return 0; } @@ -636,9 +639,16 @@ static const TypeInfo s390_pcihost_info = { } }; +static const TypeInfo s390_pcibus_info = { + .name = TYPE_S390_PCI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(S390PCIBus), +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); + type_register_static(&s390_pcibus_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index e332f6a212ae..c4d40794de1f 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -21,6 +21,7 @@ #include "hw/s390x/css.h" #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" +#define TYPE_S390_PCI_BUS "s390-pcibus" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 @@ -31,6 +32,8 @@ #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) +#define S390_PCI_BUS(obj) \ + OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS) #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 @@ -267,8 +270,13 @@ typedef struct S390PCIBusDevice { IndAddr *indicator; } S390PCIBusDevice; +typedef struct S390PCIBus { + BusState qbus; +} S390PCIBus; + typedef struct S390pciState { PCIHostState parent_obj; + S390PCIBus *bus; S390PCIBusDevice pbdev[PCI_SLOT_MAX]; AddressSpace msix_notify_as; MemoryRegion msix_notify_mr; From 67d5cd9722b230027d3d4267ae6069c5d8a65463 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 11 May 2016 15:10:36 +0800 Subject: [PATCH 16/25] s390x/pci: introduce S390PCIIOMMU Currently each zpci device holds its own DMA address space and memory region. At the same time, all instances of zpci device are stored in S390pciState. So duirng the initialization of S390pciState, all zpci devices are created and then all DMA address spaces are created. Thus, when initializing pci devices, their corresponding DMA address spaces could be found. But zpci qdev will be introduced later. Zpci device may be initialized and plugged afterwards generic pci device. So we should initialize all DMA address spaces and memory regions before initializing zpci devices. We introduce a new struct named S390PCIIOMMU. And a new field of S390pciState, which is an array to store all instances of S390PCIIOMMU, is added so that qemu pci code could find the corresponding DMA address space when initializing a generic pci device. And this should be done before the connection of a zpci device and a generic pci device is built. Signed-off-by: Yi Min Zhao Acked-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 19 +++++++++++-------- hw/s390x/s390-pci-bus.h | 9 +++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 0c67c1e7aa20..af3263f5352b 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -406,7 +406,7 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { S390pciState *s = opaque; - return &s->pbdev[PCI_SLOT(devfn)].as; + return &s->iommu[PCI_SLOT(devfn)]->as; } static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) @@ -478,15 +478,15 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) { - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), + memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr), &s390_iommu_ops, "iommu-s390", pbdev->pal + 1); - memory_region_add_subregion(&pbdev->mr, 0, &pbdev->iommu_mr); + memory_region_add_subregion(&pbdev->iommu->mr, 0, &pbdev->iommu_mr); pbdev->iommu_enabled = true; } void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) { - memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); + memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr); object_unparent(OBJECT(&pbdev->iommu_mr)); pbdev->iommu_enabled = false; } @@ -494,13 +494,15 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) static void s390_pcihost_init_as(S390pciState *s) { int i; - S390PCIBusDevice *pbdev; + S390PCIIOMMU *iommu; for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - memory_region_init(&pbdev->mr, OBJECT(s), + iommu = g_malloc0(sizeof(S390PCIIOMMU)); + memory_region_init(&iommu->mr, OBJECT(s), "iommu-root-s390", UINT64_MAX); - address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci"); + address_space_init(&iommu->as, &iommu->mr, "iommu-pci"); + + s->iommu[i] = iommu; } memory_region_init_io(&s->msix_notify_mr, OBJECT(s), @@ -576,6 +578,7 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, pbdev->pdev = pci_dev; pbdev->state = ZPCI_FS_DISABLED; pbdev->fh = s390_pci_get_pfh(pci_dev); + pbdev->iommu = s->iommu[PCI_SLOT(pci_dev->devfn)]; s390_pcihost_setup_msix(pbdev); diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index c4d40794de1f..ea1efcc0a090 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -248,6 +248,11 @@ typedef struct S390MsixInfo { uint32_t pba_offset; } S390MsixInfo; +typedef struct S390PCIIOMMU { + AddressSpace as; + MemoryRegion mr; +} S390PCIIOMMU; + typedef struct S390PCIBusDevice { PCIDevice *pdev; ZpciState state; @@ -263,8 +268,7 @@ typedef struct S390PCIBusDevice { uint8_t sum; S390MsixInfo msix; AdapterRoutes routes; - AddressSpace as; - MemoryRegion mr; + S390PCIIOMMU *iommu; MemoryRegion iommu_mr; IndAddr *summary_ind; IndAddr *indicator; @@ -278,6 +282,7 @@ typedef struct S390pciState { PCIHostState parent_obj; S390PCIBus *bus; S390PCIBusDevice pbdev[PCI_SLOT_MAX]; + S390PCIIOMMU *iommu[PCI_SLOT_MAX]; AddressSpace msix_notify_as; MemoryRegion msix_notify_mr; QTAILQ_HEAD(, SeiContainer) pending_sei; From 3e5cfba3ca0a3b1010faa171518b0bf69effb2d9 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 11 May 2016 15:10:06 +0800 Subject: [PATCH 17/25] s390x/pci: introduce S390PCIBusDevice qdev To support definitions of s390 pci attributes in Qemu cmdline, we have to make current S390PCIBusDevice struct inherit DeviceState and add three properties for it. Currently we only support definitions of uid and fid. 'uid' is optionally defined by users, identifies a zpci device and must be defined with a 16-bit and non-zero unique value. 'fid' ranges from 0x0 to 0xFFFFFFFF. For fid property, we introduce a new PropertyInfo by the name of s390_pci_fid_propinfo with our special setter and getter. As 'fid' is optional, introduce 'fid_defined' to track whether the user specified a fid. 'target' field is to direct qemu to find the corresponding generic PCI device. It is equal to the 'id' value of one of generic pci devices. If the user doesn't specify 'id' parameter for a generic pci device, its 'id' value will be generated automatically and use this value as 'target' to create an associated zpci device. If the user did not specify 'uid' or 'fid', values are generated automatically. 'target' is required. In addition, if a pci device has no associated zpci device, the code will generate a zpci device automatically for it. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 257 ++++++++++++++++++++++++++++++++++++++-- hw/s390x/s390-pci-bus.h | 12 +- 2 files changed, 261 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index af3263f5352b..8e0f70719199 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -12,6 +12,8 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #include "qemu-common.h" #include "cpu.h" #include "s390-pci-bus.h" @@ -96,8 +98,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if ((pbdev->fh != 0) && (pbdev->fid == fid)) { + pbdev = s->pbdev[i]; + if (pbdev && pbdev->fid == fid) { return pbdev; } } @@ -185,6 +187,50 @@ static uint32_t s390_pci_get_pfh(PCIDevice *pdev) return PCI_SLOT(pdev->devfn) | FH_SHM_VFIO; } +static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) +{ + int i; + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); + + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = s->pbdev[i]; + if (!pbdev) { + continue; + } + + if (pbdev->uid == uid) { + return pbdev; + } + } + + return NULL; +} + +static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) +{ + int i; + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); + + if (!target) { + return NULL; + } + + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = s->pbdev[i]; + if (!pbdev) { + continue; + } + + if (!strcmp(pbdev->target, target)) { + return pbdev; + } + } + + return NULL; +} + S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) { S390PCIBusDevice *pbdev; @@ -193,7 +239,10 @@ S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; + pbdev = s->pbdev[i]; + if (!pbdev) { + continue; + } if (pbdev->state == ZPCI_FS_RESERVED) { continue; @@ -213,8 +262,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) S390pciState *s = s390_get_phb(); S390PCIBusDevice *pbdev; - pbdev = &s->pbdev[fh & FH_MASK_INDEX]; - if (pbdev->fh != 0 && pbdev->fh == fh) { + pbdev = s->pbdev[fh & FH_MASK_INDEX]; + if (pbdev && pbdev->fh == fh) { return pbdev; } @@ -564,6 +613,22 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) return 0; } +static S390PCIBusDevice *s390_pci_device_new(const char *target) +{ + DeviceState *dev = NULL; + S390pciState *s = s390_get_phb(); + + dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE); + if (!dev) { + return NULL; + } + + qdev_prop_set_string(dev, "target", target); + qdev_init_nofail(dev); + + return S390_PCI_DEVICE(dev); +} + static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -572,8 +637,24 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) ->qbus.parent); - pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + if (!dev->id) { + /* In the case the PCI device does not define an id */ + /* we generate one based on the PCI address */ + dev->id = g_strdup_printf("auto_%02x:%02x.%01x", + pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); + } + + pbdev = s390_pci_find_dev_by_target(dev->id); + if (!pbdev) { + pbdev = s390_pci_device_new(dev->id); + if (!pbdev) { + error_setg(errp, "create zpci device failed"); + } + } + s->pbdev[PCI_SLOT(pci_dev->devfn)] = pbdev; pbdev->fid = s390_pci_get_pfid(pci_dev); pbdev->pdev = pci_dev; pbdev->state = ZPCI_FS_DISABLED; @@ -596,7 +677,7 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, PCIDevice *pci_dev = PCI_DEVICE(dev); S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) ->qbus.parent); - S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + S390PCIBusDevice *pbdev = s->pbdev[PCI_SLOT(pci_dev->devfn)]; switch (pbdev->state) { case ZPCI_FS_RESERVED: @@ -648,10 +729,172 @@ static const TypeInfo s390_pcibus_info = { .instance_size = sizeof(S390PCIBus), }; +static uint16_t s390_pci_generate_uid(void) +{ + uint16_t uid = 0; + + do { + uid++; + if (!s390_pci_find_dev_by_uid(uid)) { + return uid; + } + } while (uid < ZPCI_MAX_UID); + + return UID_UNDEFINED; +} + +static uint32_t s390_pci_generate_fid(Error **errp) +{ + uint32_t fid = 0; + + while (fid <= ZPCI_MAX_FID) { + if (!s390_pci_find_dev_by_fid(fid)) { + return fid; + } + + if (fid == ZPCI_MAX_FID) { + break; + } + + fid++; + } + + error_setg(errp, "no free fid could be found"); + return 0; +} + +static void s390_pci_device_realize(DeviceState *dev, Error **errp) +{ + S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev); + + if (!zpci->target) { + error_setg(errp, "target must be defined"); + return; + } + + if (s390_pci_find_dev_by_target(zpci->target)) { + error_setg(errp, "target %s already has an associated zpci device", + zpci->target); + return; + } + + if (zpci->uid == UID_UNDEFINED) { + zpci->uid = s390_pci_generate_uid(); + if (!zpci->uid) { + error_setg(errp, "no free uid could be found"); + return; + } + } else if (s390_pci_find_dev_by_uid(zpci->uid)) { + error_setg(errp, "uid %u already in use", zpci->uid); + return; + } + + if (!zpci->fid_defined) { + Error *local_error = NULL; + + zpci->fid = s390_pci_generate_fid(&local_error); + if (local_error) { + error_propagate(errp, local_error); + return; + } + } else if (s390_pci_find_dev_by_fid(zpci->fid)) { + error_setg(errp, "fid %u already in use", zpci->fid); + return; + } + + zpci->state = ZPCI_FS_RESERVED; +} + +static void s390_pci_device_reset(DeviceState *dev) +{ + S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + return; + case ZPCI_FS_STANDBY: + break; + default: + pbdev->fh &= ~FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_DISABLED; + break; + } + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->fmb_addr = 0; +} + +static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_uint32(v, name, ptr, errp); +} + +static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj); + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_uint32(v, name, ptr, errp); + zpci->fid_defined = true; +} + +static PropertyInfo s390_pci_fid_propinfo = { + .name = "zpci_fid", + .get = s390_pci_get_fid, + .set = s390_pci_set_fid, +}; + +#define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t) + +static Property s390_pci_device_properties[] = { + DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), + DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), + DEFINE_PROP_STRING("target", S390PCIBusDevice, target), + DEFINE_PROP_END_OF_LIST(), +}; + +static void s390_pci_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "zpci device"; + dc->reset = s390_pci_device_reset; + dc->bus_type = TYPE_S390_PCI_BUS; + dc->realize = s390_pci_device_realize; + dc->props = s390_pci_device_properties; +} + +static const TypeInfo s390_pci_device_info = { + .name = TYPE_S390_PCI_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390PCIBusDevice), + .class_init = s390_pci_device_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); type_register_static(&s390_pcibus_info); + type_register_static(&s390_pci_device_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index ea1efcc0a090..658bda5f3c43 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -22,6 +22,7 @@ #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" #define TYPE_S390_PCI_BUS "s390-pcibus" +#define TYPE_S390_PCI_DEVICE "zpci" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 @@ -29,11 +30,16 @@ #define FH_SHM_VFIO 0x00010000 #define FH_SHM_EMUL 0x00020000 #define S390_PCIPT_ADAPTER 2 +#define ZPCI_MAX_FID 0xffffffff +#define ZPCI_MAX_UID 0xffff +#define UID_UNDEFINED 0 #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) #define S390_PCI_BUS(obj) \ OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS) +#define S390_PCI_DEVICE(obj) \ + OBJECT_CHECK(S390PCIBusDevice, (obj), TYPE_S390_PCI_DEVICE) #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 @@ -254,11 +260,15 @@ typedef struct S390PCIIOMMU { } S390PCIIOMMU; typedef struct S390PCIBusDevice { + DeviceState qdev; PCIDevice *pdev; ZpciState state; bool iommu_enabled; + char *target; + uint16_t uid; uint32_t fh; uint32_t fid; + bool fid_defined; uint64_t g_iota; uint64_t pba; uint64_t pal; @@ -281,7 +291,7 @@ typedef struct S390PCIBus { typedef struct S390pciState { PCIHostState parent_obj; S390PCIBus *bus; - S390PCIBusDevice pbdev[PCI_SLOT_MAX]; + S390PCIBusDevice *pbdev[PCI_SLOT_MAX]; S390PCIIOMMU *iommu[PCI_SLOT_MAX]; AddressSpace msix_notify_as; MemoryRegion msix_notify_mr; From bf328399da57450feaeaa24c2539a351e41713db Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 11 May 2016 15:22:42 +0800 Subject: [PATCH 18/25] s390x/pci: enable uid-checking The uid-checking facility guarantees uniqueness of the uid within the vm and exposes the real uid to the guest when listing pci devices. Let's always enable it and present it to the guest in the response to the list pci clp command. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.h | 1 + hw/s390x/s390-pci-inst.c | 7 +++---- hw/s390x/s390-pci-inst.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 658bda5f3c43..2d9150c95cca 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -33,6 +33,7 @@ #define ZPCI_MAX_FID 0xffffffff #define ZPCI_MAX_UID 0xffff #define UID_UNDEFINED 0 +#define UID_CHECKING_ENABLED 0x01 #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 744f43587c31..c84d0eb3b8ae 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -56,8 +56,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) } if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || - ldq_p(&rrb->request.reserved1) != 0 || - ldq_p(&rrb->request.reserved2) != 0) { + ldq_p(&rrb->request.reserved1) != 0) { res_code = CLP_RC_RESNOT0; rc = -EINVAL; goto out; @@ -91,9 +90,9 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stl_p(&rrb->response.fmt, 0); stq_p(&rrb->response.reserved1, 0); - stq_p(&rrb->response.reserved2, 0); stl_p(&rrb->response.mdd, FH_MASK_SHM); stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); + rrb->response.flags = UID_CHECKING_ENABLED; rrb->response.entry_size = sizeof(ClpFhListEntry); finish = 0; idx = resume_token; @@ -260,7 +259,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) stl_p(&resquery->fid, pbdev->fid); stw_p(&resquery->pchid, 0); stw_p(&resquery->ug, 1); - stl_p(&resquery->uid, pbdev->fid); + stl_p(&resquery->uid, pbdev->uid); stw_p(&resquery->hdr.rsp, CLP_RC_OK); break; } diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index c35f3371f4bb..e1c2ee15295a 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -104,7 +104,7 @@ typedef struct ClpRspListPci { uint64_t resume_token; uint32_t mdd; uint16_t max_fn; - uint8_t reserved2; + uint8_t flags; uint8_t entry_size; ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES]; } QEMU_PACKED ClpRspListPci; From af9ed379fc6bb662130e17fbc27f6ac0f5909696 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 13 May 2016 12:50:09 +0800 Subject: [PATCH 19/25] s390x/pci: enable zpci hot-plug/hot-unplug We need to support hot-plug/hot-unplug for the new zpci devices as well. This patch enables the present hot-plug/hot-unplug handlers to support not only generic pci devices but also zpci devices. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 119 ++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 8e0f70719199..57d5d143184f 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -177,16 +177,6 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) psccb->header.response_code = cpu_to_be16(rc); } -static uint32_t s390_pci_get_pfid(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn); -} - -static uint32_t s390_pci_get_pfh(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn) | FH_SHM_VFIO; -} - static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) { int i; @@ -580,6 +570,7 @@ static int s390_pcihost_init(SysBusDevice *dev) phb->bus = b; s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); + qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL); QTAILQ_INIT(&s->pending_sei); return 0; @@ -632,52 +623,87 @@ static S390PCIBusDevice *s390_pci_device_new(const char *target) static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390PCIBusDevice *pbdev; - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); + PCIDevice *pdev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); - if (!dev->id) { - /* In the case the PCI device does not define an id */ - /* we generate one based on the PCI address */ - dev->id = g_strdup_printf("auto_%02x:%02x.%01x", - pci_bus_num(pci_dev->bus), - PCI_SLOT(pci_dev->devfn), - PCI_FUNC(pci_dev->devfn)); - } + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pdev = PCI_DEVICE(dev); - pbdev = s390_pci_find_dev_by_target(dev->id); - if (!pbdev) { - pbdev = s390_pci_device_new(dev->id); + if (!dev->id) { + /* In the case the PCI device does not define an id */ + /* we generate one based on the PCI address */ + dev->id = g_strdup_printf("auto_%02x:%02x.%01x", + pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + } + + pbdev = s390_pci_find_dev_by_target(dev->id); if (!pbdev) { - error_setg(errp, "create zpci device failed"); + pbdev = s390_pci_device_new(dev->id); + if (!pbdev) { + error_setg(errp, "create zpci device failed"); + } } - } - s->pbdev[PCI_SLOT(pci_dev->devfn)] = pbdev; - pbdev->fid = s390_pci_get_pfid(pci_dev); - pbdev->pdev = pci_dev; - pbdev->state = ZPCI_FS_DISABLED; - pbdev->fh = s390_pci_get_pfh(pci_dev); - pbdev->iommu = s->iommu[PCI_SLOT(pci_dev->devfn)]; + if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + pbdev->fh |= FH_SHM_VFIO; + } else { + pbdev->fh |= FH_SHM_EMUL; + } - s390_pcihost_setup_msix(pbdev); + pbdev->pdev = pdev; + pbdev->iommu = s->iommu[PCI_SLOT(pdev->devfn)]; + pbdev->state = ZPCI_FS_STANDBY; + s390_pcihost_setup_msix(pbdev); - if (dev->hotplugged) { - s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, - pbdev->fh, pbdev->fid); - s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, - pbdev->fh, pbdev->fid); + if (dev->hotplugged) { + s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, + pbdev->fh, pbdev->fid); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + int idx; + + pbdev = S390_PCI_DEVICE(dev); + for (idx = 0; idx < PCI_SLOT_MAX; idx++) { + if (!s->pbdev[idx]) { + s->pbdev[idx] = pbdev; + pbdev->fh = idx; + return; + } + } + + error_setg(errp, "no slot for plugging zpci device"); } } static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); - S390PCIBusDevice *pbdev = s->pbdev[PCI_SLOT(pci_dev->devfn)]; + int i; + PCIDevice *pci_dev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pci_dev = PCI_DEVICE(dev); + + for (i = 0 ; i < PCI_SLOT_MAX; i++) { + if (s->pbdev[i]->pdev == pci_dev) { + pbdev = s->pbdev[i]; + break; + } + } + + if (!pbdev) { + object_unparent(OBJECT(pci_dev)); + return; + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + pbdev = S390_PCI_DEVICE(dev); + pci_dev = pbdev->pdev; + } switch (pbdev->state) { case ZPCI_FS_RESERVED: @@ -691,12 +717,13 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); - pbdev->fh = 0; - pbdev->fid = 0; + object_unparent(OBJECT(pci_dev)); pbdev->pdev = NULL; pbdev->state = ZPCI_FS_RESERVED; out: - object_unparent(OBJECT(pci_dev)); + pbdev->fid = 0; + s->pbdev[pbdev->fh & FH_MASK_INDEX] = NULL; + object_unparent(OBJECT(pbdev)); } static void s390_pcihost_class_init(ObjectClass *klass, void *data) From bd4976838d1f2b79d4db68a70ddf8d46e0f8b054 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 15 Jun 2016 17:02:36 +0800 Subject: [PATCH 20/25] s390x/pci: add checkings in CLP_SET_PCI_FN The code in CLP_SET_PCI_FN case misses some checkings. Let's add them. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-inst.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index c84d0eb3b8ae..8b4f4d47b8c0 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -212,12 +212,33 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: + switch (reqsetpci->ndas) { + case 0: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS); + goto out; + case 1: + break; + default: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES); + goto out; + } + + if (pbdev->fh & FH_MASK_ENABLE) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + pbdev->fh |= FH_MASK_ENABLE; pbdev->state = ZPCI_FS_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: + if (!(pbdev->fh & FH_MASK_ENABLE)) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + device_reset(DEVICE(pbdev)); pbdev->fh &= ~FH_MASK_ENABLE; pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); From ab9746570ab375f3f2a3d5ec0c95d2621aca5493 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 3 Jun 2016 14:17:59 +0800 Subject: [PATCH 21/25] s390x/pci: refactor s390_pci_find_dev_by_idx s390_find_dev_by_idx() only indexes usable zpci devices. It implies that the index value of each zpci device is dynamic and may change if a new zpci device is plugged. So we have to use a constant index to look up the device. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 57d5d143184f..9e24268fd177 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -223,28 +223,9 @@ static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) { - S390PCIBusDevice *pbdev; - int i; - int j = 0; S390pciState *s = s390_get_phb(); - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = s->pbdev[i]; - if (!pbdev) { - continue; - } - - if (pbdev->state == ZPCI_FS_RESERVED) { - continue; - } - - if (j == idx) { - return pbdev; - } - j++; - } - - return NULL; + return s->pbdev[idx & FH_MASK_INDEX]; } S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) From 4e3bfc167d9cc9c9e3c26eb15cb06439547f0533 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 3 Jun 2016 15:16:01 +0800 Subject: [PATCH 22/25] s390x/pci: refactor list_pci Because of the refactor of s390_pci_find_dev_by_idx(), list_pci() should be updated. We introduce a new function to get the next available zpci device. It simplifies the code of looking up zpci devices. Signed-off-by: Yi Min Zhao Acked-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 20 ++++++++++++++++ hw/s390x/s390-pci-bus.h | 1 + hw/s390x/s390-pci-inst.c | 50 +++++++++++++++++++--------------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 9e24268fd177..f930b04e995b 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -91,6 +91,26 @@ int chsc_sei_nt2_have_event(void) return !QTAILQ_EMPTY(&s->pending_sei); } +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev) +{ + int idx = 0; + S390PCIBusDevice *dev = NULL; + S390pciState *s = s390_get_phb(); + + if (pbdev) { + idx = (pbdev->fh & FH_MASK_INDEX) + 1; + } + + for (; idx < PCI_SLOT_MAX; idx++) { + dev = s->pbdev[idx]; + if (dev && dev->state != ZPCI_FS_RESERVED) { + return dev; + } + } + + return NULL; +} + S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) { S390PCIBusDevice *pbdev; diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 2d9150c95cca..71cdd7aa22e7 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -310,5 +310,6 @@ void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev); #endif diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 8b4f4d47b8c0..70db83546ecc 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -37,9 +37,9 @@ static void s390_set_status_code(CPUS390XState *env, static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) { - S390PCIBusDevice *pbdev; - uint32_t res_code, initial_l2, g_l2, finish; - int rc, idx; + S390PCIBusDevice *pbdev = NULL; + uint32_t res_code, initial_l2, g_l2; + int rc, i; uint64_t resume_token; rc = 0; @@ -71,6 +71,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) rc = -EINVAL; goto out; } + } else { + pbdev = s390_pci_find_next_avail_dev(NULL); } if (lduw_p(&rrb->response.hdr.len) < 48) { @@ -94,40 +96,36 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); rrb->response.flags = UID_CHECKING_ENABLED; rrb->response.entry_size = sizeof(ClpFhListEntry); - finish = 0; - idx = resume_token; + + i = 0; g_l2 = LIST_PCI_HDR_LEN; - do { - pbdev = s390_pci_find_dev_by_idx(idx); - if (!pbdev) { - finish = 1; - break; - } - stw_p(&rrb->response.fh_list[idx - resume_token].device_id, + while (g_l2 < initial_l2 && pbdev) { + stw_p(&rrb->response.fh_list[i].device_id, pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); - stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, + stw_p(&rrb->response.fh_list[i].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); /* Ignore RESERVED devices. */ - stl_p(&rrb->response.fh_list[idx - resume_token].config, + stl_p(&rrb->response.fh_list[i].config, pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31); - stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); - stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); + stl_p(&rrb->response.fh_list[i].fid, pbdev->fid); + stl_p(&rrb->response.fh_list[i].fh, pbdev->fh); g_l2 += sizeof(ClpFhListEntry); /* Add endian check for DPRINTF? */ DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", - g_l2, - lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id), - lduw_p(&rrb->response.fh_list[idx - resume_token].device_id), - ldl_p(&rrb->response.fh_list[idx - resume_token].fid), - ldl_p(&rrb->response.fh_list[idx - resume_token].fh)); - idx++; - } while (g_l2 < initial_l2); - - if (finish == 1) { + g_l2, + lduw_p(&rrb->response.fh_list[i].vendor_id), + lduw_p(&rrb->response.fh_list[i].device_id), + ldl_p(&rrb->response.fh_list[i].fid), + ldl_p(&rrb->response.fh_list[i].fh)); + pbdev = s390_pci_find_next_avail_dev(pbdev); + i++; + } + + if (!pbdev) { resume_token = 0; } else { - resume_token = idx; + resume_token = pbdev->fh & FH_MASK_INDEX; } stq_p(&rrb->response.resume_token, resume_token); stw_p(&rrb->response.hdr.len, g_l2); From 0a608a6e132abffa8fd9455e2354a47acb95847e Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 15 Jun 2016 17:09:10 +0800 Subject: [PATCH 23/25] s390x/pci: fix stpcifc_service_call Firstly the function misses dmaas checking. This patch adds it. Secondly the function uses s390_pci_find_dev_by_fh() to look up the zpci device. This may fail if the guest provides a valid and disabled fh but fh of the associated zpci device is enabled. Thus we use s390_pci_find_dev_by_idx() instead. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-inst.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 70db83546ecc..37572df821da 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -944,6 +944,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) { CPUS390XState *env = &cpu->env; + uint8_t dmaas; uint32_t fh; ZpciFib fib; S390PCIBusDevice *pbdev; @@ -956,13 +957,20 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } fh = env->regs[r1] >> 32; + dmaas = (env->regs[r1] >> 16) & 0xff; + + if (dmaas) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_INVAL_DMAAS); + return 0; + } if (fiba & 0x7) { program_interrupt(env, PGM_SPECIFICATION, 6); return 0; } - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX); if (!pbdev) { setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; From cdd85eb2804018ab46a742ebf64dc5366b9fae73 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Fri, 6 May 2016 18:44:40 +0800 Subject: [PATCH 24/25] s390x/pci: replace fid with idx in msg data of msix Present code uses fid as the part of message data of msix for looking up the specific zpci device. However it limits the usable range of fid, and the code looking up the zpci device may fail due to truncation of the fid. In addition, fh is composed of enabled bit, FH_VIRT and the array index. So we can use the array index as the identifier to store in msg data. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 8 ++++---- hw/s390x/s390-pci-inst.c | 3 ++- target-s390x/kvm.c | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index f930b04e995b..630a8262023a 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -474,18 +474,18 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, { S390PCIBusDevice *pbdev; uint32_t io_int_word; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; uint64_t ind_bit; uint32_t sum_bit; uint32_t e = 0; - DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec); + DPRINTF("write_msix data 0x%" PRIx64 " idx %d vec 0x%x\n", data, idx, vec); - pbdev = s390_pci_find_dev_by_fid(fid); + pbdev = s390_pci_find_dev_by_idx(idx); if (!pbdev) { e |= (vec << ERR_EVENT_MVN_OFFSET); - s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e); + s390_pci_generate_error_event(ERR_EVENT_NOMSI, idx, 0, addr, e); return; } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 37572df821da..331bc4cfded9 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -419,7 +419,8 @@ static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset, msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS); + val = pci_get_long(msg_data) | + ((pbdev->fh & FH_MASK_INDEX) << ZPCI_MSI_VEC_BITS); pci_set_long(msg_data, val); DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data); } diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 45e94ca48abd..2991bff6a448 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2246,10 +2246,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { S390PCIBusDevice *pbdev; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; - pbdev = s390_pci_find_dev_by_fid(fid); + pbdev = s390_pci_find_dev_by_idx(idx); if (!pbdev) { DPRINTF("add_msi_route no dev\n"); return -ENODEV; From 93d16d81c8af0fe93a04bee8a4cb3259f480cab6 Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Wed, 27 Apr 2016 17:44:17 +0800 Subject: [PATCH 25/25] s390x/pci: make hot-unplug handler smoother The current implementation of hot-unplug handler is abrupt. Any pci operation will be just rejected if pci device is unconfigured. Thus a pci device can not be reset or destroyed in a right, smooth and safe way. Improve this as follows: - Notify the guest via a HP_EVENT_DECONFIGURE_REQUEST(0x303) event in the unplug handler, giving it a chance to deconfigure the device via sclp and allowing us to continue hot-unplug afterwards. - Set up a timer that will generate the HP_EVENT_CONFIGURE_TO_STBRES (0x304) event as before if the guest did not react after an adequate time. Signed-off-by: Yi Min Zhao Reviewed-by: Pierre Morel Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-bus.c | 35 ++++++++++++++++++++++++++++++++++- hw/s390x/s390-pci-bus.h | 3 +++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 630a8262023a..640a4eaa61e5 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -192,6 +192,10 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) } pbdev->state = ZPCI_FS_STANDBY; rc = SCLP_RC_NORMAL_COMPLETION; + + if (pbdev->release_timer) { + qdev_unplug(DEVICE(pbdev->pdev), NULL); + } } out: psccb->header.response_code = cpu_to_be16(rc); @@ -679,6 +683,23 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, } } +static void s390_pcihost_timer_cb(void *opaque) +{ + S390PCIBusDevice *pbdev = opaque; + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->state = ZPCI_FS_STANDBY; + s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + pbdev->fh, pbdev->fid); + qdev_unplug(DEVICE(pbdev), NULL); +} + static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -712,8 +733,20 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, case ZPCI_FS_STANDBY: break; default: - s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST, pbdev->fh, pbdev->fid); + pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s390_pcihost_timer_cb, + pbdev); + timer_mod(pbdev->release_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT); + return; + } + + if (pbdev->release_timer && timer_pending(pbdev->release_timer)) { + timer_del(pbdev->release_timer); + timer_free(pbdev->release_timer); + pbdev->release_timer = NULL; } s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 71cdd7aa22e7..f1fbd3c1b686 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -34,6 +34,7 @@ #define ZPCI_MAX_UID 0xffff #define UID_UNDEFINED 0 #define UID_CHECKING_ENABLED 0x01 +#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5) #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) @@ -44,6 +45,7 @@ #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 +#define HP_EVENT_DECONFIGURE_REQUEST 0x0303 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304 #define HP_EVENT_STANDBY_TO_RESERVED 0x0308 @@ -283,6 +285,7 @@ typedef struct S390PCIBusDevice { MemoryRegion iommu_mr; IndAddr *summary_ind; IndAddr *indicator; + QEMUTimer *release_timer; } S390PCIBusDevice; typedef struct S390PCIBus {