aboutsummaryrefslogtreecommitdiff
path: root/hw/vfio/ccw.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/vfio/ccw.c')
-rw-r--r--hw/vfio/ccw.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index ca06213d03..f5a5e038aa 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -41,6 +41,9 @@ struct VFIOCCWDevice {
uint64_t async_cmd_region_size;
uint64_t async_cmd_region_offset;
struct ccw_cmd_region *async_cmd_region;
+ uint64_t schib_region_size;
+ uint64_t schib_region_offset;
+ struct ccw_schib_region *schib_region;
EventNotifier io_notifier;
bool force_orb_pfch;
bool warned_orb_pfch;
@@ -116,6 +119,51 @@ again:
}
}
+static IOInstEnding vfio_ccw_handle_store(SubchDev *sch)
+{
+ S390CCWDevice *cdev = sch->driver_data;
+ VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
+ SCHIB *schib = &sch->curr_status;
+ struct ccw_schib_region *region = vcdev->schib_region;
+ SCHIB *s;
+ int ret;
+
+ /* schib region not available so nothing else to do */
+ if (!region) {
+ return IOINST_CC_EXPECTED;
+ }
+
+ memset(region, 0, sizeof(*region));
+ ret = pread(vcdev->vdev.fd, region, vcdev->schib_region_size,
+ vcdev->schib_region_offset);
+
+ if (ret == -1) {
+ /*
+ * Device is probably damaged, but store subchannel does not
+ * have a nonzero cc defined for this scenario. Log an error,
+ * and presume things are otherwise fine.
+ */
+ error_report("vfio-ccw: store region read failed with errno=%d", errno);
+ return IOINST_CC_EXPECTED;
+ }
+
+ /*
+ * Selectively copy path-related bits of the SCHIB,
+ * rather than copying the entire struct.
+ */
+ s = (SCHIB *)region->schib_area;
+ schib->pmcw.pnom = s->pmcw.pnom;
+ schib->pmcw.lpum = s->pmcw.lpum;
+ schib->pmcw.pam = s->pmcw.pam;
+ schib->pmcw.pom = s->pmcw.pom;
+
+ if (s->scsw.flags & SCSW_FLAGS_MASK_PNO) {
+ schib->scsw.flags |= SCSW_FLAGS_MASK_PNO;
+ }
+
+ return IOINST_CC_EXPECTED;
+}
+
static int vfio_ccw_handle_clear(SubchDev *sch)
{
S390CCWDevice *cdev = sch->driver_data;
@@ -382,10 +430,23 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
vcdev->async_cmd_region = g_malloc0(info->size);
}
+ ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW,
+ VFIO_REGION_SUBTYPE_CCW_SCHIB, &info);
+ if (!ret) {
+ vcdev->schib_region_size = info->size;
+ if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) {
+ error_setg(errp, "vfio: Unexpected size of the schib region");
+ goto out_err;
+ }
+ vcdev->schib_region_offset = info->offset;
+ vcdev->schib_region = g_malloc(info->size);
+ }
+
g_free(info);
return;
out_err:
+ g_free(vcdev->schib_region);
g_free(vcdev->async_cmd_region);
g_free(vcdev->io_region);
g_free(info);
@@ -394,6 +455,7 @@ out_err:
static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
{
+ g_free(vcdev->schib_region);
g_free(vcdev->async_cmd_region);
g_free(vcdev->io_region);
}
@@ -569,6 +631,7 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data)
cdc->handle_request = vfio_ccw_handle_request;
cdc->handle_halt = vfio_ccw_handle_halt;
cdc->handle_clear = vfio_ccw_handle_clear;
+ cdc->handle_store = vfio_ccw_handle_store;
}
static const TypeInfo vfio_ccw_info = {