]>
Commit | Line | Data |
---|---|---|
a8eac943 DJS |
1 | /* |
2 | * s390 CCW Assignment Support | |
3 | * | |
4 | * Copyright 2017 IBM Corp | |
5 | * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> | |
6 | * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> | |
7 | * Pierre Morel <pmorel@linux.vnet.ibm.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 | |
10 | * or (at your option) any later version. See the COPYING file in the | |
11 | * top-level directory. | |
12 | */ | |
d8e39b70 | 13 | |
a8eac943 | 14 | #include "qemu/osdep.h" |
d8e39b70 | 15 | #include <libgen.h> |
a8eac943 | 16 | #include "qapi/error.h" |
0b8fa32f | 17 | #include "qemu/module.h" |
a8eac943 | 18 | #include "hw/sysbus.h" |
a8eac943 DJS |
19 | #include "hw/s390x/css.h" |
20 | #include "hw/s390x/css-bridge.h" | |
21 | #include "hw/s390x/s390-ccw.h" | |
22 | ||
66dc50f7 | 23 | IOInstEnding s390_ccw_cmd_request(SubchDev *sch) |
bab482d7 | 24 | { |
66dc50f7 | 25 | S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data); |
bab482d7 | 26 | |
66dc50f7 HP |
27 | if (!cdc->handle_request) { |
28 | return IOINST_CC_STATUS_PRESENT; | |
bab482d7 | 29 | } |
66dc50f7 | 30 | return cdc->handle_request(sch); |
bab482d7 XFR |
31 | } |
32 | ||
a8eac943 DJS |
33 | static void s390_ccw_get_dev_info(S390CCWDevice *cdev, |
34 | char *sysfsdev, | |
35 | Error **errp) | |
36 | { | |
37 | unsigned int cssid, ssid, devid; | |
38 | char dev_path[PATH_MAX] = {0}, *tmp; | |
39 | ||
40 | if (!sysfsdev) { | |
41 | error_setg(errp, "No host device provided"); | |
42 | error_append_hint(errp, | |
43 | "Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n"); | |
44 | return; | |
45 | } | |
46 | ||
47 | if (!realpath(sysfsdev, dev_path)) { | |
48 | error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev); | |
49 | return; | |
50 | } | |
51 | ||
3e015d81 | 52 | cdev->mdevid = g_path_get_basename(dev_path); |
a8eac943 DJS |
53 | |
54 | tmp = basename(dirname(dev_path)); | |
55 | if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) { | |
56 | error_setg_errno(errp, errno, "Failed to read %s", tmp); | |
57 | return; | |
58 | } | |
59 | ||
60 | cdev->hostid.cssid = cssid; | |
61 | cdev->hostid.ssid = ssid; | |
62 | cdev->hostid.devid = devid; | |
63 | cdev->hostid.valid = true; | |
64 | } | |
65 | ||
66 | static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) | |
67 | { | |
68 | CcwDevice *ccw_dev = CCW_DEVICE(cdev); | |
69 | CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); | |
70 | DeviceState *parent = DEVICE(ccw_dev); | |
a8eac943 DJS |
71 | SubchDev *sch; |
72 | int ret; | |
73 | Error *err = NULL; | |
74 | ||
75 | s390_ccw_get_dev_info(cdev, sysfsdev, &err); | |
76 | if (err) { | |
77 | goto out_err_propagate; | |
78 | } | |
79 | ||
36699ab4 | 80 | sch = css_create_sch(ccw_dev->devno, &err); |
a8eac943 DJS |
81 | if (!sch) { |
82 | goto out_mdevid_free; | |
83 | } | |
84 | sch->driver_data = cdev; | |
bab482d7 | 85 | sch->do_subchannel_work = do_subchannel_work_passthrough; |
a8eac943 DJS |
86 | |
87 | ccw_dev->sch = sch; | |
88 | ret = css_sch_build_schib(sch, &cdev->hostid); | |
89 | if (ret) { | |
90 | error_setg_errno(&err, -ret, "%s: Failed to build initial schib", | |
91 | __func__); | |
92 | goto out_err; | |
93 | } | |
94 | ||
95 | ck->realize(ccw_dev, &err); | |
96 | if (err) { | |
97 | goto out_err; | |
98 | } | |
99 | ||
100 | css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, | |
101 | parent->hotplugged, 1); | |
102 | return; | |
103 | ||
104 | out_err: | |
105 | css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); | |
106 | ccw_dev->sch = NULL; | |
107 | g_free(sch); | |
108 | out_mdevid_free: | |
109 | g_free(cdev->mdevid); | |
110 | out_err_propagate: | |
111 | error_propagate(errp, err); | |
112 | } | |
113 | ||
114 | static void s390_ccw_unrealize(S390CCWDevice *cdev, Error **errp) | |
115 | { | |
116 | CcwDevice *ccw_dev = CCW_DEVICE(cdev); | |
117 | SubchDev *sch = ccw_dev->sch; | |
118 | ||
119 | if (sch) { | |
120 | css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); | |
121 | g_free(sch); | |
122 | ccw_dev->sch = NULL; | |
123 | } | |
124 | ||
125 | g_free(cdev->mdevid); | |
126 | } | |
127 | ||
44445d86 JH |
128 | static void s390_ccw_instance_init(Object *obj) |
129 | { | |
130 | S390CCWDevice *dev = S390_CCW_DEVICE(obj); | |
131 | ||
132 | device_add_bootindex_property(obj, &dev->bootindex, "bootindex", | |
133 | "/disk@0,0", DEVICE(obj), NULL); | |
134 | } | |
135 | ||
a8eac943 DJS |
136 | static void s390_ccw_class_init(ObjectClass *klass, void *data) |
137 | { | |
138 | DeviceClass *dc = DEVICE_CLASS(klass); | |
139 | S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); | |
140 | ||
141 | dc->bus_type = TYPE_VIRTUAL_CSS_BUS; | |
142 | cdc->realize = s390_ccw_realize; | |
143 | cdc->unrealize = s390_ccw_unrealize; | |
144 | } | |
145 | ||
146 | static const TypeInfo s390_ccw_info = { | |
147 | .name = TYPE_S390_CCW, | |
148 | .parent = TYPE_CCW_DEVICE, | |
44445d86 | 149 | .instance_init = s390_ccw_instance_init, |
a8eac943 DJS |
150 | .instance_size = sizeof(S390CCWDevice), |
151 | .class_size = sizeof(S390CCWDeviceClass), | |
152 | .class_init = s390_ccw_class_init, | |
153 | .abstract = true, | |
154 | }; | |
155 | ||
156 | static void register_s390_ccw_type(void) | |
157 | { | |
158 | type_register_static(&s390_ccw_info); | |
159 | } | |
160 | ||
161 | type_init(register_s390_ccw_type) |