]>
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 | ||
9615495a | 16 | #include "qemu/osdep.h" |
6a444f85 HG |
17 | #include "qemu/thread.h" |
18 | #include "qemu/error-report.h" | |
0b8fa32f | 19 | #include "qemu/module.h" |
4d43a603 | 20 | #include "chardev/char-fe.h" |
6a444f85 HG |
21 | |
22 | #include "hw/s390x/sclp.h" | |
d6454270 | 23 | #include "migration/vmstate.h" |
6a444f85 | 24 | #include "hw/s390x/event-facility.h" |
a27bd6c7 | 25 | #include "hw/qdev-properties.h" |
6a444f85 | 26 | #include "hw/s390x/ebcdic.h" |
db1015e9 | 27 | #include "qom/object.h" |
6a444f85 HG |
28 | |
29 | #define SIZE_BUFFER 4096 | |
30 | #define NEWLINE "\n" | |
31 | ||
32 | typedef struct OprtnsCommand { | |
33 | EventBufferHeader header; | |
34 | MDMSU message_unit; | |
880a7817 | 35 | char data[]; |
6a444f85 HG |
36 | } QEMU_PACKED OprtnsCommand; |
37 | ||
38 | /* max size for line-mode data in 4K SCCB page */ | |
39 | #define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) | |
40 | ||
db1015e9 | 41 | struct SCLPConsoleLM { |
6a444f85 | 42 | SCLPEvent event; |
becdfa00 | 43 | CharBackend chr; |
6a444f85 HG |
44 | bool echo; /* immediate echo of input if true */ |
45 | uint32_t write_errors; /* errors writing to char layer */ | |
46 | uint32_t length; /* length of byte stream in buffer */ | |
47 | uint8_t buf[SIZE_CONSOLE_BUFFER]; | |
db1015e9 EH |
48 | }; |
49 | typedef struct SCLPConsoleLM SCLPConsoleLM; | |
6a444f85 | 50 | |
e563c59b | 51 | #define TYPE_SCLPLM_CONSOLE "sclplmconsole" |
8110fa1d EH |
52 | DECLARE_INSTANCE_CHECKER(SCLPConsoleLM, SCLPLM_CONSOLE, |
53 | TYPE_SCLPLM_CONSOLE) | |
e563c59b | 54 | |
6a444f85 HG |
55 | /* |
56 | * Character layer call-back functions | |
57 | * | |
58 | * Allow 1 character at a time | |
59 | * | |
60 | * Accumulate bytes from character layer in console buffer, | |
61 | * event_pending is set when a newline character is encountered | |
62 | * | |
63 | * The maximum command line length is limited by the maximum | |
b3191432 HG |
64 | * space available in an SCCB. Line mode console input is sent |
65 | * truncated to the guest in case it doesn't fit into the SCCB. | |
6a444f85 HG |
66 | */ |
67 | ||
68 | static int chr_can_read(void *opaque) | |
69 | { | |
70 | SCLPConsoleLM *scon = opaque; | |
71 | ||
72 | if (scon->event.event_pending) { | |
73 | return 0; | |
6a444f85 | 74 | } |
b3191432 | 75 | return 1; |
6a444f85 HG |
76 | } |
77 | ||
4f3ed190 | 78 | static void chr_read(void *opaque, const uint8_t *buf, int size) |
6a444f85 | 79 | { |
4f3ed190 CB |
80 | SCLPConsoleLM *scon = opaque; |
81 | ||
6a444f85 HG |
82 | assert(size == 1); |
83 | ||
84 | if (*buf == '\r' || *buf == '\n') { | |
85 | scon->event.event_pending = true; | |
4f3ed190 | 86 | sclp_service_interrupt(0); |
6a444f85 HG |
87 | return; |
88 | } | |
b3191432 HG |
89 | if (scon->length == SIZE_CONSOLE_BUFFER) { |
90 | /* Eat the character, but still process CR and LF. */ | |
91 | return; | |
92 | } | |
6a444f85 HG |
93 | scon->buf[scon->length] = *buf; |
94 | scon->length += 1; | |
95 | if (scon->echo) { | |
6ab3fc32 DB |
96 | /* XXX this blocks entire thread. Rewrite to use |
97 | * qemu_chr_fe_write and background I/O callbacks */ | |
5345fdb4 | 98 | qemu_chr_fe_write_all(&scon->chr, buf, size); |
6a444f85 HG |
99 | } |
100 | } | |
101 | ||
6a444f85 HG |
102 | /* functions to be called by event facility */ |
103 | ||
104 | static bool can_handle_event(uint8_t type) | |
105 | { | |
106 | return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; | |
107 | } | |
108 | ||
1ffed98f | 109 | static sccb_mask_t send_mask(void) |
6a444f85 HG |
110 | { |
111 | return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; | |
112 | } | |
113 | ||
1ffed98f | 114 | static sccb_mask_t receive_mask(void) |
6a444f85 HG |
115 | { |
116 | return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; | |
117 | } | |
118 | ||
119 | /* | |
120 | * Triggered by SCLP's read_event_data | |
121 | * - convert ASCII byte stream to EBCDIC and | |
122 | * - copy converted data into provided (SCLP) buffer | |
123 | */ | |
124 | static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, | |
125 | int avail) | |
126 | { | |
127 | int len; | |
128 | ||
e563c59b | 129 | SCLPConsoleLM *cons = SCLPLM_CONSOLE(event); |
6a444f85 HG |
130 | |
131 | len = cons->length; | |
132 | /* data need to fit into provided SCLP buffer */ | |
133 | if (len > avail) { | |
134 | return 1; | |
135 | } | |
136 | ||
137 | ebcdic_put(buf, (char *)&cons->buf, len); | |
138 | *size = len; | |
139 | cons->length = 0; | |
140 | /* data provided and no more data pending */ | |
141 | event->event_pending = false; | |
87f2eff0 | 142 | qemu_notify_event(); |
6a444f85 HG |
143 | return 0; |
144 | } | |
145 | ||
146 | static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, | |
147 | int *slen) | |
148 | { | |
149 | int avail, rc; | |
150 | size_t src_len; | |
151 | uint8_t *to; | |
152 | OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; | |
153 | ||
154 | if (!event->event_pending) { | |
155 | /* no data pending */ | |
156 | return 0; | |
157 | } | |
158 | ||
159 | to = (uint8_t *)&oc->data; | |
160 | avail = *slen - sizeof(OprtnsCommand); | |
161 | rc = get_console_data(event, to, &src_len, avail); | |
162 | if (rc) { | |
163 | /* data didn't fit, try next SCCB */ | |
164 | return 1; | |
165 | } | |
166 | ||
167 | oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; | |
168 | oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); | |
169 | ||
170 | oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; | |
171 | oc->message_unit.cpmsu.length = | |
172 | cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); | |
173 | ||
174 | oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; | |
175 | oc->message_unit.text_command.length = | |
176 | cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); | |
177 | ||
178 | oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; | |
179 | oc->message_unit.self_def_text_message.length = | |
180 | cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); | |
181 | ||
182 | oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; | |
183 | oc->message_unit.text_message.length = | |
184 | cpu_to_be16(sizeof(GdsSubvector) + src_len); | |
185 | ||
186 | oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); | |
187 | oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; | |
188 | *slen = avail - src_len; | |
189 | ||
190 | return 1; | |
191 | } | |
192 | ||
193 | /* | |
194 | * Triggered by SCLP's write_event_data | |
195 | * - write console data to character layer | |
196 | * returns < 0 if an error occurred | |
197 | */ | |
198 | static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) | |
199 | { | |
e563c59b | 200 | SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); |
6a444f85 | 201 | |
30650701 | 202 | if (!qemu_chr_fe_backend_connected(&scon->chr)) { |
6a444f85 HG |
203 | /* If there's no backend, we can just say we consumed all data. */ |
204 | return len; | |
205 | } | |
206 | ||
7983e829 DB |
207 | /* XXX this blocks entire thread. Rewrite to use |
208 | * qemu_chr_fe_write and background I/O callbacks */ | |
5345fdb4 | 209 | return qemu_chr_fe_write_all(&scon->chr, buf, len); |
6a444f85 HG |
210 | } |
211 | ||
212 | static int process_mdb(SCLPEvent *event, MDBO *mdbo) | |
213 | { | |
214 | int rc; | |
215 | int len; | |
216 | uint8_t buffer[SIZE_BUFFER]; | |
217 | ||
218 | len = be16_to_cpu(mdbo->length); | |
219 | len -= sizeof(mdbo->length) + sizeof(mdbo->type) | |
220 | + sizeof(mdbo->mto.line_type_flags) | |
221 | + sizeof(mdbo->mto.alarm_control) | |
222 | + sizeof(mdbo->mto._reserved); | |
223 | ||
224 | assert(len <= SIZE_BUFFER); | |
225 | ||
226 | /* convert EBCDIC SCLP contents to ASCII console message */ | |
227 | ascii_put(buffer, mdbo->mto.message, len); | |
228 | rc = write_console_data(event, (uint8_t *)NEWLINE, 1); | |
229 | if (rc < 0) { | |
230 | return rc; | |
231 | } | |
232 | return write_console_data(event, buffer, len); | |
233 | } | |
234 | ||
235 | static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) | |
236 | { | |
237 | int len; | |
238 | int written; | |
239 | int errors = 0; | |
240 | MDBO *mdbo; | |
241 | SclpMsg *data = (SclpMsg *) ebh; | |
e563c59b | 242 | SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); |
6a444f85 HG |
243 | |
244 | len = be16_to_cpu(data->mdb.header.length); | |
245 | if (len < sizeof(data->mdb.header)) { | |
246 | return SCLP_RC_INCONSISTENT_LENGTHS; | |
247 | } | |
248 | len -= sizeof(data->mdb.header); | |
249 | ||
250 | /* first check message buffers */ | |
251 | mdbo = data->mdb.mdbo; | |
252 | while (len > 0) { | |
253 | if (be16_to_cpu(mdbo->length) > len | |
254 | || be16_to_cpu(mdbo->length) == 0) { | |
255 | return SCLP_RC_INCONSISTENT_LENGTHS; | |
256 | } | |
257 | len -= be16_to_cpu(mdbo->length); | |
258 | mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | |
259 | } | |
260 | ||
261 | /* then execute */ | |
262 | len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); | |
263 | mdbo = data->mdb.mdbo; | |
264 | while (len > 0) { | |
265 | switch (be16_to_cpu(mdbo->type)) { | |
266 | case MESSAGE_TEXT: | |
267 | /* message text object */ | |
268 | written = process_mdb(event, mdbo); | |
269 | if (written < 0) { | |
270 | /* character layer error */ | |
271 | errors++; | |
272 | } | |
273 | break; | |
274 | default: /* ignore */ | |
275 | break; | |
276 | } | |
277 | len -= be16_to_cpu(mdbo->length); | |
278 | mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | |
279 | } | |
280 | if (errors) { | |
281 | scon->write_errors += errors; | |
282 | } | |
283 | data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; | |
284 | ||
285 | return SCLP_RC_NORMAL_COMPLETION; | |
286 | } | |
287 | ||
6a444f85 HG |
288 | /* functions for live migration */ |
289 | ||
290 | static const VMStateDescription vmstate_sclplmconsole = { | |
291 | .name = "sclplmconsole", | |
292 | .version_id = 0, | |
293 | .minimum_version_id = 0, | |
35d08458 | 294 | .fields = (VMStateField[]) { |
6a444f85 HG |
295 | VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), |
296 | VMSTATE_UINT32(write_errors, SCLPConsoleLM), | |
297 | VMSTATE_UINT32(length, SCLPConsoleLM), | |
298 | VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), | |
299 | VMSTATE_END_OF_LIST() | |
300 | } | |
301 | }; | |
302 | ||
303 | /* qemu object creation and initialization functions */ | |
304 | ||
305 | /* tell character layer our call-back functions */ | |
306 | ||
307 | static int console_init(SCLPEvent *event) | |
308 | { | |
309 | static bool console_available; | |
310 | ||
e563c59b | 311 | SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); |
6a444f85 HG |
312 | |
313 | if (console_available) { | |
314 | error_report("Multiple line-mode operator consoles are not supported"); | |
315 | return -1; | |
316 | } | |
317 | console_available = true; | |
318 | ||
fa394ed6 | 319 | qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, |
81517ba3 | 320 | chr_read, NULL, NULL, scon, NULL, true); |
6a444f85 HG |
321 | |
322 | return 0; | |
323 | } | |
324 | ||
6a444f85 HG |
325 | static void console_reset(DeviceState *dev) |
326 | { | |
327 | SCLPEvent *event = SCLP_EVENT(dev); | |
e563c59b | 328 | SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); |
6a444f85 HG |
329 | |
330 | event->event_pending = false; | |
331 | scon->length = 0; | |
332 | scon->write_errors = 0; | |
333 | } | |
334 | ||
335 | static Property console_properties[] = { | |
336 | DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), | |
337 | DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), | |
338 | DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), | |
339 | DEFINE_PROP_END_OF_LIST(), | |
340 | }; | |
341 | ||
342 | static void console_class_init(ObjectClass *klass, void *data) | |
343 | { | |
344 | DeviceClass *dc = DEVICE_CLASS(klass); | |
345 | SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); | |
346 | ||
4f67d30b | 347 | device_class_set_props(dc, console_properties); |
6a444f85 HG |
348 | dc->reset = console_reset; |
349 | dc->vmsd = &vmstate_sclplmconsole; | |
350 | ec->init = console_init; | |
6a444f85 HG |
351 | ec->get_send_mask = send_mask; |
352 | ec->get_receive_mask = receive_mask; | |
353 | ec->can_handle_event = can_handle_event; | |
354 | ec->read_event_data = read_event_data; | |
355 | ec->write_event_data = write_event_data; | |
183f6b8d | 356 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |
6a444f85 HG |
357 | } |
358 | ||
359 | static const TypeInfo sclp_console_info = { | |
1a3bae79 | 360 | .name = TYPE_SCLPLM_CONSOLE, |
6a444f85 HG |
361 | .parent = TYPE_SCLP_EVENT, |
362 | .instance_size = sizeof(SCLPConsoleLM), | |
363 | .class_init = console_class_init, | |
364 | .class_size = sizeof(SCLPEventClass), | |
365 | }; | |
366 | ||
367 | static void register_types(void) | |
368 | { | |
369 | type_register_static(&sclp_console_info); | |
370 | } | |
371 | ||
372 | type_init(register_types) |