1 | /* |
2 | * css bridge implementation |
3 | * |
4 | * Copyright 2012,2016 IBM Corp. |
5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * Pierre Morel <pmorel@linux.vnet.ibm.com> |
7 | * |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
9 | * your option) any later version. See the COPYING file in the top-level |
10 | * directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include "qapi/error.h" |
15 | #include "hw/hotplug.h" |
16 | #include "hw/qdev-properties.h" |
17 | #include "hw/sysbus.h" |
18 | #include "qemu/bitops.h" |
19 | #include "qemu/module.h" |
20 | #include "hw/s390x/css.h" |
21 | #include "ccw-device.h" |
22 | #include "hw/s390x/css-bridge.h" |
23 | #include "cpu.h" |
24 | |
25 | /* |
26 | * Invoke device-specific unplug handler, disable the subchannel |
27 | * (including sending a channel report to the guest) and remove the |
28 | * device from the virtual css bus. |
29 | */ |
30 | static void ccw_device_unplug(HotplugHandler *hotplug_dev, |
31 | DeviceState *dev, Error **errp) |
32 | { |
33 | CcwDevice *ccw_dev = CCW_DEVICE(dev); |
34 | CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev); |
35 | SubchDev *sch = ccw_dev->sch; |
36 | Error *err = NULL; |
37 | |
38 | if (k->unplug) { |
39 | k->unplug(hotplug_dev, dev, &err); |
40 | if (err) { |
41 | error_propagate(errp, err); |
42 | return; |
43 | } |
44 | } |
45 | |
46 | /* |
47 | * We should arrive here only for device_del, since we don't support |
48 | * direct hot(un)plug of channels. |
49 | */ |
50 | assert(sch != NULL); |
51 | /* Subchannel is now disabled and no longer valid. */ |
52 | sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | |
53 | PMCW_FLAGS_MASK_DNV); |
54 | |
55 | css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); |
56 | |
57 | object_property_set_bool(OBJECT(dev), false, "realized" , NULL); |
58 | } |
59 | |
60 | static void virtual_css_bus_reset(BusState *qbus) |
61 | { |
62 | /* This should actually be modelled via the generic css */ |
63 | css_reset(); |
64 | } |
65 | |
66 | static char *virtual_css_bus_get_dev_path(DeviceState *dev) |
67 | { |
68 | CcwDevice *ccw_dev = CCW_DEVICE(dev); |
69 | SubchDev *sch = ccw_dev->sch; |
70 | VirtualCssBridge *bridge = |
71 | VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent); |
72 | |
73 | /* |
74 | * We can't provide a dev path for backward compatibility on |
75 | * older machines, as it is visible in the migration stream. |
76 | */ |
77 | return bridge->css_dev_path ? |
78 | g_strdup_printf("/%02x.%1x.%04x" , sch->cssid, sch->ssid, sch->devno) : |
79 | NULL; |
80 | } |
81 | |
82 | static void virtual_css_bus_class_init(ObjectClass *klass, void *data) |
83 | { |
84 | BusClass *k = BUS_CLASS(klass); |
85 | |
86 | k->reset = virtual_css_bus_reset; |
87 | k->get_dev_path = virtual_css_bus_get_dev_path; |
88 | } |
89 | |
90 | static const TypeInfo virtual_css_bus_info = { |
91 | .name = TYPE_VIRTUAL_CSS_BUS, |
92 | .parent = TYPE_BUS, |
93 | .instance_size = sizeof(VirtualCssBus), |
94 | .class_init = virtual_css_bus_class_init, |
95 | }; |
96 | |
97 | VirtualCssBus *virtual_css_bus_init(void) |
98 | { |
99 | VirtualCssBus *cbus; |
100 | BusState *bus; |
101 | DeviceState *dev; |
102 | |
103 | /* Create bridge device */ |
104 | dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); |
105 | object_property_add_child(qdev_get_machine(), TYPE_VIRTUAL_CSS_BRIDGE, |
106 | OBJECT(dev), NULL); |
107 | qdev_init_nofail(dev); |
108 | |
109 | /* Create bus on bridge device */ |
110 | bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css" ); |
111 | cbus = VIRTUAL_CSS_BUS(bus); |
112 | |
113 | /* Enable hotplugging */ |
114 | qbus_set_hotplug_handler(bus, OBJECT(dev), &error_abort); |
115 | |
116 | css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, |
117 | 0, &error_abort); |
118 | |
119 | return cbus; |
120 | } |
121 | |
122 | /***************** Virtual-css Bus Bridge Device ********************/ |
123 | |
124 | static Property virtual_css_bridge_properties[] = { |
125 | DEFINE_PROP_BOOL("css_dev_path" , VirtualCssBridge, css_dev_path, |
126 | true), |
127 | DEFINE_PROP_END_OF_LIST(), |
128 | }; |
129 | |
130 | static bool prop_get_true(Object *obj, Error **errp) |
131 | { |
132 | return true; |
133 | } |
134 | |
135 | static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) |
136 | { |
137 | HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); |
138 | DeviceClass *dc = DEVICE_CLASS(klass); |
139 | |
140 | hc->unplug = ccw_device_unplug; |
141 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); |
142 | dc->props = virtual_css_bridge_properties; |
143 | object_class_property_add_bool(klass, "cssid-unrestricted" , |
144 | prop_get_true, NULL, NULL); |
145 | object_class_property_set_description(klass, "cssid-unrestricted" , |
146 | "A css device can use any cssid, regardless whether virtual" |
147 | " or not (read only, always true)" , |
148 | NULL); |
149 | } |
150 | |
151 | static const TypeInfo virtual_css_bridge_info = { |
152 | .name = TYPE_VIRTUAL_CSS_BRIDGE, |
153 | .parent = TYPE_SYS_BUS_DEVICE, |
154 | .instance_size = sizeof(VirtualCssBridge), |
155 | .class_init = virtual_css_bridge_class_init, |
156 | .interfaces = (InterfaceInfo[]) { |
157 | { TYPE_HOTPLUG_HANDLER }, |
158 | { } |
159 | } |
160 | }; |
161 | |
162 | static void virtual_css_register(void) |
163 | { |
164 | type_register_static(&virtual_css_bridge_info); |
165 | type_register_static(&virtual_css_bus_info); |
166 | } |
167 | |
168 | type_init(virtual_css_register) |
169 | |