]>
Commit | Line | Data |
---|---|---|
6a444f85 HG |
1 | /* |
2 | * SCLP event types | |
3 | * Operations Command - Line Mode input | |
4 | * Message - Line Mode output | |
5 | * | |
6 | * Copyright IBM, Corp. 2013 | |
7 | * | |
8 | * Authors: | |
9 | * Heinz Graalfs <graalfs@linux.vnet.ibm.com> | |
10 | * | |
11 | * This work is licensed under the terms of the GNU GPL, version 2 or (at your | |
12 | * option) any later version. See the COPYING file in the top-level directory. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include "hw/qdev.h" | |
17 | #include "qemu/thread.h" | |
18 | #include "qemu/error-report.h" | |
19 | #include "sysemu/char.h" | |
20 | ||
21 | #include "hw/s390x/sclp.h" | |
22 | #include "hw/s390x/event-facility.h" | |
23 | #include "hw/s390x/ebcdic.h" | |
24 | ||
25 | #define SIZE_BUFFER 4096 | |
26 | #define NEWLINE "\n" | |
27 | ||
28 | typedef struct OprtnsCommand { | |
29 | EventBufferHeader header; | |
30 | MDMSU message_unit; | |
31 | char data[0]; | |
32 | } QEMU_PACKED OprtnsCommand; | |
33 | ||
34 | /* max size for line-mode data in 4K SCCB page */ | |
35 | #define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) | |
36 | ||
37 | typedef struct SCLPConsoleLM { | |
38 | SCLPEvent event; | |
39 | CharDriverState *chr; | |
40 | bool echo; /* immediate echo of input if true */ | |
41 | uint32_t write_errors; /* errors writing to char layer */ | |
42 | uint32_t length; /* length of byte stream in buffer */ | |
43 | uint8_t buf[SIZE_CONSOLE_BUFFER]; | |
44 | qemu_irq irq_console_read; | |
45 | } SCLPConsoleLM; | |
46 | ||
47 | /* | |
48 | * Character layer call-back functions | |
49 | * | |
50 | * Allow 1 character at a time | |
51 | * | |
52 | * Accumulate bytes from character layer in console buffer, | |
53 | * event_pending is set when a newline character is encountered | |
54 | * | |
55 | * The maximum command line length is limited by the maximum | |
56 | * space available in an SCCB | |
57 | */ | |
58 | ||
59 | static int chr_can_read(void *opaque) | |
60 | { | |
61 | SCLPConsoleLM *scon = opaque; | |
62 | ||
63 | if (scon->event.event_pending) { | |
64 | return 0; | |
65 | } else if (SIZE_CONSOLE_BUFFER - scon->length) { | |
66 | return 1; | |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | static void receive_from_chr_layer(SCLPConsoleLM *scon, const uint8_t *buf, | |
72 | int size) | |
73 | { | |
74 | assert(size == 1); | |
75 | ||
76 | if (*buf == '\r' || *buf == '\n') { | |
77 | scon->event.event_pending = true; | |
78 | return; | |
79 | } | |
80 | scon->buf[scon->length] = *buf; | |
81 | scon->length += 1; | |
82 | if (scon->echo) { | |
83 | qemu_chr_fe_write(scon->chr, buf, size); | |
84 | } | |
85 | } | |
86 | ||
87 | /* | |
88 | * Send data from a char device over to the guest | |
89 | */ | |
90 | static void chr_read(void *opaque, const uint8_t *buf, int size) | |
91 | { | |
92 | SCLPConsoleLM *scon = opaque; | |
93 | ||
94 | receive_from_chr_layer(scon, buf, size); | |
95 | if (scon->event.event_pending) { | |
96 | /* trigger SCLP read operation */ | |
97 | qemu_irq_raise(scon->irq_console_read); | |
98 | } | |
99 | } | |
100 | ||
101 | /* functions to be called by event facility */ | |
102 | ||
103 | static bool can_handle_event(uint8_t type) | |
104 | { | |
105 | return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; | |
106 | } | |
107 | ||
108 | static unsigned int send_mask(void) | |
109 | { | |
110 | return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; | |
111 | } | |
112 | ||
113 | static unsigned int receive_mask(void) | |
114 | { | |
115 | return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Triggered by SCLP's read_event_data | |
120 | * - convert ASCII byte stream to EBCDIC and | |
121 | * - copy converted data into provided (SCLP) buffer | |
122 | */ | |
123 | static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, | |
124 | int avail) | |
125 | { | |
126 | int len; | |
127 | ||
128 | SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); | |
129 | ||
130 | len = cons->length; | |
131 | /* data need to fit into provided SCLP buffer */ | |
132 | if (len > avail) { | |
133 | return 1; | |
134 | } | |
135 | ||
136 | ebcdic_put(buf, (char *)&cons->buf, len); | |
137 | *size = len; | |
138 | cons->length = 0; | |
139 | /* data provided and no more data pending */ | |
140 | event->event_pending = false; | |
141 | return 0; | |
142 | } | |
143 | ||
144 | static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, | |
145 | int *slen) | |
146 | { | |
147 | int avail, rc; | |
148 | size_t src_len; | |
149 | uint8_t *to; | |
150 | OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; | |
151 | ||
152 | if (!event->event_pending) { | |
153 | /* no data pending */ | |
154 | return 0; | |
155 | } | |
156 | ||
157 | to = (uint8_t *)&oc->data; | |
158 | avail = *slen - sizeof(OprtnsCommand); | |
159 | rc = get_console_data(event, to, &src_len, avail); | |
160 | if (rc) { | |
161 | /* data didn't fit, try next SCCB */ | |
162 | return 1; | |
163 | } | |
164 | ||
165 | oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; | |
166 | oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); | |
167 | ||
168 | oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; | |
169 | oc->message_unit.cpmsu.length = | |
170 | cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); | |
171 | ||
172 | oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; | |
173 | oc->message_unit.text_command.length = | |
174 | cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); | |
175 | ||
176 | oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; | |
177 | oc->message_unit.self_def_text_message.length = | |
178 | cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); | |
179 | ||
180 | oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; | |
181 | oc->message_unit.text_message.length = | |
182 | cpu_to_be16(sizeof(GdsSubvector) + src_len); | |
183 | ||
184 | oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); | |
185 | oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; | |
186 | *slen = avail - src_len; | |
187 | ||
188 | return 1; | |
189 | } | |
190 | ||
191 | /* | |
192 | * Triggered by SCLP's write_event_data | |
193 | * - write console data to character layer | |
194 | * returns < 0 if an error occurred | |
195 | */ | |
196 | static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) | |
197 | { | |
198 | int ret = 0; | |
199 | const uint8_t *buf_offset; | |
200 | ||
201 | SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); | |
202 | ||
203 | if (!scon->chr) { | |
204 | /* If there's no backend, we can just say we consumed all data. */ | |
205 | return len; | |
206 | } | |
207 | ||
208 | buf_offset = buf; | |
209 | while (len > 0) { | |
210 | ret = qemu_chr_fe_write(scon->chr, buf, len); | |
211 | if (ret == 0) { | |
212 | /* a pty doesn't seem to be connected - no error */ | |
213 | len = 0; | |
214 | } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { | |
215 | len -= ret; | |
216 | buf_offset += ret; | |
217 | } else { | |
218 | len = 0; | |
219 | } | |
220 | } | |
221 | ||
222 | return ret; | |
223 | } | |
224 | ||
225 | static int process_mdb(SCLPEvent *event, MDBO *mdbo) | |
226 | { | |
227 | int rc; | |
228 | int len; | |
229 | uint8_t buffer[SIZE_BUFFER]; | |
230 | ||
231 | len = be16_to_cpu(mdbo->length); | |
232 | len -= sizeof(mdbo->length) + sizeof(mdbo->type) | |
233 | + sizeof(mdbo->mto.line_type_flags) | |
234 | + sizeof(mdbo->mto.alarm_control) | |
235 | + sizeof(mdbo->mto._reserved); | |
236 | ||
237 | assert(len <= SIZE_BUFFER); | |
238 | ||
239 | /* convert EBCDIC SCLP contents to ASCII console message */ | |
240 | ascii_put(buffer, mdbo->mto.message, len); | |
241 | rc = write_console_data(event, (uint8_t *)NEWLINE, 1); | |
242 | if (rc < 0) { | |
243 | return rc; | |
244 | } | |
245 | return write_console_data(event, buffer, len); | |
246 | } | |
247 | ||
248 | static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) | |
249 | { | |
250 | int len; | |
251 | int written; | |
252 | int errors = 0; | |
253 | MDBO *mdbo; | |
254 | SclpMsg *data = (SclpMsg *) ebh; | |
255 | SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); | |
256 | ||
257 | len = be16_to_cpu(data->mdb.header.length); | |
258 | if (len < sizeof(data->mdb.header)) { | |
259 | return SCLP_RC_INCONSISTENT_LENGTHS; | |
260 | } | |
261 | len -= sizeof(data->mdb.header); | |
262 | ||
263 | /* first check message buffers */ | |
264 | mdbo = data->mdb.mdbo; | |
265 | while (len > 0) { | |
266 | if (be16_to_cpu(mdbo->length) > len | |
267 | || be16_to_cpu(mdbo->length) == 0) { | |
268 | return SCLP_RC_INCONSISTENT_LENGTHS; | |
269 | } | |
270 | len -= be16_to_cpu(mdbo->length); | |
271 | mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | |
272 | } | |
273 | ||
274 | /* then execute */ | |
275 | len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); | |
276 | mdbo = data->mdb.mdbo; | |
277 | while (len > 0) { | |
278 | switch (be16_to_cpu(mdbo->type)) { | |
279 | case MESSAGE_TEXT: | |
280 | /* message text object */ | |
281 | written = process_mdb(event, mdbo); | |
282 | if (written < 0) { | |
283 | /* character layer error */ | |
284 | errors++; | |
285 | } | |
286 | break; | |
287 | default: /* ignore */ | |
288 | break; | |
289 | } | |
290 | len -= be16_to_cpu(mdbo->length); | |
291 | mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | |
292 | } | |
293 | if (errors) { | |
294 | scon->write_errors += errors; | |
295 | } | |
296 | data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; | |
297 | ||
298 | return SCLP_RC_NORMAL_COMPLETION; | |
299 | } | |
300 | ||
301 | static void trigger_console_data(void *opaque, int n, int level) | |
302 | { | |
303 | sclp_service_interrupt(0); | |
304 | } | |
305 | ||
306 | /* functions for live migration */ | |
307 | ||
308 | static const VMStateDescription vmstate_sclplmconsole = { | |
309 | .name = "sclplmconsole", | |
310 | .version_id = 0, | |
311 | .minimum_version_id = 0, | |
312 | .minimum_version_id_old = 0, | |
313 | .fields = (VMStateField[]) { | |
314 | VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), | |
315 | VMSTATE_UINT32(write_errors, SCLPConsoleLM), | |
316 | VMSTATE_UINT32(length, SCLPConsoleLM), | |
317 | VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), | |
318 | VMSTATE_END_OF_LIST() | |
319 | } | |
320 | }; | |
321 | ||
322 | /* qemu object creation and initialization functions */ | |
323 | ||
324 | /* tell character layer our call-back functions */ | |
325 | ||
326 | static int console_init(SCLPEvent *event) | |
327 | { | |
328 | static bool console_available; | |
329 | ||
330 | SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); | |
331 | ||
332 | if (console_available) { | |
333 | error_report("Multiple line-mode operator consoles are not supported"); | |
334 | return -1; | |
335 | } | |
336 | console_available = true; | |
337 | ||
338 | if (scon->chr) { | |
339 | qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); | |
340 | } | |
341 | scon->irq_console_read = *qemu_allocate_irqs(trigger_console_data, NULL, 1); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static int console_exit(SCLPEvent *event) | |
347 | { | |
348 | return 0; | |
349 | } | |
350 | ||
351 | static void console_reset(DeviceState *dev) | |
352 | { | |
353 | SCLPEvent *event = SCLP_EVENT(dev); | |
354 | SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); | |
355 | ||
356 | event->event_pending = false; | |
357 | scon->length = 0; | |
358 | scon->write_errors = 0; | |
359 | } | |
360 | ||
361 | static Property console_properties[] = { | |
362 | DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), | |
363 | DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), | |
364 | DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), | |
365 | DEFINE_PROP_END_OF_LIST(), | |
366 | }; | |
367 | ||
368 | static void console_class_init(ObjectClass *klass, void *data) | |
369 | { | |
370 | DeviceClass *dc = DEVICE_CLASS(klass); | |
371 | SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); | |
372 | ||
373 | dc->props = console_properties; | |
374 | dc->reset = console_reset; | |
375 | dc->vmsd = &vmstate_sclplmconsole; | |
376 | ec->init = console_init; | |
377 | ec->exit = console_exit; | |
378 | ec->get_send_mask = send_mask; | |
379 | ec->get_receive_mask = receive_mask; | |
380 | ec->can_handle_event = can_handle_event; | |
381 | ec->read_event_data = read_event_data; | |
382 | ec->write_event_data = write_event_data; | |
383 | } | |
384 | ||
385 | static const TypeInfo sclp_console_info = { | |
386 | .name = "sclplmconsole", | |
387 | .parent = TYPE_SCLP_EVENT, | |
388 | .instance_size = sizeof(SCLPConsoleLM), | |
389 | .class_init = console_class_init, | |
390 | .class_size = sizeof(SCLPEventClass), | |
391 | }; | |
392 | ||
393 | static void register_types(void) | |
394 | { | |
395 | type_register_static(&sclp_console_info); | |
396 | } | |
397 | ||
398 | type_init(register_types) |