mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
+32
-5
@@ -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)
|
||||
{
|
||||
|
||||
+443
-142
File diff suppressed because it is too large
Load Diff
+68
-9
@@ -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
|
||||
|
||||
+200
-64
@@ -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",
|
||||
|
||||
+53
-218
@@ -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
|
||||
|
||||
+4
-16
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user