Last round of s390x patches for 2.7:

- A large update of the s390x PCI code, bringing it in line with
   the architecture
 - Fixes and improvements in the ipl (boot) code
 - Refactoring in the css code
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXg1MjAAoJEN7Pa5PG8C+vOpIP/A0s4oGk056YHN8APCCPEnhD
 jQIH7GxMZXsCvBb0w7mcvk0EYqoGBOplWZ9mOzHGr8wzQhL56quOOY3Esidj8yVD
 Nulwg4bPtd4SQJoaV0CndZq9Vn6JtWsyS05ncq23VnasrMLPzzAPltI7EuV8OynY
 zgpb/B6KM1vk/wN9brci2WGKTaeTeAD9FmHTuPTiYJDZEZL8FjjQkE0sHmCwNm9Z
 w0dlwEfRyFPwNtvYYMQmlU8jl9MlizOi/FKIU5G2FtsGlwgyMYPoayAuv455iIm9
 a7wb4Sb2VdWeLWzB9E0bqzMJ+mmH+nwxMtMzFw5XYw2uBLbLh0J3PxB1UUDwab12
 BJMJc4QRzTNfjddwVD2Yi8xg9da0AEOoynHogK4r2z3mlBaloNBHIm63Dkjmrvfw
 2W/FQ67RdcQah54Du62ZezSoyjfxVhCUF0tHfh+PqjhXeC8L8dPvKug/vLp2bgVS
 JZfz0ObjGrYGJ3JLIPKobEif4teXPmxiOEzYhCnIUE6ZHFnjlrOdWUiVEpA8laMR
 jQn1amp0UkbaPXnNjcS35swnSYHHOGpI3N0cJ5E/xM+uRLZ2W569ceIfAhCRb2Su
 oGRQTE8vnSyni+H4SoAXqSttGbXX8ST5mR2BCnvX+vNzUYd/azV+CKckcdgfJtXR
 Q+6YDC9Y8SLL/pJFzSfg
 =/+SU
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20160711' into staging

Last round of s390x patches for 2.7:
- A large update of the s390x PCI code, bringing it in line with
  the architecture
- Fixes and improvements in the ipl (boot) code
- Refactoring in the css code

# gpg: Signature made Mon 11 Jul 2016 09:04:51 BST
# gpg:                using RSA key 0xDECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20160711: (25 commits)
  s390x/pci: make hot-unplug handler smoother
  s390x/pci: replace fid with idx in msg data of msix
  s390x/pci: fix stpcifc_service_call
  s390x/pci: refactor list_pci
  s390x/pci: refactor s390_pci_find_dev_by_idx
  s390x/pci: add checkings in CLP_SET_PCI_FN
  s390x/pci: enable zpci hot-plug/hot-unplug
  s390x/pci: enable uid-checking
  s390x/pci: introduce S390PCIBusDevice qdev
  s390x/pci: introduce S390PCIIOMMU
  s390x/pci: introduce S390PCIBus
  s390x/pci: enforce zPCI state checking
  s390x/pci: refactor s390_pci_find_dev_by_fh
  s390x/pci: unify FH_ macros
  s390x/pci: write fid in CLP_QUERY_PCI_FN
  s390x/pci: acceleration for getting S390pciState
  s390x/pci: fix failures of dma map/unmap
  s390x/css: Unplug handler of virtual css bridge
  s390x/css: Factor out virtual css bridge and bus
  s390x/css: use define for "virtual-css-bridge" literal
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-07-11 18:46:38 +01:00
commit f1ef557866
23 changed files with 1251 additions and 458 deletions

View file

@ -8,6 +8,8 @@ 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 += 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

27
hw/s390x/ccw-device.c Normal file
View file

@ -0,0 +1,27 @@
/*
* Common device infrastructure for devices in the virtual css
*
* Copyright 2016 IBM Corp.
* Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#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)

43
hw/s390x/ccw-device.h Normal file
View file

@ -0,0 +1,43 @@
/*
* Common device infrastructure for devices in the virtual css
*
* Copyright 2016 IBM Corp.
* Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#ifndef HW_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;
/* <cssid>.<ssid>.<device number> */
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

124
hw/s390x/css-bridge.c Normal file
View file

@ -0,0 +1,124 @@
/*
* css bridge implementation
*
* Copyright 2012,2016 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.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 */
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 = ccw_device_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)

View file

@ -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;
}

View file

@ -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);
@ -214,10 +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);
if (ccw_dev) {
SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st),
TYPE_SCSI_DEVICE);
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);
@ -225,6 +236,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);
CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw);
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_dev->sch->devno);
ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
return true;
}
}

View file

