66dc50f705
Simplify the error handling of the SSCH and RSCH handler avoiding arbitrary and cryptic error codes being used to tell how the instruction is supposed to end. Let the code detecting the condition tell how it's to be handled in a less ambiguous way. It's best to handle SSCH and RSCH in one go as the emulation of the two shares a lot of code. For passthrough this change isn't pure refactoring, but changes the way kernel reported EFAULT is handled. After clarifying the kernel interface we decided that EFAULT shall be mapped to unit exception. Same goes for unexpected error codes and absence of required ORB flags. Signed-off-by: Halil Pasic <pasic@linux.vnet.ibm.com> Message-Id: <20171017140453.51099-4-pasic@linux.vnet.ibm.com> Tested-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> [CH: cosmetic changes] Reviewed-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cohuck@redhat.com>
153 lines
4.1 KiB
C
153 lines
4.1 KiB
C
/*
|
|
* s390 CCW Assignment Support
|
|
*
|
|
* Copyright 2017 IBM Corp
|
|
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
|
* Xiao Feng Ren <renxiaof@linux.vnet.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/sysbus.h"
|
|
#include "libgen.h"
|
|
#include "hw/s390x/css.h"
|
|
#include "hw/s390x/css-bridge.h"
|
|
#include "hw/s390x/s390-ccw.h"
|
|
|
|
IOInstEnding s390_ccw_cmd_request(SubchDev *sch)
|
|
{
|
|
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
|
|
|
|
if (!cdc->handle_request) {
|
|
return IOINST_CC_STATUS_PRESENT;
|
|
}
|
|
return cdc->handle_request(sch);
|
|
}
|
|
|
|
static void s390_ccw_get_dev_info(S390CCWDevice *cdev,
|
|
char *sysfsdev,
|
|
Error **errp)
|
|
{
|
|
unsigned int cssid, ssid, devid;
|
|
char dev_path[PATH_MAX] = {0}, *tmp;
|
|
|
|
if (!sysfsdev) {
|
|
error_setg(errp, "No host device provided");
|
|
error_append_hint(errp,
|
|
"Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n");
|
|
return;
|
|
}
|
|
|
|
if (!realpath(sysfsdev, dev_path)) {
|
|
error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev);
|
|
return;
|
|
}
|
|
|
|
cdev->mdevid = g_strdup(basename(dev_path));
|
|
|
|
tmp = basename(dirname(dev_path));
|
|
if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) {
|
|
error_setg_errno(errp, errno, "Failed to read %s", tmp);
|
|
return;
|
|
}
|
|
|
|
cdev->hostid.cssid = cssid;
|
|
cdev->hostid.ssid = ssid;
|
|
cdev->hostid.devid = devid;
|
|
cdev->hostid.valid = true;
|
|
}
|
|
|
|
static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp)
|
|
{
|
|
CcwDevice *ccw_dev = CCW_DEVICE(cdev);
|
|
CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev);
|
|
DeviceState *parent = DEVICE(ccw_dev);
|
|
BusState *qbus = qdev_get_parent_bus(parent);
|
|
VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus);
|
|
SubchDev *sch;
|
|
int ret;
|
|
Error *err = NULL;
|
|
|
|
s390_ccw_get_dev_info(cdev, sysfsdev, &err);
|
|
if (err) {
|
|
goto out_err_propagate;
|
|
}
|
|
|
|
sch = css_create_sch(ccw_dev->devno, false, cbus->squash_mcss, &err);
|
|
if (!sch) {
|
|
goto out_mdevid_free;
|
|
}
|
|
sch->driver_data = cdev;
|
|
sch->do_subchannel_work = do_subchannel_work_passthrough;
|
|
|
|
ccw_dev->sch = sch;
|
|
ret = css_sch_build_schib(sch, &cdev->hostid);
|
|
if (ret) {
|
|
error_setg_errno(&err, -ret, "%s: Failed to build initial schib",
|
|
__func__);
|
|
goto out_err;
|
|
}
|
|
|
|
ck->realize(ccw_dev, &err);
|
|
if (err) {
|
|
goto out_err;
|
|
}
|
|
|
|
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
|
|
parent->hotplugged, 1);
|
|
return;
|
|
|
|
out_err:
|
|
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
|
ccw_dev->sch = NULL;
|
|
g_free(sch);
|
|
out_mdevid_free:
|
|
g_free(cdev->mdevid);
|
|
out_err_propagate:
|
|
error_propagate(errp, err);
|
|
}
|
|
|
|
static void s390_ccw_unrealize(S390CCWDevice *cdev, Error **errp)
|
|
{
|
|
CcwDevice *ccw_dev = CCW_DEVICE(cdev);
|
|
SubchDev *sch = ccw_dev->sch;
|
|
|
|
if (sch) {
|
|
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
|
g_free(sch);
|
|
ccw_dev->sch = NULL;
|
|
}
|
|
|
|
g_free(cdev->mdevid);
|
|
}
|
|
|
|
static void s390_ccw_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass);
|
|
|
|
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
|
|
cdc->realize = s390_ccw_realize;
|
|
cdc->unrealize = s390_ccw_unrealize;
|
|
}
|
|
|
|
static const TypeInfo s390_ccw_info = {
|
|
.name = TYPE_S390_CCW,
|
|
.parent = TYPE_CCW_DEVICE,
|
|
.instance_size = sizeof(S390CCWDevice),
|
|
.class_size = sizeof(S390CCWDeviceClass),
|
|
.class_init = s390_ccw_class_init,
|
|
.abstract = true,
|
|
};
|
|
|
|
static void register_s390_ccw_type(void)
|
|
{
|
|
type_register_static(&s390_ccw_info);
|
|
}
|
|
|
|
type_init(register_s390_ccw_type)
|