root / hw / s390x / event-facility.c @ 477a72a1
History | View | Annotate | Download (11.4 kB)
1 |
/*
|
---|---|
2 |
* SCLP
|
3 |
* Event Facility
|
4 |
* handles SCLP event types
|
5 |
* - Signal Quiesce - system power down
|
6 |
* - ASCII Console Data - VT220 read and write
|
7 |
*
|
8 |
* Copyright IBM, Corp. 2012
|
9 |
*
|
10 |
* Authors:
|
11 |
* Heinz Graalfs <graalfs@de.ibm.com>
|
12 |
*
|
13 |
* This work is licensed under the terms of the GNU GPL, version 2 or (at your
|
14 |
* option) any later version. See the COPYING file in the top-level directory.
|
15 |
*
|
16 |
*/
|
17 |
|
18 |
#include "monitor/monitor.h" |
19 |
#include "sysemu/sysemu.h" |
20 |
|
21 |
#include "hw/s390x/sclp.h" |
22 |
#include "hw/s390x/event-facility.h" |
23 |
|
24 |
typedef struct SCLPEventsBus { |
25 |
BusState qbus; |
26 |
} SCLPEventsBus; |
27 |
|
28 |
struct SCLPEventFacility {
|
29 |
SysBusDevice parent_obj; |
30 |
SCLPEventsBus sbus; |
31 |
/* guest' receive mask */
|
32 |
unsigned int receive_mask; |
33 |
}; |
34 |
|
35 |
SCLPEvent cpu_hotplug; |
36 |
|
37 |
/* return true if any child has event pending set */
|
38 |
static bool event_pending(SCLPEventFacility *ef) |
39 |
{ |
40 |
BusChild *kid; |
41 |
SCLPEvent *event; |
42 |
SCLPEventClass *event_class; |
43 |
|
44 |
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { |
45 |
DeviceState *qdev = kid->child; |
46 |
event = DO_UPCAST(SCLPEvent, qdev, qdev); |
47 |
event_class = SCLP_EVENT_GET_CLASS(event); |
48 |
if (event->event_pending &&
|
49 |
event_class->get_send_mask() & ef->receive_mask) { |
50 |
return true; |
51 |
} |
52 |
} |
53 |
return false; |
54 |
} |
55 |
|
56 |
static unsigned int get_host_send_mask(SCLPEventFacility *ef) |
57 |
{ |
58 |
unsigned int mask; |
59 |
BusChild *kid; |
60 |
SCLPEventClass *child; |
61 |
|
62 |
mask = 0;
|
63 |
|
64 |
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { |
65 |
DeviceState *qdev = kid->child; |
66 |
child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); |
67 |
mask |= child->get_send_mask(); |
68 |
} |
69 |
return mask;
|
70 |
} |
71 |
|
72 |
static unsigned int get_host_receive_mask(SCLPEventFacility *ef) |
73 |
{ |
74 |
unsigned int mask; |
75 |
BusChild *kid; |
76 |
SCLPEventClass *child; |
77 |
|
78 |
mask = 0;
|
79 |
|
80 |
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { |
81 |
DeviceState *qdev = kid->child; |
82 |
child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); |
83 |
mask |= child->get_receive_mask(); |
84 |
} |
85 |
return mask;
|
86 |
} |
87 |
|
88 |
static uint16_t write_event_length_check(SCCB *sccb)
|
89 |
{ |
90 |
int slen;
|
91 |
unsigned elen = 0; |
92 |
EventBufferHeader *event; |
93 |
WriteEventData *wed = (WriteEventData *) sccb; |
94 |
|
95 |
event = (EventBufferHeader *) &wed->ebh; |
96 |
for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { |
97 |
elen = be16_to_cpu(event->length); |
98 |
if (elen < sizeof(*event) || elen > slen) { |
99 |
return SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR;
|
100 |
} |
101 |
event = (void *) event + elen;
|
102 |
} |
103 |
if (slen) {
|
104 |
return SCLP_RC_INCONSISTENT_LENGTHS;
|
105 |
} |
106 |
return SCLP_RC_NORMAL_COMPLETION;
|
107 |
} |
108 |
|
109 |
static uint16_t handle_write_event_buf(SCLPEventFacility *ef,
|
110 |
EventBufferHeader *event_buf, SCCB *sccb) |
111 |
{ |
112 |
uint16_t rc; |
113 |
BusChild *kid; |
114 |
SCLPEvent *event; |
115 |
SCLPEventClass *ec; |
116 |
|
117 |
rc = SCLP_RC_INVALID_FUNCTION; |
118 |
|
119 |
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { |
120 |
DeviceState *qdev = kid->child; |
121 |
event = (SCLPEvent *) qdev; |
122 |
ec = SCLP_EVENT_GET_CLASS(event); |
123 |
|
124 |
if (ec->write_event_data &&
|
125 |
ec->can_handle_event(event_buf->type)) { |
126 |
rc = ec->write_event_data(event, event_buf); |
127 |
break;
|
128 |
} |
129 |
} |
130 |
return rc;
|
131 |
} |
132 |
|
133 |
static uint16_t handle_sccb_write_events(SCLPEventFacility *ef, SCCB *sccb)
|
134 |
{ |
135 |
uint16_t rc; |
136 |
int slen;
|
137 |
unsigned elen = 0; |
138 |
EventBufferHeader *event_buf; |
139 |
WriteEventData *wed = (WriteEventData *) sccb; |
140 |
|
141 |
event_buf = &wed->ebh; |
142 |
rc = SCLP_RC_NORMAL_COMPLETION; |
143 |
|
144 |
/* loop over all contained event buffers */
|
145 |
for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { |
146 |
elen = be16_to_cpu(event_buf->length); |
147 |
|
148 |
/* in case of a previous error mark all trailing buffers
|
149 |
* as not accepted */
|
150 |
if (rc != SCLP_RC_NORMAL_COMPLETION) {
|
151 |
event_buf->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); |
152 |
} else {
|
153 |
rc = handle_write_event_buf(ef, event_buf, sccb); |
154 |
} |
155 |
event_buf = (void *) event_buf + elen;
|
156 |
} |
157 |
return rc;
|
158 |
} |
159 |
|
160 |
static void write_event_data(SCLPEventFacility *ef, SCCB *sccb) |
161 |
{ |
162 |
if (sccb->h.function_code != SCLP_FC_NORMAL_WRITE) {
|
163 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); |
164 |
goto out;
|
165 |
} |
166 |
if (be16_to_cpu(sccb->h.length) < 8) { |
167 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); |
168 |
goto out;
|
169 |
} |
170 |
/* first do a sanity check of the write events */
|
171 |
sccb->h.response_code = cpu_to_be16(write_event_length_check(sccb)); |
172 |
|
173 |
/* if no early error, then execute */
|
174 |
if (sccb->h.response_code == be16_to_cpu(SCLP_RC_NORMAL_COMPLETION)) {
|
175 |
sccb->h.response_code = |
176 |
cpu_to_be16(handle_sccb_write_events(ef, sccb)); |
177 |
} |
178 |
|
179 |
out:
|
180 |
return;
|
181 |
} |
182 |
|
183 |
static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
|
184 |
unsigned int mask) |
185 |
{ |
186 |
uint16_t rc; |
187 |
int slen;
|
188 |
unsigned elen;
|
189 |
BusChild *kid; |
190 |
SCLPEvent *event; |
191 |
SCLPEventClass *ec; |
192 |
EventBufferHeader *event_buf; |
193 |
ReadEventData *red = (ReadEventData *) sccb; |
194 |
|
195 |
event_buf = &red->ebh; |
196 |
event_buf->length = 0;
|
197 |
slen = sizeof(sccb->data);
|
198 |
|
199 |
rc = SCLP_RC_NO_EVENT_BUFFERS_STORED; |
200 |
|
201 |
QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { |
202 |
DeviceState *qdev = kid->child; |
203 |
event = (SCLPEvent *) qdev; |
204 |
ec = SCLP_EVENT_GET_CLASS(event); |
205 |
|
206 |
if (mask & ec->get_send_mask()) {
|
207 |
if (ec->read_event_data(event, event_buf, &slen)) {
|
208 |
elen = be16_to_cpu(event_buf->length); |
209 |
event_buf = (EventBufferHeader *) ((char *)event_buf + elen);
|
210 |
rc = SCLP_RC_NORMAL_COMPLETION; |
211 |
} |
212 |
} |
213 |
} |
214 |
|
215 |
if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { |
216 |
/* architecture suggests to reset variable-length-response bit */
|
217 |
sccb->h.control_mask[2] &= ~SCLP_VARIABLE_LENGTH_RESPONSE;
|
218 |
/* with a new length value */
|
219 |
sccb->h.length = cpu_to_be16(SCCB_SIZE - slen); |
220 |
} |
221 |
return rc;
|
222 |
} |
223 |
|
224 |
static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) |
225 |
{ |
226 |
unsigned int sclp_active_selection_mask; |
227 |
unsigned int sclp_cp_receive_mask; |
228 |
|
229 |
ReadEventData *red = (ReadEventData *) sccb; |
230 |
|
231 |
if (be16_to_cpu(sccb->h.length) != SCCB_SIZE) {
|
232 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); |
233 |
goto out;
|
234 |
} |
235 |
|
236 |
sclp_cp_receive_mask = ef->receive_mask; |
237 |
|
238 |
/* get active selection mask */
|
239 |
switch (sccb->h.function_code) {
|
240 |
case SCLP_UNCONDITIONAL_READ:
|
241 |
sclp_active_selection_mask = sclp_cp_receive_mask; |
242 |
break;
|
243 |
case SCLP_SELECTIVE_READ:
|
244 |
if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) {
|
245 |
sccb->h.response_code = |
246 |
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); |
247 |
goto out;
|
248 |
} |
249 |
sclp_active_selection_mask = be32_to_cpu(red->mask); |
250 |
break;
|
251 |
default:
|
252 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); |
253 |
goto out;
|
254 |
} |
255 |
sccb->h.response_code = cpu_to_be16( |
256 |
handle_sccb_read_events(ef, sccb, sclp_active_selection_mask)); |
257 |
|
258 |
out:
|
259 |
return;
|
260 |
} |
261 |
|
262 |
static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) |
263 |
{ |
264 |
WriteEventMask *we_mask = (WriteEventMask *) sccb; |
265 |
|
266 |
/* Attention: We assume that Linux uses 4-byte masks, what it actually
|
267 |
does. Architecture allows for masks of variable size, though */
|
268 |
if (be16_to_cpu(we_mask->mask_length) != 4) { |
269 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH); |
270 |
goto out;
|
271 |
} |
272 |
|
273 |
/* keep track of the guest's capability masks */
|
274 |
ef->receive_mask = be32_to_cpu(we_mask->cp_receive_mask); |
275 |
|
276 |
/* return the SCLP's capability masks to the guest */
|
277 |
we_mask->send_mask = cpu_to_be32(get_host_send_mask(ef)); |
278 |
we_mask->receive_mask = cpu_to_be32(get_host_receive_mask(ef)); |
279 |
|
280 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); |
281 |
|
282 |
out:
|
283 |
return;
|
284 |
} |
285 |
|
286 |
/* qemu object creation and initialization functions */
|
287 |
|
288 |
#define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus" |
289 |
|
290 |
static void sclp_events_bus_class_init(ObjectClass *klass, void *data) |
291 |
{ |
292 |
} |
293 |
|
294 |
static const TypeInfo sclp_events_bus_info = { |
295 |
.name = TYPE_SCLP_EVENTS_BUS, |
296 |
.parent = TYPE_BUS, |
297 |
.class_init = sclp_events_bus_class_init, |
298 |
}; |
299 |
|
300 |
static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) |
301 |
{ |
302 |
switch (code & SCLP_CMD_CODE_MASK) {
|
303 |
case SCLP_CMD_READ_EVENT_DATA:
|
304 |
read_event_data(ef, sccb); |
305 |
break;
|
306 |
case SCLP_CMD_WRITE_EVENT_DATA:
|
307 |
write_event_data(ef, sccb); |
308 |
break;
|
309 |
case SCLP_CMD_WRITE_EVENT_MASK:
|
310 |
write_event_mask(ef, sccb); |
311 |
break;
|
312 |
default:
|
313 |
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); |
314 |
break;
|
315 |
} |
316 |
} |
317 |
|
318 |
static int init_event_facility(SCLPEventFacility *event_facility) |
319 |
{ |
320 |
DeviceState *sdev = DEVICE(event_facility); |
321 |
DeviceState *quiesce; |
322 |
|
323 |
/* Spawn a new bus for SCLP events */
|
324 |
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
|
325 |
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
|
326 |
event_facility->sbus.qbus.allow_hotplug = 0;
|
327 |
|
328 |
quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
|
329 |
if (!quiesce) {
|
330 |
return -1; |
331 |
} |
332 |
qdev_init_nofail(quiesce); |
333 |
|
334 |
object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG);
|
335 |
qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus)); |
336 |
object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL); |
337 |
|
338 |
return 0; |
339 |
} |
340 |
|
341 |
static void reset_event_facility(DeviceState *dev) |
342 |
{ |
343 |
SCLPEventFacility *sdev = EVENT_FACILITY(dev); |
344 |
|
345 |
sdev->receive_mask = 0;
|
346 |
} |
347 |
|
348 |
static void init_event_facility_class(ObjectClass *klass, void *data) |
349 |
{ |
350 |
SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); |
351 |
DeviceClass *dc = DEVICE_CLASS(sbdc); |
352 |
SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc); |
353 |
|
354 |
dc->reset = reset_event_facility; |
355 |
k->init = init_event_facility; |
356 |
k->command_handler = command_handler; |
357 |
k->event_pending = event_pending; |
358 |
} |
359 |
|
360 |
static const TypeInfo sclp_event_facility_info = { |
361 |
.name = TYPE_SCLP_EVENT_FACILITY, |
362 |
.parent = TYPE_SYS_BUS_DEVICE, |
363 |
.instance_size = sizeof(SCLPEventFacility),
|
364 |
.class_init = init_event_facility_class, |
365 |
.class_size = sizeof(SCLPEventFacilityClass),
|
366 |
}; |
367 |
|
368 |
static int event_qdev_init(DeviceState *qdev) |
369 |
{ |
370 |
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev); |
371 |
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); |
372 |
|
373 |
return child->init(event);
|
374 |
} |
375 |
|
376 |
static int event_qdev_exit(DeviceState *qdev) |
377 |
{ |
378 |
SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev); |
379 |
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); |
380 |
if (child->exit) {
|
381 |
child->exit(event); |
382 |
} |
383 |
return 0; |
384 |
} |
385 |
|
386 |
static void event_class_init(ObjectClass *klass, void *data) |
387 |
{ |
388 |
DeviceClass *dc = DEVICE_CLASS(klass); |
389 |
|
390 |
dc->bus_type = TYPE_SCLP_EVENTS_BUS; |
391 |
dc->unplug = qdev_simple_unplug_cb; |
392 |
dc->init = event_qdev_init; |
393 |
dc->exit = event_qdev_exit; |
394 |
} |
395 |
|
396 |
static const TypeInfo sclp_event_type_info = { |
397 |
.name = TYPE_SCLP_EVENT, |
398 |
.parent = TYPE_DEVICE, |
399 |
.instance_size = sizeof(SCLPEvent),
|
400 |
.class_init = event_class_init, |
401 |
.class_size = sizeof(SCLPEventClass),
|
402 |
.abstract = true,
|
403 |
}; |
404 |
|
405 |
static void register_types(void) |
406 |
{ |
407 |
type_register_static(&sclp_events_bus_info); |
408 |
type_register_static(&sclp_event_facility_info); |
409 |
type_register_static(&sclp_event_type_info); |
410 |
} |
411 |
|
412 |
type_init(register_types) |