@ -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 {
@ -82,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;
@ -102,10 +115,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)
{

View file

@ -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"
@ -29,6 +31,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 +51,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,30 +86,40 @@ 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);
}
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;
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];
if ((pbdev->fh != 0) && (pbdev->fid == fid)) {
pbdev = s->pbdev[i];
if (pbdev && pbdev->fid == fid) {
return pbdev;
}
}
@ -118,16 +138,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);
@ -144,79 +170,74 @@ 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;
if (pbdev->release_timer) {
qdev_unplug(DEVICE(pbdev->pdev), NULL);
}
}
out:
psccb->header.response_code = cpu_to_be16(rc);
}
static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid)
{
return PCI_SLOT(pdev->devfn);
}
static uint32_t s390_pci_get_pfh(PCIDevice *pdev)
{
return PCI_SLOT(pdev->devfn) | FH_VIRT;
}
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;
}
S390PCIBusDevice *pbdev;
S390pciState *s = s390_get_phb();
for (i = 0; i < PCI_SLOT_MAX; i++) {
pbdev = &s->pbdev[i];
if (pbdev->fh == 0) {
pbdev = s->pbdev[i];
if (!pbdev) {
continue;
}
if (j == idx) {
if (pbdev->uid == uid) {
return pbdev;
}
j++;
}
return NULL;
}
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target)
{
S390PCIBusDevice *pbdev;
int i;
S390pciState *s = S390_PCI_HOST_BRIDGE(
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
S390PCIBusDevice *pbdev;
S390pciState *s = s390_get_phb();
if (!s || !fh) {
if (!target) {
return NULL;
}
for (i = 0; i < PCI_SLOT_MAX; i++) {
pbdev = &s->pbdev[i];
if (pbdev->fh == fh) {
pbdev = s->pbdev[i];
if (!pbdev) {
continue;
}
if (!strcmp(pbdev->target, target)) {
return pbdev;
}
}
@ -224,16 +245,31 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
return NULL;
}
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
{
S390pciState *s = s390_get_phb();
return s->pbdev[idx & FH_MASK_INDEX];
}
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 && pbdev->fh == fh) {
return pbdev;
}
return NULL;
}
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;
@ -253,9 +289,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);
}
@ -357,8 +392,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
if (!pbdev->configured || !pbdev->pdev ||
!(pbdev->fh & FH_ENABLED) || !pbdev->iommu_enabled) {
switch (pbdev->state) {
case ZPCI_FS_ENABLED:
case ZPCI_FS_BLOCKED:
if (!pbdev->iommu_enabled) {
return ret;
}
break;
default:
return ret;
}
@ -377,30 +418,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;
}
@ -426,7 +450,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)
@ -454,22 +478,22 @@ 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;
}
if (!(pbdev->fh & FH_ENABLED)) {
if (pbdev->state != ZPCI_FS_ENABLED) {
return;
}
@ -498,17 +522,15 @@ 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);
memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr),
&s390_iommu_ops, "iommu-s390", pbdev->pal + 1);
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;
}
@ -516,13 +538,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),
@ -549,6 +573,10 @@ 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));
qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL);
QTAILQ_INIT(&s->pending_sei);
return 0;
}
@ -581,51 +609,155 @@ 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)
{
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();
pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
pdev = PCI_DEVICE(dev);
pbdev->fid = s390_pci_get_pfid(pci_dev);
pbdev->pdev = pci_dev;
pbdev->configured = true;
pbdev->fh = s390_pci_get_pfh(pci_dev);
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));
}
s390_pcihost_setup_msix(pbdev);
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");
}
}
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 (object_dynamic_cast(OBJECT(dev), "vfio-pci")) {
pbdev->fh |= FH_SHM_VFIO;
} else {
pbdev->fh |= FH_SHM_EMUL;
}
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);
}
} 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_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)
{
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 (pbdev->configured) {
pbdev->configured = false;
s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
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:
goto out;
case ZPCI_FS_STANDBY:
break;
default:
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,
pbdev->fh, pbdev->fid);
pbdev->fh = 0;
pbdev->fid = 0;
pbdev->pdev = NULL;
object_unparent(OBJECT(pci_dev));
pbdev->pdev = NULL;
pbdev->state = ZPCI_FS_RESERVED;
out:
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)
@ -652,9 +784,178 @@ 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 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)

View file

