xref: /openbmc/qemu/hw/s390x/s390-ccw.c (revision d89b64beea65f77c21a553cb54cb97b75c53dc21)
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