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:
commit
f1ef557866
@ -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
27
hw/s390x/ccw-device.c
Normal 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
43
hw/s390x/ccw-device.h
Normal 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
124
hw/s390x/css-bridge.c
Normal 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)
|
143
hw/s390x/css.c
143
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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
31
include/hw/s390x/css-bridge.h
Normal file
31
include/hw/s390x/css-bridge.h
Normal 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
|
@ -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
|
||||
|
@ -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.
@ -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)
|
||||
{
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user