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