@ -21,16 +21,31 @@
#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 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
#define FH_MASK_INDEX 0x0000001f
#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 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)
#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
#define HP_EVENT_DECONFIGURE_REQUEST 0x0303
#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
#define HP_EVENT_STANDBY_TO_RESERVED 0x0308
@ -150,6 +165,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;
@ -214,14 +257,21 @@ typedef struct S390MsixInfo {
uint32_t pba_offset;
} S390MsixInfo;
typedef struct S390PCIIOMMU {
AddressSpace as;
MemoryRegion mr;
} S390PCIIOMMU;
typedef struct S390PCIBusDevice {
DeviceState qdev;
PCIDevice *pdev;
bool configured;
bool error_state;
bool lgstg_blocked;
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;
@ -231,16 +281,22 @@ typedef struct S390PCIBusDevice {
uint8_t sum;
S390MsixInfo msix;
AdapterRoutes routes;
AddressSpace as;
MemoryRegion mr;
S390PCIIOMMU *iommu;
MemoryRegion iommu_mr;
IndAddr *summary_ind;
IndAddr *indicator;
QEMUTimer *release_timer;
} S390PCIBusDevice;
typedef struct S390PCIBus {
BusState qbus;
} S390PCIBus;
typedef struct S390pciState {
PCIHostState parent_obj;
S390PCIBusDevice pbdev[PCI_SLOT_MAX];
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;
@ -252,8 +308,11 @@ 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);
S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev);
#endif

View file

@ -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;
@ -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;
@ -72,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) {
@ -91,43 +92,40 @@ 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.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));
stl_p(&rrb->response.fh_list[idx - resume_token].config,
pbdev->configured << 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);
/* Ignore RESERVED devices. */
stl_p(&rrb->response.fh_list[i].config,
pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31);
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);
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 (finish == 1) {
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);
@ -212,14 +210,35 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
switch (reqsetpci->oc) {
case CLP_SET_ENABLE_PCI_FN:
pbdev->fh = pbdev->fh | FH_ENABLED;
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:
pbdev->fh = pbdev->fh & ~FH_ENABLED;
pbdev->error_state = false;
pbdev->lgstg_blocked = false;
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);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
@ -256,9 +275,10 @@ 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);
stl_p(&resquery->uid, pbdev->uid);
stw_p(&resquery->hdr.rsp, CLP_RC_OK);
break;
}
@ -317,16 +337,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_ENABLED)) {
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) {
@ -390,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);
}
@ -434,16 +464,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_ENABLED)) {
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];
@ -525,18 +564,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_ENABLED)) {
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;
}
@ -589,16 +665,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_ENABLED)) {
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;
@ -742,12 +827,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_ENABLED)) {
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;
}
@ -814,11 +910,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);
@ -835,6 +945,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;
@ -847,19 +958,59 @@ 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;
}
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);
@ -872,22 +1023,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) {
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;
}

View file

@ -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;
@ -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

View file

@ -27,9 +27,10 @@
#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[] = {
"virtual-css-bridge",
TYPE_VIRTUAL_CSS_BRIDGE,
"s390-sclp-event-facility",
"s390-flic",
"diag288",

View file

@ -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;
@ -117,32 +97,13 @@ 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);
}
VirtualCssBus *virtual_css_bus_init(void)
{
VirtualCssBus *cbus;
BusState *bus;
DeviceState *dev;
/* Create bridge device */
dev = qdev_create(NULL, "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;
@ -234,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) {
@ -248,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,
@ -703,116 +666,28 @@ 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);
CcwDevice *ccw_dev = CCW_DEVICE(dev);
SubchDev *sch = css_create_virtual_sch(ccw_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;
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,
ccw_dev->bus_id.valid ? "user-configured" : "auto-configured");
if (k->realize) {
k->realize(dev, &err);
@ -820,19 +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);
goto out_err;
ccw_dev->sch = NULL;
g_free(sch);
}
return;
out_err:
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);
@ -1013,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,
@ -1042,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 */
@ -1100,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)
@ -1118,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);
}
@ -1126,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;
}
@ -1249,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) {
@ -1308,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);
@ -1342,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;
@ -1387,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) {
@ -1431,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,
@ -1460,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,
@ -1489,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,
@ -1518,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,
@ -1547,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,
@ -1577,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(),
@ -1615,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,
@ -1662,29 +1542,17 @@ static int virtio_ccw_busdev_exit(DeviceState *dev)
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;
@ -1692,44 +1560,13 @@ 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),
.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 = "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,
@ -1775,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,
@ -1824,7 +1661,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);
@ -1835,7 +1671,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

View file

@ -26,8 +26,8 @@
#include <hw/s390x/s390_flic.h>
#include <hw/s390x/css.h>
#define VIRTUAL_CSSID 0xfe
#include "ccw-device.h"
#include "hw/s390x/css-bridge.h"
#define VIRTIO_CCW_CU_TYPE 0x3832
#define VIRTIO_CCW_CHPID_TYPE 0x32
@ -67,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;
@ -78,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;
@ -103,15 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
return dev->max_rev;
}
/* 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)
/* virtio-scsi-ccw */
#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw"
@ -191,7 +180,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);

View file

@ -0,0 +1,31 @@
/*
* virtual css bridge definition
*
* Copyright 2012,2016 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#ifndef HW_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

View file

@ -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

View file

@ -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

Binary file not shown.

View file

@ -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)
{

View file

@ -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");
}

View file

@ -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 */

View file

@ -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;

View file

@ -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;