1 | /* |
2 | * I/O instructions for S/390 |
3 | * |
4 | * Copyright 2012, 2015 IBM Corp. |
5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
8 | * your option) any later version. See the COPYING file in the top-level |
9 | * directory. |
10 | */ |
11 | |
12 | #include "qemu/osdep.h" |
13 | |
14 | #include "cpu.h" |
15 | #include "internal.h" |
16 | #include "hw/s390x/ioinst.h" |
17 | #include "trace.h" |
18 | #include "hw/s390x/s390-pci-bus.h" |
19 | |
20 | int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, |
21 | int *schid) |
22 | { |
23 | if (!IOINST_SCHID_ONE(value)) { |
24 | return -EINVAL; |
25 | } |
26 | if (!IOINST_SCHID_M(value)) { |
27 | if (IOINST_SCHID_CSSID(value)) { |
28 | return -EINVAL; |
29 | } |
30 | *cssid = 0; |
31 | *m = 0; |
32 | } else { |
33 | *cssid = IOINST_SCHID_CSSID(value); |
34 | *m = 1; |
35 | } |
36 | *ssid = IOINST_SCHID_SSID(value); |
37 | *schid = IOINST_SCHID_NR(value); |
38 | return 0; |
39 | } |
40 | |
41 | void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
42 | { |
43 | int cssid, ssid, schid, m; |
44 | SubchDev *sch; |
45 | |
46 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
47 | s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); |
48 | return; |
49 | } |
50 | trace_ioinst_sch_id("xsch" , cssid, ssid, schid); |
51 | sch = css_find_subch(m, cssid, ssid, schid); |
52 | if (!sch || !css_subch_visible(sch)) { |
53 | setcc(cpu, 3); |
54 | return; |
55 | } |
56 | setcc(cpu, css_do_xsch(sch)); |
57 | } |
58 | |
59 | void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
60 | { |
61 | int cssid, ssid, schid, m; |
62 | SubchDev *sch; |
63 | |
64 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
65 | s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); |
66 | return; |
67 | } |
68 | trace_ioinst_sch_id("csch" , cssid, ssid, schid); |
69 | sch = css_find_subch(m, cssid, ssid, schid); |
70 | if (!sch || !css_subch_visible(sch)) { |
71 | setcc(cpu, 3); |
72 | return; |
73 | } |
74 | setcc(cpu, css_do_csch(sch)); |
75 | } |
76 | |
77 | void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
78 | { |
79 | int cssid, ssid, schid, m; |
80 | SubchDev *sch; |
81 | |
82 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
83 | s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); |
84 | return; |
85 | } |
86 | trace_ioinst_sch_id("hsch" , cssid, ssid, schid); |
87 | sch = css_find_subch(m, cssid, ssid, schid); |
88 | if (!sch || !css_subch_visible(sch)) { |
89 | setcc(cpu, 3); |
90 | return; |
91 | } |
92 | setcc(cpu, css_do_hsch(sch)); |
93 | } |
94 | |
95 | static int ioinst_schib_valid(SCHIB *schib) |
96 | { |
97 | if ((be16_to_cpu(schib->pmcw.flags) & PMCW_FLAGS_MASK_INVALID) || |
98 | (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_INVALID)) { |
99 | return 0; |
100 | } |
101 | /* Disallow extended measurements for now. */ |
102 | if (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_XMWME) { |
103 | return 0; |
104 | } |
105 | return 1; |
106 | } |
107 | |
108 | void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) |
109 | { |
110 | int cssid, ssid, schid, m; |
111 | SubchDev *sch; |
112 | SCHIB schib; |
113 | uint64_t addr; |
114 | CPUS390XState *env = &cpu->env; |
115 | uint8_t ar; |
116 | |
117 | addr = decode_basedisp_s(env, ipb, &ar); |
118 | if (addr & 3) { |
119 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
120 | return; |
121 | } |
122 | if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) { |
123 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
124 | return; |
125 | } |
126 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || |
127 | !ioinst_schib_valid(&schib)) { |
128 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
129 | return; |
130 | } |
131 | trace_ioinst_sch_id("msch" , cssid, ssid, schid); |
132 | sch = css_find_subch(m, cssid, ssid, schid); |
133 | if (!sch || !css_subch_visible(sch)) { |
134 | setcc(cpu, 3); |
135 | return; |
136 | } |
137 | setcc(cpu, css_do_msch(sch, &schib)); |
138 | } |
139 | |
140 | static void copy_orb_from_guest(ORB *dest, const ORB *src) |
141 | { |
142 | dest->intparm = be32_to_cpu(src->intparm); |
143 | dest->ctrl0 = be16_to_cpu(src->ctrl0); |
144 | dest->lpm = src->lpm; |
145 | dest->ctrl1 = src->ctrl1; |
146 | dest->cpa = be32_to_cpu(src->cpa); |
147 | } |
148 | |
149 | static int ioinst_orb_valid(ORB *orb) |
150 | { |
151 | if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) || |
152 | (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) { |
153 | return 0; |
154 | } |
155 | /* We don't support MIDA. */ |
156 | if (orb->ctrl1 & ORB_CTRL1_MASK_MIDAW) { |
157 | return 0; |
158 | } |
159 | if ((orb->cpa & HIGH_ORDER_BIT) != 0) { |
160 | return 0; |
161 | } |
162 | return 1; |
163 | } |
164 | |
165 | void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) |
166 | { |
167 | int cssid, ssid, schid, m; |
168 | SubchDev *sch; |
169 | ORB orig_orb, orb; |
170 | uint64_t addr; |
171 | CPUS390XState *env = &cpu->env; |
172 | uint8_t ar; |
173 | |
174 | addr = decode_basedisp_s(env, ipb, &ar); |
175 | if (addr & 3) { |
176 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
177 | return; |
178 | } |
179 | if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) { |
180 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
181 | return; |
182 | } |
183 | copy_orb_from_guest(&orb, &orig_orb); |
184 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || |
185 | !ioinst_orb_valid(&orb)) { |
186 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
187 | return; |
188 | } |
189 | trace_ioinst_sch_id("ssch" , cssid, ssid, schid); |
190 | sch = css_find_subch(m, cssid, ssid, schid); |
191 | if (!sch || !css_subch_visible(sch)) { |
192 | setcc(cpu, 3); |
193 | return; |
194 | } |
195 | setcc(cpu, css_do_ssch(sch, &orb)); |
196 | } |
197 | |
198 | void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra) |
199 | { |
200 | CRW crw; |
201 | uint64_t addr; |
202 | int cc; |
203 | CPUS390XState *env = &cpu->env; |
204 | uint8_t ar; |
205 | |
206 | addr = decode_basedisp_s(env, ipb, &ar); |
207 | if (addr & 3) { |
208 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
209 | return; |
210 | } |
211 | |
212 | cc = css_do_stcrw(&crw); |
213 | /* 0 - crw stored, 1 - zeroes stored */ |
214 | |
215 | if (s390_cpu_virt_mem_write(cpu, addr, ar, &crw, sizeof(crw)) == 0) { |
216 | setcc(cpu, cc); |
217 | } else { |
218 | if (cc == 0) { |
219 | /* Write failed: requeue CRW since STCRW is suppressing */ |
220 | css_undo_stcrw(&crw); |
221 | } |
222 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
223 | } |
224 | } |
225 | |
226 | void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, |
227 | uintptr_t ra) |
228 | { |
229 | int cssid, ssid, schid, m; |
230 | SubchDev *sch; |
231 | uint64_t addr; |
232 | int cc; |
233 | SCHIB schib; |
234 | CPUS390XState *env = &cpu->env; |
235 | uint8_t ar; |
236 | |
237 | addr = decode_basedisp_s(env, ipb, &ar); |
238 | if (addr & 3) { |
239 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
240 | return; |
241 | } |
242 | |
243 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
244 | /* |
245 | * As operand exceptions have a lower priority than access exceptions, |
246 | * we check whether the memory area is writeable (injecting the |
247 | * access execption if it is not) first. |
248 | */ |
249 | if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) { |
250 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
251 | } else { |
252 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
253 | } |
254 | return; |
255 | } |
256 | trace_ioinst_sch_id("stsch" , cssid, ssid, schid); |
257 | sch = css_find_subch(m, cssid, ssid, schid); |
258 | if (sch) { |
259 | if (css_subch_visible(sch)) { |
260 | css_do_stsch(sch, &schib); |
261 | cc = 0; |
262 | } else { |
263 | /* Indicate no more subchannels in this css/ss */ |
264 | cc = 3; |
265 | } |
266 | } else { |
267 | if (css_schid_final(m, cssid, ssid, schid)) { |
268 | cc = 3; /* No more subchannels in this css/ss */ |
269 | } else { |
270 | /* Store an empty schib. */ |
271 | memset(&schib, 0, sizeof(schib)); |
272 | cc = 0; |
273 | } |
274 | } |
275 | if (cc != 3) { |
276 | if (s390_cpu_virt_mem_write(cpu, addr, ar, &schib, |
277 | sizeof(schib)) != 0) { |
278 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
279 | return; |
280 | } |
281 | } else { |
282 | /* Access exceptions have a higher priority than cc3 */ |
283 | if (s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib)) != 0) { |
284 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
285 | return; |
286 | } |
287 | } |
288 | setcc(cpu, cc); |
289 | } |
290 | |
291 | int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) |
292 | { |
293 | CPUS390XState *env = &cpu->env; |
294 | int cssid, ssid, schid, m; |
295 | SubchDev *sch; |
296 | IRB irb; |
297 | uint64_t addr; |
298 | int cc, irb_len; |
299 | uint8_t ar; |
300 | |
301 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
302 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
303 | return -EIO; |
304 | } |
305 | trace_ioinst_sch_id("tsch" , cssid, ssid, schid); |
306 | addr = decode_basedisp_s(env, ipb, &ar); |
307 | if (addr & 3) { |
308 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
309 | return -EIO; |
310 | } |
311 | |
312 | sch = css_find_subch(m, cssid, ssid, schid); |
313 | if (sch && css_subch_visible(sch)) { |
314 | cc = css_do_tsch_get_irb(sch, &irb, &irb_len); |
315 | } else { |
316 | cc = 3; |
317 | } |
318 | /* 0 - status pending, 1 - not status pending, 3 - not operational */ |
319 | if (cc != 3) { |
320 | if (s390_cpu_virt_mem_write(cpu, addr, ar, &irb, irb_len) != 0) { |
321 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
322 | return -EFAULT; |
323 | } |
324 | css_do_tsch_update_subch(sch); |
325 | } else { |
326 | irb_len = sizeof(irb) - sizeof(irb.emw); |
327 | /* Access exceptions have a higher priority than cc3 */ |
328 | if (s390_cpu_virt_mem_check_write(cpu, addr, ar, irb_len) != 0) { |
329 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
330 | return -EFAULT; |
331 | } |
332 | } |
333 | |
334 | setcc(cpu, cc); |
335 | return 0; |
336 | } |
337 | |
338 | typedef struct ChscReq { |
339 | uint16_t len; |
340 | uint16_t command; |
341 | uint32_t param0; |
342 | uint32_t param1; |
343 | uint32_t param2; |
344 | } QEMU_PACKED ChscReq; |
345 | |
346 | typedef struct ChscResp { |
347 | uint16_t len; |
348 | uint16_t code; |
349 | uint32_t param; |
350 | char data[0]; |
351 | } QEMU_PACKED ChscResp; |
352 | |
353 | #define CHSC_MIN_RESP_LEN 0x0008 |
354 | |
355 | #define CHSC_SCPD 0x0002 |
356 | #define CHSC_SCSC 0x0010 |
357 | #define CHSC_SDA 0x0031 |
358 | #define CHSC_SEI 0x000e |
359 | |
360 | #define CHSC_SCPD_0_M 0x20000000 |
361 | #define CHSC_SCPD_0_C 0x10000000 |
362 | #define CHSC_SCPD_0_FMT 0x0f000000 |
363 | #define CHSC_SCPD_0_CSSID 0x00ff0000 |
364 | #define CHSC_SCPD_0_RFMT 0x00000f00 |
365 | #define CHSC_SCPD_0_RES 0xc000f000 |
366 | #define CHSC_SCPD_1_RES 0xffffff00 |
367 | #define CHSC_SCPD_01_CHPID 0x000000ff |
368 | static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res) |
369 | { |
370 | uint16_t len = be16_to_cpu(req->len); |
371 | uint32_t param0 = be32_to_cpu(req->param0); |
372 | uint32_t param1 = be32_to_cpu(req->param1); |
373 | uint16_t resp_code; |
374 | int rfmt; |
375 | uint16_t cssid; |
376 | uint8_t f_chpid, l_chpid; |
377 | int desc_size; |
378 | int m; |
379 | |
380 | rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8; |
381 | if ((rfmt == 0) || (rfmt == 1)) { |
382 | rfmt = !!(param0 & CHSC_SCPD_0_C); |
383 | } |
384 | if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) || |
385 | (param1 & CHSC_SCPD_1_RES) || req->param2) { |
386 | resp_code = 0x0003; |
387 | goto out_err; |
388 | } |
389 | if (param0 & CHSC_SCPD_0_FMT) { |
390 | resp_code = 0x0007; |
391 | goto out_err; |
392 | } |
393 | cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16; |
394 | m = param0 & CHSC_SCPD_0_M; |
395 | if (cssid != 0) { |
396 | if (!m || !css_present(cssid)) { |
397 | resp_code = 0x0008; |
398 | goto out_err; |
399 | } |
400 | } |
401 | f_chpid = param0 & CHSC_SCPD_01_CHPID; |
402 | l_chpid = param1 & CHSC_SCPD_01_CHPID; |
403 | if (l_chpid < f_chpid) { |
404 | resp_code = 0x0003; |
405 | goto out_err; |
406 | } |
407 | /* css_collect_chp_desc() is endian-aware */ |
408 | desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt, |
409 | &res->data); |
410 | res->code = cpu_to_be16(0x0001); |
411 | res->len = cpu_to_be16(8 + desc_size); |
412 | res->param = cpu_to_be32(rfmt); |
413 | return; |
414 | |
415 | out_err: |
416 | res->code = cpu_to_be16(resp_code); |
417 | res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); |
418 | res->param = cpu_to_be32(rfmt); |
419 | } |
420 | |
421 | #define CHSC_SCSC_0_M 0x20000000 |
422 | #define CHSC_SCSC_0_FMT 0x000f0000 |
423 | #define CHSC_SCSC_0_CSSID 0x0000ff00 |
424 | #define CHSC_SCSC_0_RES 0xdff000ff |
425 | static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res) |
426 | { |
427 | uint16_t len = be16_to_cpu(req->len); |
428 | uint32_t param0 = be32_to_cpu(req->param0); |
429 | uint8_t cssid; |
430 | uint16_t resp_code; |
431 | uint32_t general_chars[510]; |
432 | uint32_t chsc_chars[508]; |
433 | |
434 | if (len != 0x0010) { |
435 | resp_code = 0x0003; |
436 | goto out_err; |
437 | } |
438 | |
439 | if (param0 & CHSC_SCSC_0_FMT) { |
440 | resp_code = 0x0007; |
441 | goto out_err; |
442 | } |
443 | cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8; |
444 | if (cssid != 0) { |
445 | if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) { |
446 | resp_code = 0x0008; |
447 | goto out_err; |
448 | } |
449 | } |
450 | if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) { |
451 | resp_code = 0x0003; |
452 | goto out_err; |
453 | } |
454 | res->code = cpu_to_be16(0x0001); |
455 | res->len = cpu_to_be16(4080); |
456 | res->param = 0; |
457 | |
458 | memset(general_chars, 0, sizeof(general_chars)); |
459 | memset(chsc_chars, 0, sizeof(chsc_chars)); |
460 | |
461 | general_chars[0] = cpu_to_be32(0x03000000); |
462 | general_chars[1] = cpu_to_be32(0x00079000); |
463 | general_chars[3] = cpu_to_be32(0x00080000); |
464 | |
465 | chsc_chars[0] = cpu_to_be32(0x40000000); |
466 | chsc_chars[3] = cpu_to_be32(0x00040000); |
467 | |
468 | memcpy(res->data, general_chars, sizeof(general_chars)); |
469 | memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars)); |
470 | return; |
471 | |
472 | out_err: |
473 | res->code = cpu_to_be16(resp_code); |
474 | res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); |
475 | res->param = 0; |
476 | } |
477 | |
478 | #define CHSC_SDA_0_FMT 0x0f000000 |
479 | #define CHSC_SDA_0_OC 0x0000ffff |
480 | #define CHSC_SDA_0_RES 0xf0ff0000 |
481 | #define CHSC_SDA_OC_MCSSE 0x0 |
482 | #define CHSC_SDA_OC_MSS 0x2 |
483 | static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) |
484 | { |
485 | uint16_t resp_code = 0x0001; |
486 | uint16_t len = be16_to_cpu(req->len); |
487 | uint32_t param0 = be32_to_cpu(req->param0); |
488 | uint16_t oc; |
489 | int ret; |
490 | |
491 | if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) { |
492 | resp_code = 0x0003; |
493 | goto out; |
494 | } |
495 | |
496 | if (param0 & CHSC_SDA_0_FMT) { |
497 | resp_code = 0x0007; |
498 | goto out; |
499 | } |
500 | |
501 | oc = param0 & CHSC_SDA_0_OC; |
502 | switch (oc) { |
503 | case CHSC_SDA_OC_MCSSE: |
504 | ret = css_enable_mcsse(); |
505 | if (ret == -EINVAL) { |
506 | resp_code = 0x0101; |
507 | goto out; |
508 | } |
509 | break; |
510 | case CHSC_SDA_OC_MSS: |
511 | ret = css_enable_mss(); |
512 | if (ret == -EINVAL) { |
513 | resp_code = 0x0101; |
514 | goto out; |
515 | } |
516 | break; |
517 | default: |
518 | resp_code = 0x0003; |
519 | goto out; |
520 | } |
521 | |
522 | out: |
523 | res->code = cpu_to_be16(resp_code); |
524 | res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); |
525 | res->param = 0; |
526 | } |
527 | |
528 | static int chsc_sei_nt0_get_event(void *res) |
529 | { |
530 | /* no events yet */ |
531 | return 1; |
532 | } |
533 | |
534 | static int chsc_sei_nt0_have_event(void) |
535 | { |
536 | /* no events yet */ |
537 | return 0; |
538 | } |
539 | |
540 | static int chsc_sei_nt2_get_event(void *res) |
541 | { |
542 | if (s390_has_feat(S390_FEAT_ZPCI)) { |
543 | return pci_chsc_sei_nt2_get_event(res); |
544 | } |
545 | return 1; |
546 | } |
547 | |
548 | static int chsc_sei_nt2_have_event(void) |
549 | { |
550 | if (s390_has_feat(S390_FEAT_ZPCI)) { |
551 | return pci_chsc_sei_nt2_have_event(); |
552 | } |
553 | return 0; |
554 | } |
555 | |
556 | #define CHSC_SEI_NT0 (1ULL << 63) |
557 | #define CHSC_SEI_NT2 (1ULL << 61) |
558 | static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res) |
559 | { |
560 | uint64_t selection_mask = ldq_p(&req->param1); |
561 | uint8_t *res_flags = (uint8_t *)res->data; |
562 | int have_event = 0; |
563 | int have_more = 0; |
564 | |
565 | /* regarding architecture nt0 can not be masked */ |
566 | have_event = !chsc_sei_nt0_get_event(res); |
567 | have_more = chsc_sei_nt0_have_event(); |
568 | |
569 | if (selection_mask & CHSC_SEI_NT2) { |
570 | if (!have_event) { |
571 | have_event = !chsc_sei_nt2_get_event(res); |
572 | } |
573 | |
574 | if (!have_more) { |
575 | have_more = chsc_sei_nt2_have_event(); |
576 | } |
577 | } |
578 | |
579 | if (have_event) { |
580 | res->code = cpu_to_be16(0x0001); |
581 | if (have_more) { |
582 | (*res_flags) |= 0x80; |
583 | } else { |
584 | (*res_flags) &= ~0x80; |
585 | css_clear_sei_pending(); |
586 | } |
587 | } else { |
588 | res->code = cpu_to_be16(0x0005); |
589 | res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); |
590 | } |
591 | } |
592 | |
593 | static void ioinst_handle_chsc_unimplemented(ChscResp *res) |
594 | { |
595 | res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); |
596 | res->code = cpu_to_be16(0x0004); |
597 | res->param = 0; |
598 | } |
599 | |
600 | void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra) |
601 | { |
602 | ChscReq *req; |
603 | ChscResp *res; |
604 | uint64_t addr; |
605 | int reg; |
606 | uint16_t len; |
607 | uint16_t command; |
608 | CPUS390XState *env = &cpu->env; |
609 | uint8_t buf[TARGET_PAGE_SIZE]; |
610 | |
611 | trace_ioinst("chsc" ); |
612 | reg = (ipb >> 20) & 0x00f; |
613 | addr = env->regs[reg]; |
614 | /* Page boundary? */ |
615 | if (addr & 0xfff) { |
616 | s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); |
617 | return; |
618 | } |
619 | /* |
620 | * Reading sizeof(ChscReq) bytes is currently enough for all of our |
621 | * present CHSC sub-handlers ... if we ever need more, we should take |
622 | * care of req->len here first. |
623 | */ |
624 | if (s390_cpu_virt_mem_read(cpu, addr, reg, buf, sizeof(ChscReq))) { |
625 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
626 | return; |
627 | } |
628 | req = (ChscReq *)buf; |
629 | len = be16_to_cpu(req->len); |
630 | /* Length field valid? */ |
631 | if ((len < 16) || (len > 4088) || (len & 7)) { |
632 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
633 | return; |
634 | } |
635 | memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); |
636 | res = (void *)((char *)req + len); |
637 | command = be16_to_cpu(req->command); |
638 | trace_ioinst_chsc_cmd(command, len); |
639 | switch (command) { |
640 | case CHSC_SCSC: |
641 | ioinst_handle_chsc_scsc(req, res); |
642 | break; |
643 | case CHSC_SCPD: |
644 | ioinst_handle_chsc_scpd(req, res); |
645 | break; |
646 | case CHSC_SDA: |
647 | ioinst_handle_chsc_sda(req, res); |
648 | break; |
649 | case CHSC_SEI: |
650 | ioinst_handle_chsc_sei(req, res); |
651 | break; |
652 | default: |
653 | ioinst_handle_chsc_unimplemented(res); |
654 | break; |
655 | } |
656 | |
657 | if (!s390_cpu_virt_mem_write(cpu, addr + len, reg, res, |
658 | be16_to_cpu(res->len))) { |
659 | setcc(cpu, 0); /* Command execution complete */ |
660 | } else { |
661 | s390_cpu_virt_mem_handle_exc(cpu, ra); |
662 | } |
663 | } |
664 | |
665 | #define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc) |
666 | #define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28) |
667 | #define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1) |
668 | #define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001) |
669 | |
670 | void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, |
671 | uint32_t ipb, uintptr_t ra) |
672 | { |
673 | uint8_t mbk; |
674 | int update; |
675 | int dct; |
676 | CPUS390XState *env = &cpu->env; |
677 | |
678 | trace_ioinst("schm" ); |
679 | |
680 | if (SCHM_REG1_RES(reg1)) { |
681 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
682 | return; |
683 | } |
684 | |
685 | mbk = SCHM_REG1_MBK(reg1); |
686 | update = SCHM_REG1_UPD(reg1); |
687 | dct = SCHM_REG1_DCT(reg1); |
688 | |
689 | if (update && (reg2 & 0x000000000000001f)) { |
690 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
691 | return; |
692 | } |
693 | |
694 | css_do_schm(mbk, update, dct, update ? reg2 : 0); |
695 | } |
696 | |
697 | void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
698 | { |
699 | int cssid, ssid, schid, m; |
700 | SubchDev *sch; |
701 | |
702 | if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { |
703 | s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); |
704 | return; |
705 | } |
706 | trace_ioinst_sch_id("rsch" , cssid, ssid, schid); |
707 | sch = css_find_subch(m, cssid, ssid, schid); |
708 | if (!sch || !css_subch_visible(sch)) { |
709 | setcc(cpu, 3); |
710 | return; |
711 | } |
712 | setcc(cpu, css_do_rsch(sch)); |
713 | } |
714 | |
715 | #define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) |
716 | #define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16) |
717 | #define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff) |
718 | void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
719 | { |
720 | int cc; |
721 | uint8_t cssid; |
722 | uint8_t chpid; |
723 | int ret; |
724 | CPUS390XState *env = &cpu->env; |
725 | |
726 | if (RCHP_REG1_RES(reg1)) { |
727 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
728 | return; |
729 | } |
730 | |
731 | cssid = RCHP_REG1_CSSID(reg1); |
732 | chpid = RCHP_REG1_CHPID(reg1); |
733 | |
734 | trace_ioinst_chp_id("rchp" , cssid, chpid); |
735 | |
736 | ret = css_do_rchp(cssid, chpid); |
737 | |
738 | switch (ret) { |
739 | case -ENODEV: |
740 | cc = 3; |
741 | break; |
742 | case -EBUSY: |
743 | cc = 2; |
744 | break; |
745 | case 0: |
746 | cc = 0; |
747 | break; |
748 | default: |
749 | /* Invalid channel subsystem. */ |
750 | s390_program_interrupt(env, PGM_OPERAND, 4, ra); |
751 | return; |
752 | } |
753 | setcc(cpu, cc); |
754 | } |
755 | |
756 | #define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) |
757 | void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra) |
758 | { |
759 | /* We do not provide address limit checking, so let's suppress it. */ |
760 | if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { |
761 | s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); |
762 | } |
763 | } |
764 | |