1a8eac943SDong Jia Shi /*
2a8eac943SDong Jia Shi * s390 CCW Assignment Support
3a8eac943SDong Jia Shi *
4a8eac943SDong Jia Shi * Copyright 2017 IBM Corp
5a8eac943SDong Jia Shi * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
6a8eac943SDong Jia Shi * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
7a8eac943SDong Jia Shi * Pierre Morel <pmorel@linux.vnet.ibm.com>
8a8eac943SDong Jia Shi *
9a8eac943SDong Jia Shi * This work is licensed under the terms of the GNU GPL, version 2
10a8eac943SDong Jia Shi * or (at your option) any later version. See the COPYING file in the
11a8eac943SDong Jia Shi * top-level directory.
12a8eac943SDong Jia Shi */
13d8e39b70SMarkus Armbruster
14a8eac943SDong Jia Shi #include "qemu/osdep.h"
15d8e39b70SMarkus Armbruster #include <libgen.h>
16a8eac943SDong Jia Shi #include "qapi/error.h"
170b8fa32fSMarkus Armbruster #include "qemu/module.h"
18a8eac943SDong Jia Shi #include "hw/s390x/css.h"
19a8eac943SDong Jia Shi #include "hw/s390x/css-bridge.h"
20a8eac943SDong Jia Shi #include "hw/s390x/s390-ccw.h"
212f780b6aSMarkus Armbruster #include "sysemu/sysemu.h"
22a8eac943SDong Jia Shi
s390_ccw_cmd_request(SubchDev * sch)2366dc50f7SHalil Pasic IOInstEnding s390_ccw_cmd_request(SubchDev *sch)
24bab482d7SXiao Feng Ren {
2566dc50f7SHalil Pasic S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
26bab482d7SXiao Feng Ren
2766dc50f7SHalil Pasic if (!cdc->handle_request) {
2866dc50f7SHalil Pasic return IOINST_CC_STATUS_PRESENT;
29bab482d7SXiao Feng Ren }
3066dc50f7SHalil Pasic return cdc->handle_request(sch);
31bab482d7SXiao Feng Ren }
32bab482d7SXiao Feng Ren
s390_ccw_halt(SubchDev * sch)338fadea24SCornelia Huck int s390_ccw_halt(SubchDev *sch)
348fadea24SCornelia Huck {
358fadea24SCornelia Huck S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
368fadea24SCornelia Huck
378fadea24SCornelia Huck if (!cdc->handle_halt) {
388fadea24SCornelia Huck return -ENOSYS;
398fadea24SCornelia Huck }
408fadea24SCornelia Huck return cdc->handle_halt(sch);
418fadea24SCornelia Huck }
428fadea24SCornelia Huck
s390_ccw_clear(SubchDev * sch)438fadea24SCornelia Huck int s390_ccw_clear(SubchDev *sch)
448fadea24SCornelia Huck {
458fadea24SCornelia Huck S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
468fadea24SCornelia Huck
478fadea24SCornelia Huck if (!cdc->handle_clear) {
488fadea24SCornelia Huck return -ENOSYS;
498fadea24SCornelia Huck }
508fadea24SCornelia Huck return cdc->handle_clear(sch);
518fadea24SCornelia Huck }
528fadea24SCornelia Huck
s390_ccw_store(SubchDev * sch)5346ea3841SFarhan Ali IOInstEnding s390_ccw_store(SubchDev *sch)
5446ea3841SFarhan Ali {
5546ea3841SFarhan Ali S390CCWDeviceClass *cdc = NULL;
5646ea3841SFarhan Ali int ret = IOINST_CC_EXPECTED;
5746ea3841SFarhan Ali
5846ea3841SFarhan Ali /*
5946ea3841SFarhan Ali * This code is called for both virtual and passthrough devices,
607a21bee2SDaniel P. Berrangé * but only applies to the latter. This ugly check makes that
6146ea3841SFarhan Ali * distinction for us.
6246ea3841SFarhan Ali */
6346ea3841SFarhan Ali if (object_dynamic_cast(OBJECT(sch->driver_data), TYPE_S390_CCW)) {
6446ea3841SFarhan Ali cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
6546ea3841SFarhan Ali }
6646ea3841SFarhan Ali
6746ea3841SFarhan Ali if (cdc && cdc->handle_store) {
6846ea3841SFarhan Ali ret = cdc->handle_store(sch);
6946ea3841SFarhan Ali }
7046ea3841SFarhan Ali
7146ea3841SFarhan Ali return ret;
7246ea3841SFarhan Ali }
7346ea3841SFarhan Ali
s390_ccw_get_dev_info(S390CCWDevice * cdev,char * sysfsdev,Error ** errp)749bf21277SCédric Le Goater static bool s390_ccw_get_dev_info(S390CCWDevice *cdev,
75a8eac943SDong Jia Shi char *sysfsdev,
76a8eac943SDong Jia Shi Error **errp)
77a8eac943SDong Jia Shi {
78a8eac943SDong Jia Shi unsigned int cssid, ssid, devid;
79469897edSZhao Liu char dev_path[PATH_MAX] = {0};
807af0cc14SZhao Liu g_autofree char *tmp_dir = NULL;
81469897edSZhao Liu g_autofree char *tmp = NULL;
82a8eac943SDong Jia Shi
83a8eac943SDong Jia Shi if (!sysfsdev) {
84a8eac943SDong Jia Shi error_setg(errp, "No host device provided");
85a8eac943SDong Jia Shi error_append_hint(errp,
86a8eac943SDong Jia Shi "Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n");
879bf21277SCédric Le Goater return false;
88a8eac943SDong Jia Shi }
89a8eac943SDong Jia Shi
90a8eac943SDong Jia Shi if (!realpath(sysfsdev, dev_path)) {
91a8eac943SDong Jia Shi error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev);
929bf21277SCédric Le Goater return false;
93a8eac943SDong Jia Shi }
94a8eac943SDong Jia Shi
953e015d81SJulia Suvorova cdev->mdevid = g_path_get_basename(dev_path);
96a8eac943SDong Jia Shi
977af0cc14SZhao Liu tmp_dir = g_path_get_dirname(dev_path);
987af0cc14SZhao Liu tmp = g_path_get_basename(tmp_dir);
99a8eac943SDong Jia Shi if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) {
100a8eac943SDong Jia Shi error_setg_errno(errp, errno, "Failed to read %s", tmp);
1019bf21277SCédric Le Goater return false;
102a8eac943SDong Jia Shi }
103a8eac943SDong Jia Shi
104a8eac943SDong Jia Shi cdev->hostid.cssid = cssid;
105a8eac943SDong Jia Shi cdev->hostid.ssid = ssid;
106a8eac943SDong Jia Shi cdev->hostid.devid = devid;
107a8eac943SDong Jia Shi cdev->hostid.valid = true;
1089bf21277SCédric Le Goater return true;
109a8eac943SDong Jia Shi }
110a8eac943SDong Jia Shi
s390_ccw_realize(S390CCWDevice * cdev,char * sysfsdev,Error ** errp)111*45f42187SCédric Le Goater static bool s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp)
112a8eac943SDong Jia Shi {
113a8eac943SDong Jia Shi CcwDevice *ccw_dev = CCW_DEVICE(cdev);
114a8eac943SDong Jia Shi CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev);
115a8eac943SDong Jia Shi DeviceState *parent = DEVICE(ccw_dev);
116a8eac943SDong Jia Shi SubchDev *sch;
117a8eac943SDong Jia Shi int ret;
118a8eac943SDong Jia Shi
11919a1740fSCédric Le Goater if (!s390_ccw_get_dev_info(cdev, sysfsdev, errp)) {
120*45f42187SCédric Le Goater return false;
121a8eac943SDong Jia Shi }
122a8eac943SDong Jia Shi
12319a1740fSCédric Le Goater sch = css_create_sch(ccw_dev->devno, errp);
124a8eac943SDong Jia Shi if (!sch) {
125a8eac943SDong Jia Shi goto out_mdevid_free;
126a8eac943SDong Jia Shi }
127a8eac943SDong Jia Shi sch->driver_data = cdev;
128bab482d7SXiao Feng Ren sch->do_subchannel_work = do_subchannel_work_passthrough;
129c626710fSEric Farman sch->irb_cb = build_irb_passthrough;
130a8eac943SDong Jia Shi
131a8eac943SDong Jia Shi ccw_dev->sch = sch;
132a8eac943SDong Jia Shi ret = css_sch_build_schib(sch, &cdev->hostid);
133a8eac943SDong Jia Shi if (ret) {
13419a1740fSCédric Le Goater error_setg_errno(errp, -ret, "%s: Failed to build initial schib",
135a8eac943SDong Jia Shi __func__);
136a8eac943SDong Jia Shi goto out_err;
137a8eac943SDong Jia Shi }
138a8eac943SDong Jia Shi
13919a1740fSCédric Le Goater if (!ck->realize(ccw_dev, errp)) {
140a8eac943SDong Jia Shi goto out_err;
141a8eac943SDong Jia Shi }
142a8eac943SDong Jia Shi
143a8eac943SDong Jia Shi css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
144a8eac943SDong Jia Shi parent->hotplugged, 1);
145*45f42187SCédric Le Goater return true;
146a8eac943SDong Jia Shi
147a8eac943SDong Jia Shi out_err:
148a8eac943SDong Jia Shi css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
149a8eac943SDong Jia Shi ccw_dev->sch = NULL;
150a8eac943SDong Jia Shi g_free(sch);
151a8eac943SDong Jia Shi out_mdevid_free:
152a8eac943SDong Jia Shi g_free(cdev->mdevid);
153*45f42187SCédric Le Goater return false;
154a8eac943SDong Jia Shi }
155a8eac943SDong Jia Shi
s390_ccw_unrealize(S390CCWDevice * cdev)156b69c3c21SMarkus Armbruster static void s390_ccw_unrealize(S390CCWDevice *cdev)
157a8eac943SDong Jia Shi {
158a8eac943SDong Jia Shi CcwDevice *ccw_dev = CCW_DEVICE(cdev);
159a8eac943SDong Jia Shi SubchDev *sch = ccw_dev->sch;
160a8eac943SDong Jia Shi
161a8eac943SDong Jia Shi if (sch) {
162a8eac943SDong Jia Shi css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
163a8eac943SDong Jia Shi g_free(sch);
164a8eac943SDong Jia Shi ccw_dev->sch = NULL;
165a8eac943SDong Jia Shi }
166a8eac943SDong Jia Shi
167a8eac943SDong Jia Shi g_free(cdev->mdevid);
168a8eac943SDong Jia Shi }
169a8eac943SDong Jia Shi
s390_ccw_instance_init(Object * obj)17044445d86SJason J. Herne static void s390_ccw_instance_init(Object *obj)
17144445d86SJason J. Herne {
17244445d86SJason J. Herne S390CCWDevice *dev = S390_CCW_DEVICE(obj);
17344445d86SJason J. Herne
17444445d86SJason J. Herne device_add_bootindex_property(obj, &dev->bootindex, "bootindex",
17540c2281cSMarkus Armbruster "/disk@0,0", DEVICE(obj));
17644445d86SJason J. Herne }
17744445d86SJason J. Herne
s390_ccw_class_init(ObjectClass * klass,void * data)178a8eac943SDong Jia Shi static void s390_ccw_class_init(ObjectClass *klass, void *data)
179a8eac943SDong Jia Shi {
180a8eac943SDong Jia Shi S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass);
181a8eac943SDong Jia Shi
182a8eac943SDong Jia Shi cdc->realize = s390_ccw_realize;
183a8eac943SDong Jia Shi cdc->unrealize = s390_ccw_unrealize;
184a8eac943SDong Jia Shi }
185a8eac943SDong Jia Shi
186a8eac943SDong Jia Shi static const TypeInfo s390_ccw_info = {
187a8eac943SDong Jia Shi .name = TYPE_S390_CCW,
188a8eac943SDong Jia Shi .parent = TYPE_CCW_DEVICE,
18944445d86SJason J. Herne .instance_init = s390_ccw_instance_init,
190a8eac943SDong Jia Shi .instance_size = sizeof(S390CCWDevice),
191a8eac943SDong Jia Shi .class_size = sizeof(S390CCWDeviceClass),
192a8eac943SDong Jia Shi .class_init = s390_ccw_class_init,
193a8eac943SDong Jia Shi .abstract = true,
194a8eac943SDong Jia Shi };
195a8eac943SDong Jia Shi
register_s390_ccw_type(void)196a8eac943SDong Jia Shi static void register_s390_ccw_type(void)
197a8eac943SDong Jia Shi {
198a8eac943SDong Jia Shi type_register_static(&s390_ccw_info);
199a8eac943SDong Jia Shi }
200a8eac943SDong Jia Shi
201a8eac943SDong Jia Shi type_init(register_s390_ccw_type)
202