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