xref: /openbmc/qemu/target/s390x/ioinst.c (revision 2af37e791906cfda42cb9604a16d218e56994bb1)
1fcf5ef2aSThomas Huth /*
2fcf5ef2aSThomas Huth  * I/O instructions for S/390
3fcf5ef2aSThomas Huth  *
4fcf5ef2aSThomas Huth  * Copyright 2012, 2015 IBM Corp.
5fcf5ef2aSThomas Huth  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
6fcf5ef2aSThomas Huth  *
7fcf5ef2aSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or (at
8fcf5ef2aSThomas Huth  * your option) any later version. See the COPYING file in the top-level
9fcf5ef2aSThomas Huth  * directory.
10fcf5ef2aSThomas Huth  */
11fcf5ef2aSThomas Huth 
12fcf5ef2aSThomas Huth #include "qemu/osdep.h"
13fcf5ef2aSThomas Huth 
14fcf5ef2aSThomas Huth #include "cpu.h"
15b6b47223SCho, Yu-Chen #include "s390x-internal.h"
16fcf5ef2aSThomas Huth #include "hw/s390x/ioinst.h"
17fcf5ef2aSThomas Huth #include "trace.h"
18fcf5ef2aSThomas Huth #include "hw/s390x/s390-pci-bus.h"
19f5f9c6eaSPhilippe Mathieu-Daudé #include "target/s390x/kvm/pv.h"
20c10b7087SJanosch Frank 
21c10b7087SJanosch Frank /* All I/O instructions but chsc use the s format */
get_address_from_regs(CPUS390XState * env,uint32_t ipb,uint8_t * ar)22c10b7087SJanosch Frank static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb,
23c10b7087SJanosch Frank                                       uint8_t *ar)
24c10b7087SJanosch Frank {
25c10b7087SJanosch Frank     /*
26c10b7087SJanosch Frank      * Addresses for protected guests are all offsets into the
27c10b7087SJanosch Frank      * satellite block which holds the IO control structures. Those
28c10b7087SJanosch Frank      * control structures are always starting at offset 0 and are
29c10b7087SJanosch Frank      * always aligned and accessible. So we can return 0 here which
30c10b7087SJanosch Frank      * will pass the following address checks.
31c10b7087SJanosch Frank      */
32c10b7087SJanosch Frank     if (s390_is_pv()) {
33c10b7087SJanosch Frank         *ar = 0;
34c10b7087SJanosch Frank         return 0;
35c10b7087SJanosch Frank     }
36c10b7087SJanosch Frank     return decode_basedisp_s(env, ipb, ar);
37c10b7087SJanosch Frank }
38fcf5ef2aSThomas Huth 
ioinst_disassemble_sch_ident(uint32_t value,int * m,int * cssid,int * ssid,int * schid)39fcf5ef2aSThomas Huth int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
40fcf5ef2aSThomas Huth                                  int *schid)
41fcf5ef2aSThomas Huth {
42fcf5ef2aSThomas Huth     if (!IOINST_SCHID_ONE(value)) {
43fcf5ef2aSThomas Huth         return -EINVAL;
44fcf5ef2aSThomas Huth     }
45fcf5ef2aSThomas Huth     if (!IOINST_SCHID_M(value)) {
46fcf5ef2aSThomas Huth         if (IOINST_SCHID_CSSID(value)) {
47fcf5ef2aSThomas Huth             return -EINVAL;
48fcf5ef2aSThomas Huth         }
49fcf5ef2aSThomas Huth         *cssid = 0;
50fcf5ef2aSThomas Huth         *m = 0;
51fcf5ef2aSThomas Huth     } else {
52fcf5ef2aSThomas Huth         *cssid = IOINST_SCHID_CSSID(value);
53fcf5ef2aSThomas Huth         *m = 1;
54fcf5ef2aSThomas Huth     }
55fcf5ef2aSThomas Huth     *ssid = IOINST_SCHID_SSID(value);
56fcf5ef2aSThomas Huth     *schid = IOINST_SCHID_NR(value);
57fcf5ef2aSThomas Huth     return 0;
58fcf5ef2aSThomas Huth }
59fcf5ef2aSThomas Huth 
ioinst_handle_xsch(S390CPU * cpu,uint64_t reg1,uintptr_t ra)601b98fb99SDavid Hildenbrand void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
61fcf5ef2aSThomas Huth {
62fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
63fcf5ef2aSThomas Huth     SubchDev *sch;
64fcf5ef2aSThomas Huth 
65fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
6677b703f8SRichard Henderson         s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
67fcf5ef2aSThomas Huth         return;
68fcf5ef2aSThomas Huth     }
69fcf5ef2aSThomas Huth     trace_ioinst_sch_id("xsch", cssid, ssid, schid);
70fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
7196376408SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
7296376408SHalil Pasic         setcc(cpu, 3);
7396376408SHalil Pasic         return;
74fcf5ef2aSThomas Huth     }
7596376408SHalil Pasic     setcc(cpu, css_do_xsch(sch));
76fcf5ef2aSThomas Huth }
77fcf5ef2aSThomas Huth 
ioinst_handle_csch(S390CPU * cpu,uint64_t reg1,uintptr_t ra)781b98fb99SDavid Hildenbrand void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
79fcf5ef2aSThomas Huth {
80fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
81fcf5ef2aSThomas Huth     SubchDev *sch;
82fcf5ef2aSThomas Huth 
83fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
8477b703f8SRichard Henderson         s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
85fcf5ef2aSThomas Huth         return;
86fcf5ef2aSThomas Huth     }
87fcf5ef2aSThomas Huth     trace_ioinst_sch_id("csch", cssid, ssid, schid);
88fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
8977331442SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
9077331442SHalil Pasic         setcc(cpu, 3);
9177331442SHalil Pasic         return;
92fcf5ef2aSThomas Huth     }
9377331442SHalil Pasic     setcc(cpu, css_do_csch(sch));
94fcf5ef2aSThomas Huth }
95fcf5ef2aSThomas Huth 
ioinst_handle_hsch(S390CPU * cpu,uint64_t reg1,uintptr_t ra)961b98fb99SDavid Hildenbrand void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
97fcf5ef2aSThomas Huth {
98fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
99fcf5ef2aSThomas Huth     SubchDev *sch;
100fcf5ef2aSThomas Huth 
101fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
10277b703f8SRichard Henderson         s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
103fcf5ef2aSThomas Huth         return;
104fcf5ef2aSThomas Huth     }
105fcf5ef2aSThomas Huth     trace_ioinst_sch_id("hsch", cssid, ssid, schid);
106fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
107ae9f1be3SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
108ae9f1be3SHalil Pasic         setcc(cpu, 3);
109ae9f1be3SHalil Pasic         return;
110fcf5ef2aSThomas Huth     }
111ae9f1be3SHalil Pasic     setcc(cpu, css_do_hsch(sch));
112fcf5ef2aSThomas Huth }
113fcf5ef2aSThomas Huth 
ioinst_schib_valid(SCHIB * schib)114fcf5ef2aSThomas Huth static int ioinst_schib_valid(SCHIB *schib)
115fcf5ef2aSThomas Huth {
116fcf5ef2aSThomas Huth     if ((be16_to_cpu(schib->pmcw.flags) & PMCW_FLAGS_MASK_INVALID) ||
117fcf5ef2aSThomas Huth         (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_INVALID)) {
118fcf5ef2aSThomas Huth         return 0;
119fcf5ef2aSThomas Huth     }
120fcf5ef2aSThomas Huth     /* Disallow extended measurements for now. */
121fcf5ef2aSThomas Huth     if (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_XMWME) {
122fcf5ef2aSThomas Huth         return 0;
123fcf5ef2aSThomas Huth     }
124a54b8ac3SPierre Morel     /* for MB format 1 bits 26-31 of word 11 must be 0 */
125a54b8ac3SPierre Morel     /* MBA uses words 10 and 11, it means align on 2**6 */
1260dd05d06SDavid Hildenbrand     if ((be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_MBFC) &&
127a54b8ac3SPierre Morel         (be64_to_cpu(schib->mba) & 0x03fUL)) {
128a54b8ac3SPierre Morel         return 0;
129a54b8ac3SPierre Morel     }
130fcf5ef2aSThomas Huth     return 1;
131fcf5ef2aSThomas Huth }
132fcf5ef2aSThomas Huth 
ioinst_handle_msch(S390CPU * cpu,uint64_t reg1,uint32_t ipb,uintptr_t ra)1331b98fb99SDavid Hildenbrand void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
134fcf5ef2aSThomas Huth {
135fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
136fcf5ef2aSThomas Huth     SubchDev *sch;
137fcf5ef2aSThomas Huth     SCHIB schib;
138fcf5ef2aSThomas Huth     uint64_t addr;
139fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
140fcf5ef2aSThomas Huth     uint8_t ar;
141fcf5ef2aSThomas Huth 
142c10b7087SJanosch Frank     addr = get_address_from_regs(env, ipb, &ar);
143fcf5ef2aSThomas Huth     if (addr & 3) {
14477b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
145fcf5ef2aSThomas Huth         return;
146fcf5ef2aSThomas Huth     }
147fcc10c14SJanosch Frank     if (s390_is_pv()) {
148fcc10c14SJanosch Frank         s390_cpu_pv_mem_read(cpu, addr, &schib, sizeof(schib));
149fcc10c14SJanosch Frank     } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
15098ee9bedSDavid Hildenbrand         s390_cpu_virt_mem_handle_exc(cpu, ra);
151fcf5ef2aSThomas Huth         return;
152fcf5ef2aSThomas Huth     }
153fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
154fcf5ef2aSThomas Huth         !ioinst_schib_valid(&schib)) {
15577b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
156fcf5ef2aSThomas Huth         return;
157fcf5ef2aSThomas Huth     }
158fcf5ef2aSThomas Huth     trace_ioinst_sch_id("msch", cssid, ssid, schid);
159fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
1606bb6f194SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
1616bb6f194SHalil Pasic         setcc(cpu, 3);
1626bb6f194SHalil Pasic         return;
163fcf5ef2aSThomas Huth     }
1646bb6f194SHalil Pasic     setcc(cpu, css_do_msch(sch, &schib));
165fcf5ef2aSThomas Huth }
166fcf5ef2aSThomas Huth 
copy_orb_from_guest(ORB * dest,const ORB * src)167fcf5ef2aSThomas Huth static void copy_orb_from_guest(ORB *dest, const ORB *src)
168fcf5ef2aSThomas Huth {
169fcf5ef2aSThomas Huth     dest->intparm = be32_to_cpu(src->intparm);
170fcf5ef2aSThomas Huth     dest->ctrl0 = be16_to_cpu(src->ctrl0);
171fcf5ef2aSThomas Huth     dest->lpm = src->lpm;
172fcf5ef2aSThomas Huth     dest->ctrl1 = src->ctrl1;
173fcf5ef2aSThomas Huth     dest->cpa = be32_to_cpu(src->cpa);
174fcf5ef2aSThomas Huth }
175fcf5ef2aSThomas Huth 
ioinst_orb_valid(ORB * orb)176fcf5ef2aSThomas Huth static int ioinst_orb_valid(ORB *orb)
177fcf5ef2aSThomas Huth {
178fcf5ef2aSThomas Huth     if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
179fcf5ef2aSThomas Huth         (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
180fcf5ef2aSThomas Huth         return 0;
181fcf5ef2aSThomas Huth     }
1824e19b57bSCornelia Huck     /* We don't support MIDA. */
1834e19b57bSCornelia Huck     if (orb->ctrl1 & ORB_CTRL1_MASK_MIDAW) {
1844e19b57bSCornelia Huck         return 0;
1854e19b57bSCornelia Huck     }
186fcf5ef2aSThomas Huth     if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
187fcf5ef2aSThomas Huth         return 0;
188fcf5ef2aSThomas Huth     }
189fcf5ef2aSThomas Huth     return 1;
190fcf5ef2aSThomas Huth }
191fcf5ef2aSThomas Huth 
ioinst_handle_ssch(S390CPU * cpu,uint64_t reg1,uint32_t ipb,uintptr_t ra)1921b98fb99SDavid Hildenbrand void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
193fcf5ef2aSThomas Huth {
194fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
195fcf5ef2aSThomas Huth     SubchDev *sch;
196fcf5ef2aSThomas Huth     ORB orig_orb, orb;
197fcf5ef2aSThomas Huth     uint64_t addr;
198fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
199fcf5ef2aSThomas Huth     uint8_t ar;
200fcf5ef2aSThomas Huth 
201c10b7087SJanosch Frank     addr = get_address_from_regs(env, ipb, &ar);
202fcf5ef2aSThomas Huth     if (addr & 3) {
20377b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
204fcf5ef2aSThomas Huth         return;
205fcf5ef2aSThomas Huth     }
206fcc10c14SJanosch Frank     if (s390_is_pv()) {
207fcc10c14SJanosch Frank         s390_cpu_pv_mem_read(cpu, addr, &orig_orb, sizeof(orb));
208fcc10c14SJanosch Frank     } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
20998ee9bedSDavid Hildenbrand         s390_cpu_virt_mem_handle_exc(cpu, ra);
210fcf5ef2aSThomas Huth         return;
211fcf5ef2aSThomas Huth     }
212fcf5ef2aSThomas Huth     copy_orb_from_guest(&orb, &orig_orb);
213fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
214fcf5ef2aSThomas Huth         !ioinst_orb_valid(&orb)) {
21577b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
216fcf5ef2aSThomas Huth         return;
217fcf5ef2aSThomas Huth     }
218fcf5ef2aSThomas Huth     trace_ioinst_sch_id("ssch", cssid, ssid, schid);
219fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
22066dc50f7SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
22166dc50f7SHalil Pasic         setcc(cpu, 3);
222bab482d7SXiao Feng Ren         return;
223fcf5ef2aSThomas Huth     }
22466dc50f7SHalil Pasic     setcc(cpu, css_do_ssch(sch, &orb));
225fcf5ef2aSThomas Huth }
226fcf5ef2aSThomas Huth 
ioinst_handle_stcrw(S390CPU * cpu,uint32_t ipb,uintptr_t ra)2271b98fb99SDavid Hildenbrand void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
228fcf5ef2aSThomas Huth {
229fcf5ef2aSThomas Huth     CRW crw;
230fcf5ef2aSThomas Huth     uint64_t addr;
231fcf5ef2aSThomas Huth     int cc;
232fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
233fcf5ef2aSThomas Huth     uint8_t ar;
234fcf5ef2aSThomas Huth 
235c10b7087SJanosch Frank     addr = get_address_from_regs(env, ipb, &ar);
236fcf5ef2aSThomas Huth     if (addr & 3) {
23777b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
238fcf5ef2aSThomas Huth         return;
239fcf5ef2aSThomas Huth     }
240fcf5ef2aSThomas Huth 
241fcf5ef2aSThomas Huth     cc = css_do_stcrw(&crw);
242fcf5ef2aSThomas Huth     /* 0 - crw stored, 1 - zeroes stored */
243fcf5ef2aSThomas Huth 
244fcc10c14SJanosch Frank     if (s390_is_pv()) {
245fcc10c14SJanosch Frank         s390_cpu_pv_mem_write(cpu, addr, &crw, sizeof(crw));
246fcc10c14SJanosch Frank         setcc(cpu, cc);
247fcc10c14SJanosch Frank     } else {
248fcf5ef2aSThomas Huth         if (s390_cpu_virt_mem_write(cpu, addr, ar, &crw, sizeof(crw)) == 0) {
249fcf5ef2aSThomas Huth             setcc(cpu, cc);
25098ee9bedSDavid Hildenbrand         } else {
25198ee9bedSDavid Hildenbrand             if (cc == 0) {
25298ee9bedSDavid Hildenbrand                 /* Write failed: requeue CRW since STCRW is suppressing */
253fcf5ef2aSThomas Huth                 css_undo_stcrw(&crw);
254fcf5ef2aSThomas Huth             }
25598ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
25698ee9bedSDavid Hildenbrand         }
257fcf5ef2aSThomas Huth     }
258fcc10c14SJanosch Frank }
259fcf5ef2aSThomas Huth 
ioinst_handle_stsch(S390CPU * cpu,uint64_t reg1,uint32_t ipb,uintptr_t ra)2601b98fb99SDavid Hildenbrand void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
2611b98fb99SDavid Hildenbrand                          uintptr_t ra)
262fcf5ef2aSThomas Huth {
263fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
264fcf5ef2aSThomas Huth     SubchDev *sch;
265fcf5ef2aSThomas Huth     uint64_t addr;
266fcf5ef2aSThomas Huth     int cc;
267fcf5ef2aSThomas Huth     SCHIB schib;
268fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
269fcf5ef2aSThomas Huth     uint8_t ar;
270fcf5ef2aSThomas Huth 
271c10b7087SJanosch Frank     addr = get_address_from_regs(env, ipb, &ar);
272fcf5ef2aSThomas Huth     if (addr & 3) {
27377b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
274fcf5ef2aSThomas Huth         return;
275fcf5ef2aSThomas Huth     }
276fcf5ef2aSThomas Huth 
277fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
278fcf5ef2aSThomas Huth         /*
279fcc10c14SJanosch Frank          * The Ultravisor checks schid bit 16 to be one and bits 0-12
280fcc10c14SJanosch Frank          * to be 0 and injects a operand exception itself.
281fcc10c14SJanosch Frank          *
282fcc10c14SJanosch Frank          * Hence we should never end up here.
283fcc10c14SJanosch Frank          */
284fcc10c14SJanosch Frank         g_assert(!s390_is_pv());
285fcc10c14SJanosch Frank         /*
286fcf5ef2aSThomas Huth          * As operand exceptions have a lower priority than access exceptions,
2879323e79fSPeter Maydell          * we check whether the memory area is writable (injecting the
28844ee69eaSThomas Huth          * access exception if it is not) first.
289fcf5ef2aSThomas Huth          */
290fcf5ef2aSThomas Huth         if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) {
29177b703f8SRichard Henderson             s390_program_interrupt(env, PGM_OPERAND, ra);
29298ee9bedSDavid Hildenbrand         } else {
29398ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
294fcf5ef2aSThomas Huth         }
295fcf5ef2aSThomas Huth         return;
296fcf5ef2aSThomas Huth     }
297fcf5ef2aSThomas Huth     trace_ioinst_sch_id("stsch", cssid, ssid, schid);
298fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
299fcf5ef2aSThomas Huth     if (sch) {
300fcf5ef2aSThomas Huth         if (css_subch_visible(sch)) {
30146ea3841SFarhan Ali             cc = css_do_stsch(sch, &schib);
302fcf5ef2aSThomas Huth         } else {
303fcf5ef2aSThomas Huth             /* Indicate no more subchannels in this css/ss */
304fcf5ef2aSThomas Huth             cc = 3;
305fcf5ef2aSThomas Huth         }
306fcf5ef2aSThomas Huth     } else {
307fcf5ef2aSThomas Huth         if (css_schid_final(m, cssid, ssid, schid)) {
308fcf5ef2aSThomas Huth             cc = 3; /* No more subchannels in this css/ss */
309fcf5ef2aSThomas Huth         } else {
310fcf5ef2aSThomas Huth             /* Store an empty schib. */
311fcf5ef2aSThomas Huth             memset(&schib, 0, sizeof(schib));
312fcf5ef2aSThomas Huth             cc = 0;
313fcf5ef2aSThomas Huth         }
314fcf5ef2aSThomas Huth     }
315fcf5ef2aSThomas Huth     if (cc != 3) {
316fcc10c14SJanosch Frank         if (s390_is_pv()) {
317fcc10c14SJanosch Frank             s390_cpu_pv_mem_write(cpu, addr, &schib, sizeof(schib));
318fcc10c14SJanosch Frank         } else if (s390_cpu_virt_mem_write(cpu, addr, ar, &schib,
319fcf5ef2aSThomas Huth                                            sizeof(schib)) != 0) {
32098ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
321fcf5ef2aSThomas Huth             return;
322fcf5ef2aSThomas Huth         }
323fcf5ef2aSThomas Huth     } else {
324fcf5ef2aSThomas Huth         /* Access exceptions have a higher priority than cc3 */
325fcc10c14SJanosch Frank         if (!s390_is_pv() &&
326fcc10c14SJanosch Frank             s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib)) != 0) {
32798ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
328fcf5ef2aSThomas Huth             return;
329fcf5ef2aSThomas Huth         }
330fcf5ef2aSThomas Huth     }
331fcf5ef2aSThomas Huth     setcc(cpu, cc);
332fcf5ef2aSThomas Huth }
333fcf5ef2aSThomas Huth 
ioinst_handle_tsch(S390CPU * cpu,uint64_t reg1,uint32_t ipb,uintptr_t ra)3341b98fb99SDavid Hildenbrand int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
335fcf5ef2aSThomas Huth {
336fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
337fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
338fcf5ef2aSThomas Huth     SubchDev *sch;
339fcf5ef2aSThomas Huth     IRB irb;
340fcf5ef2aSThomas Huth     uint64_t addr;
341fcf5ef2aSThomas Huth     int cc, irb_len;
342fcf5ef2aSThomas Huth     uint8_t ar;
343fcf5ef2aSThomas Huth 
344fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
34577b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
346fcf5ef2aSThomas Huth         return -EIO;
347fcf5ef2aSThomas Huth     }
348fcf5ef2aSThomas Huth     trace_ioinst_sch_id("tsch", cssid, ssid, schid);
349c10b7087SJanosch Frank     addr = get_address_from_regs(env, ipb, &ar);
350fcf5ef2aSThomas Huth     if (addr & 3) {
35177b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
352fcf5ef2aSThomas Huth         return -EIO;
353fcf5ef2aSThomas Huth     }
354fcf5ef2aSThomas Huth 
355fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
356fcf5ef2aSThomas Huth     if (sch && css_subch_visible(sch)) {
357fcf5ef2aSThomas Huth         cc = css_do_tsch_get_irb(sch, &irb, &irb_len);
358fcf5ef2aSThomas Huth     } else {
359fcf5ef2aSThomas Huth         cc = 3;
360fcf5ef2aSThomas Huth     }
361fcf5ef2aSThomas Huth     /* 0 - status pending, 1 - not status pending, 3 - not operational */
362fcf5ef2aSThomas Huth     if (cc != 3) {
363fcc10c14SJanosch Frank         if (s390_is_pv()) {
364fcc10c14SJanosch Frank             s390_cpu_pv_mem_write(cpu, addr, &irb, irb_len);
365fcc10c14SJanosch Frank         } else if (s390_cpu_virt_mem_write(cpu, addr, ar, &irb, irb_len) != 0) {
36698ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
367fcf5ef2aSThomas Huth             return -EFAULT;
368fcf5ef2aSThomas Huth         }
369fcf5ef2aSThomas Huth         css_do_tsch_update_subch(sch);
370fcf5ef2aSThomas Huth     } else {
371fcf5ef2aSThomas Huth         irb_len = sizeof(irb) - sizeof(irb.emw);
372fcf5ef2aSThomas Huth         /* Access exceptions have a higher priority than cc3 */
373fcc10c14SJanosch Frank         if (!s390_is_pv() &&
374fcc10c14SJanosch Frank             s390_cpu_virt_mem_check_write(cpu, addr, ar, irb_len) != 0) {
37598ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
376fcf5ef2aSThomas Huth             return -EFAULT;
377fcf5ef2aSThomas Huth         }
378fcf5ef2aSThomas Huth     }
379fcf5ef2aSThomas Huth 
380fcf5ef2aSThomas Huth     setcc(cpu, cc);
381fcf5ef2aSThomas Huth     return 0;
382fcf5ef2aSThomas Huth }
383fcf5ef2aSThomas Huth 
384fcf5ef2aSThomas Huth typedef struct ChscReq {
385fcf5ef2aSThomas Huth     uint16_t len;
386fcf5ef2aSThomas Huth     uint16_t command;
387fcf5ef2aSThomas Huth     uint32_t param0;
388fcf5ef2aSThomas Huth     uint32_t param1;
389fcf5ef2aSThomas Huth     uint32_t param2;
390fcf5ef2aSThomas Huth } QEMU_PACKED ChscReq;
391fcf5ef2aSThomas Huth 
392fcf5ef2aSThomas Huth typedef struct ChscResp {
393fcf5ef2aSThomas Huth     uint16_t len;
394fcf5ef2aSThomas Huth     uint16_t code;
395fcf5ef2aSThomas Huth     uint32_t param;
396880a7817SPhilippe Mathieu-Daudé     char data[];
397fcf5ef2aSThomas Huth } QEMU_PACKED ChscResp;
398fcf5ef2aSThomas Huth 
399fcf5ef2aSThomas Huth #define CHSC_MIN_RESP_LEN 0x0008
400fcf5ef2aSThomas Huth 
401fcf5ef2aSThomas Huth #define CHSC_SCPD 0x0002
402fcf5ef2aSThomas Huth #define CHSC_SCSC 0x0010
403fcf5ef2aSThomas Huth #define CHSC_SDA  0x0031
404fcf5ef2aSThomas Huth #define CHSC_SEI  0x000e
405fcf5ef2aSThomas Huth 
406fcf5ef2aSThomas Huth #define CHSC_SCPD_0_M 0x20000000
407fcf5ef2aSThomas Huth #define CHSC_SCPD_0_C 0x10000000
408fcf5ef2aSThomas Huth #define CHSC_SCPD_0_FMT 0x0f000000
409fcf5ef2aSThomas Huth #define CHSC_SCPD_0_CSSID 0x00ff0000
410fcf5ef2aSThomas Huth #define CHSC_SCPD_0_RFMT 0x00000f00
411fcf5ef2aSThomas Huth #define CHSC_SCPD_0_RES 0xc000f000
412fcf5ef2aSThomas Huth #define CHSC_SCPD_1_RES 0xffffff00
413fcf5ef2aSThomas Huth #define CHSC_SCPD_01_CHPID 0x000000ff
ioinst_handle_chsc_scpd(ChscReq * req,ChscResp * res)414fcf5ef2aSThomas Huth static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
415fcf5ef2aSThomas Huth {
416fcf5ef2aSThomas Huth     uint16_t len = be16_to_cpu(req->len);
417fcf5ef2aSThomas Huth     uint32_t param0 = be32_to_cpu(req->param0);
418fcf5ef2aSThomas Huth     uint32_t param1 = be32_to_cpu(req->param1);
419fcf5ef2aSThomas Huth     uint16_t resp_code;
420fcf5ef2aSThomas Huth     int rfmt;
421fcf5ef2aSThomas Huth     uint16_t cssid;
422fcf5ef2aSThomas Huth     uint8_t f_chpid, l_chpid;
423fcf5ef2aSThomas Huth     int desc_size;
424fcf5ef2aSThomas Huth     int m;
425fcf5ef2aSThomas Huth 
426fcf5ef2aSThomas Huth     rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
427fcf5ef2aSThomas Huth     if ((rfmt == 0) ||  (rfmt == 1)) {
428fcf5ef2aSThomas Huth         rfmt = !!(param0 & CHSC_SCPD_0_C);
429fcf5ef2aSThomas Huth     }
430fcf5ef2aSThomas Huth     if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
431fcf5ef2aSThomas Huth         (param1 & CHSC_SCPD_1_RES) || req->param2) {
432fcf5ef2aSThomas Huth         resp_code = 0x0003;
433fcf5ef2aSThomas Huth         goto out_err;
434fcf5ef2aSThomas Huth     }
435fcf5ef2aSThomas Huth     if (param0 & CHSC_SCPD_0_FMT) {
436fcf5ef2aSThomas Huth         resp_code = 0x0007;
437fcf5ef2aSThomas Huth         goto out_err;
438fcf5ef2aSThomas Huth     }
439fcf5ef2aSThomas Huth     cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
440fcf5ef2aSThomas Huth     m = param0 & CHSC_SCPD_0_M;
441fcf5ef2aSThomas Huth     if (cssid != 0) {
442fcf5ef2aSThomas Huth         if (!m || !css_present(cssid)) {
443fcf5ef2aSThomas Huth             resp_code = 0x0008;
444fcf5ef2aSThomas Huth             goto out_err;
445fcf5ef2aSThomas Huth         }
446fcf5ef2aSThomas Huth     }
447fcf5ef2aSThomas Huth     f_chpid = param0 & CHSC_SCPD_01_CHPID;
448fcf5ef2aSThomas Huth     l_chpid = param1 & CHSC_SCPD_01_CHPID;
449fcf5ef2aSThomas Huth     if (l_chpid < f_chpid) {
450fcf5ef2aSThomas Huth         resp_code = 0x0003;
451fcf5ef2aSThomas Huth         goto out_err;
452fcf5ef2aSThomas Huth     }
453fcf5ef2aSThomas Huth     /* css_collect_chp_desc() is endian-aware */
454fcf5ef2aSThomas Huth     desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
455fcf5ef2aSThomas Huth                                      &res->data);
456fcf5ef2aSThomas Huth     res->code = cpu_to_be16(0x0001);
457fcf5ef2aSThomas Huth     res->len = cpu_to_be16(8 + desc_size);
458fcf5ef2aSThomas Huth     res->param = cpu_to_be32(rfmt);
459fcf5ef2aSThomas Huth     return;
460fcf5ef2aSThomas Huth 
461fcf5ef2aSThomas Huth   out_err:
462fcf5ef2aSThomas Huth     res->code = cpu_to_be16(resp_code);
463fcf5ef2aSThomas Huth     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
464fcf5ef2aSThomas Huth     res->param = cpu_to_be32(rfmt);
465fcf5ef2aSThomas Huth }
466fcf5ef2aSThomas Huth 
467fcf5ef2aSThomas Huth #define CHSC_SCSC_0_M 0x20000000
468fcf5ef2aSThomas Huth #define CHSC_SCSC_0_FMT 0x000f0000
469fcf5ef2aSThomas Huth #define CHSC_SCSC_0_CSSID 0x0000ff00
470fcf5ef2aSThomas Huth #define CHSC_SCSC_0_RES 0xdff000ff
ioinst_handle_chsc_scsc(ChscReq * req,ChscResp * res)471fcf5ef2aSThomas Huth static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
472fcf5ef2aSThomas Huth {
473fcf5ef2aSThomas Huth     uint16_t len = be16_to_cpu(req->len);
474fcf5ef2aSThomas Huth     uint32_t param0 = be32_to_cpu(req->param0);
475fcf5ef2aSThomas Huth     uint8_t cssid;
476fcf5ef2aSThomas Huth     uint16_t resp_code;
477fcf5ef2aSThomas Huth     uint32_t general_chars[510];
478fcf5ef2aSThomas Huth     uint32_t chsc_chars[508];
479fcf5ef2aSThomas Huth 
480fcf5ef2aSThomas Huth     if (len != 0x0010) {
481fcf5ef2aSThomas Huth         resp_code = 0x0003;
482fcf5ef2aSThomas Huth         goto out_err;
483fcf5ef2aSThomas Huth     }
484fcf5ef2aSThomas Huth 
485fcf5ef2aSThomas Huth     if (param0 & CHSC_SCSC_0_FMT) {
486fcf5ef2aSThomas Huth         resp_code = 0x0007;
487fcf5ef2aSThomas Huth         goto out_err;
488fcf5ef2aSThomas Huth     }
489fcf5ef2aSThomas Huth     cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
490fcf5ef2aSThomas Huth     if (cssid != 0) {
491fcf5ef2aSThomas Huth         if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
492fcf5ef2aSThomas Huth             resp_code = 0x0008;
493fcf5ef2aSThomas Huth             goto out_err;
494fcf5ef2aSThomas Huth         }
495fcf5ef2aSThomas Huth     }
496fcf5ef2aSThomas Huth     if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
497fcf5ef2aSThomas Huth         resp_code = 0x0003;
498fcf5ef2aSThomas Huth         goto out_err;
499fcf5ef2aSThomas Huth     }
500fcf5ef2aSThomas Huth     res->code = cpu_to_be16(0x0001);
501fcf5ef2aSThomas Huth     res->len = cpu_to_be16(4080);
502fcf5ef2aSThomas Huth     res->param = 0;
503fcf5ef2aSThomas Huth 
504fcf5ef2aSThomas Huth     memset(general_chars, 0, sizeof(general_chars));
505fcf5ef2aSThomas Huth     memset(chsc_chars, 0, sizeof(chsc_chars));
506fcf5ef2aSThomas Huth 
507fcf5ef2aSThomas Huth     general_chars[0] = cpu_to_be32(0x03000000);
508fcf5ef2aSThomas Huth     general_chars[1] = cpu_to_be32(0x00079000);
509fcf5ef2aSThomas Huth     general_chars[3] = cpu_to_be32(0x00080000);
510fcf5ef2aSThomas Huth 
511fcf5ef2aSThomas Huth     chsc_chars[0] = cpu_to_be32(0x40000000);
512fcf5ef2aSThomas Huth     chsc_chars[3] = cpu_to_be32(0x00040000);
513fcf5ef2aSThomas Huth 
514fcf5ef2aSThomas Huth     memcpy(res->data, general_chars, sizeof(general_chars));
515fcf5ef2aSThomas Huth     memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
516fcf5ef2aSThomas Huth     return;
517fcf5ef2aSThomas Huth 
518fcf5ef2aSThomas Huth   out_err:
519fcf5ef2aSThomas Huth     res->code = cpu_to_be16(resp_code);
520fcf5ef2aSThomas Huth     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
521fcf5ef2aSThomas Huth     res->param = 0;
522fcf5ef2aSThomas Huth }
523fcf5ef2aSThomas Huth 
524fcf5ef2aSThomas Huth #define CHSC_SDA_0_FMT 0x0f000000
525fcf5ef2aSThomas Huth #define CHSC_SDA_0_OC 0x0000ffff
526fcf5ef2aSThomas Huth #define CHSC_SDA_0_RES 0xf0ff0000
527fcf5ef2aSThomas Huth #define CHSC_SDA_OC_MCSSE 0x0
528fcf5ef2aSThomas Huth #define CHSC_SDA_OC_MSS 0x2
ioinst_handle_chsc_sda(ChscReq * req,ChscResp * res)529fcf5ef2aSThomas Huth static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
530fcf5ef2aSThomas Huth {
531fcf5ef2aSThomas Huth     uint16_t resp_code = 0x0001;
532fcf5ef2aSThomas Huth     uint16_t len = be16_to_cpu(req->len);
533fcf5ef2aSThomas Huth     uint32_t param0 = be32_to_cpu(req->param0);
534fcf5ef2aSThomas Huth     uint16_t oc;
535fcf5ef2aSThomas Huth     int ret;
536fcf5ef2aSThomas Huth 
537fcf5ef2aSThomas Huth     if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
538fcf5ef2aSThomas Huth         resp_code = 0x0003;
539fcf5ef2aSThomas Huth         goto out;
540fcf5ef2aSThomas Huth     }
541fcf5ef2aSThomas Huth 
542fcf5ef2aSThomas Huth     if (param0 & CHSC_SDA_0_FMT) {
543fcf5ef2aSThomas Huth         resp_code = 0x0007;
544fcf5ef2aSThomas Huth         goto out;
545fcf5ef2aSThomas Huth     }
546fcf5ef2aSThomas Huth 
547fcf5ef2aSThomas Huth     oc = param0 & CHSC_SDA_0_OC;
548fcf5ef2aSThomas Huth     switch (oc) {
549fcf5ef2aSThomas Huth     case CHSC_SDA_OC_MCSSE:
550fcf5ef2aSThomas Huth         ret = css_enable_mcsse();
551fcf5ef2aSThomas Huth         if (ret == -EINVAL) {
552fcf5ef2aSThomas Huth             resp_code = 0x0101;
553fcf5ef2aSThomas Huth             goto out;
554fcf5ef2aSThomas Huth         }
555fcf5ef2aSThomas Huth         break;
556fcf5ef2aSThomas Huth     case CHSC_SDA_OC_MSS:
557fcf5ef2aSThomas Huth         ret = css_enable_mss();
558fcf5ef2aSThomas Huth         if (ret == -EINVAL) {
559fcf5ef2aSThomas Huth             resp_code = 0x0101;
560fcf5ef2aSThomas Huth             goto out;
561fcf5ef2aSThomas Huth         }
562fcf5ef2aSThomas Huth         break;
563fcf5ef2aSThomas Huth     default:
564fcf5ef2aSThomas Huth         resp_code = 0x0003;
565fcf5ef2aSThomas Huth         goto out;
566fcf5ef2aSThomas Huth     }
567fcf5ef2aSThomas Huth 
568fcf5ef2aSThomas Huth out:
569fcf5ef2aSThomas Huth     res->code = cpu_to_be16(resp_code);
570fcf5ef2aSThomas Huth     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
571fcf5ef2aSThomas Huth     res->param = 0;
572fcf5ef2aSThomas Huth }
573fcf5ef2aSThomas Huth 
chsc_sei_nt0_get_event(void * res)574fcf5ef2aSThomas Huth static int chsc_sei_nt0_get_event(void *res)
575fcf5ef2aSThomas Huth {
576fcf5ef2aSThomas Huth     /* no events yet */
577fcf5ef2aSThomas Huth     return 1;
578fcf5ef2aSThomas Huth }
579fcf5ef2aSThomas Huth 
chsc_sei_nt0_have_event(void)580fcf5ef2aSThomas Huth static int chsc_sei_nt0_have_event(void)
581fcf5ef2aSThomas Huth {
582fcf5ef2aSThomas Huth     /* no events yet */
583fcf5ef2aSThomas Huth     return 0;
584fcf5ef2aSThomas Huth }
585fcf5ef2aSThomas Huth 
chsc_sei_nt2_get_event(void * res)5861c5deaecSCornelia Huck static int chsc_sei_nt2_get_event(void *res)
5871c5deaecSCornelia Huck {
5881c5deaecSCornelia Huck     if (s390_has_feat(S390_FEAT_ZPCI)) {
5891c5deaecSCornelia Huck         return pci_chsc_sei_nt2_get_event(res);
5901c5deaecSCornelia Huck     }
5911c5deaecSCornelia Huck     return 1;
5921c5deaecSCornelia Huck }
5931c5deaecSCornelia Huck 
chsc_sei_nt2_have_event(void)5941c5deaecSCornelia Huck static int chsc_sei_nt2_have_event(void)
5951c5deaecSCornelia Huck {
5961c5deaecSCornelia Huck     if (s390_has_feat(S390_FEAT_ZPCI)) {
5971c5deaecSCornelia Huck         return pci_chsc_sei_nt2_have_event();
5981c5deaecSCornelia Huck     }
5991c5deaecSCornelia Huck     return 0;
6001c5deaecSCornelia Huck }
6011c5deaecSCornelia Huck 
602fcf5ef2aSThomas Huth #define CHSC_SEI_NT0    (1ULL << 63)
603fcf5ef2aSThomas Huth #define CHSC_SEI_NT2    (1ULL << 61)
ioinst_handle_chsc_sei(ChscReq * req,ChscResp * res)604fcf5ef2aSThomas Huth static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res)
605fcf5ef2aSThomas Huth {
606*1d94eafdSPhilippe Mathieu-Daudé     uint64_t selection_mask = ldq_be_p(&req->param1);
607fcf5ef2aSThomas Huth     uint8_t *res_flags = (uint8_t *)res->data;
608fcf5ef2aSThomas Huth     int have_event = 0;
609fcf5ef2aSThomas Huth     int have_more = 0;
610fcf5ef2aSThomas Huth 
611fcf5ef2aSThomas Huth     /* regarding architecture nt0 can not be masked */
612fcf5ef2aSThomas Huth     have_event = !chsc_sei_nt0_get_event(res);
613fcf5ef2aSThomas Huth     have_more = chsc_sei_nt0_have_event();
614fcf5ef2aSThomas Huth 
615fcf5ef2aSThomas Huth     if (selection_mask & CHSC_SEI_NT2) {
616fcf5ef2aSThomas Huth         if (!have_event) {
617fcf5ef2aSThomas Huth             have_event = !chsc_sei_nt2_get_event(res);
618fcf5ef2aSThomas Huth         }
619fcf5ef2aSThomas Huth 
620fcf5ef2aSThomas Huth         if (!have_more) {
621fcf5ef2aSThomas Huth             have_more = chsc_sei_nt2_have_event();
622fcf5ef2aSThomas Huth         }
623fcf5ef2aSThomas Huth     }
624fcf5ef2aSThomas Huth 
625fcf5ef2aSThomas Huth     if (have_event) {
626fcf5ef2aSThomas Huth         res->code = cpu_to_be16(0x0001);
627fcf5ef2aSThomas Huth         if (have_more) {
628fcf5ef2aSThomas Huth             (*res_flags) |= 0x80;
629fcf5ef2aSThomas Huth         } else {
630fcf5ef2aSThomas Huth             (*res_flags) &= ~0x80;
631fcf5ef2aSThomas Huth             css_clear_sei_pending();
632fcf5ef2aSThomas Huth         }
633fcf5ef2aSThomas Huth     } else {
634fcf5ef2aSThomas Huth         res->code = cpu_to_be16(0x0005);
635fcf5ef2aSThomas Huth         res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
636fcf5ef2aSThomas Huth     }
637fcf5ef2aSThomas Huth }
638fcf5ef2aSThomas Huth 
ioinst_handle_chsc_unimplemented(ChscResp * res)639fcf5ef2aSThomas Huth static void ioinst_handle_chsc_unimplemented(ChscResp *res)
640fcf5ef2aSThomas Huth {
641fcf5ef2aSThomas Huth     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
642fcf5ef2aSThomas Huth     res->code = cpu_to_be16(0x0004);
643fcf5ef2aSThomas Huth     res->param = 0;
644fcf5ef2aSThomas Huth }
645fcf5ef2aSThomas Huth 
ioinst_handle_chsc(S390CPU * cpu,uint32_t ipb,uintptr_t ra)6461b98fb99SDavid Hildenbrand void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
647fcf5ef2aSThomas Huth {
648fcf5ef2aSThomas Huth     ChscReq *req;
649fcf5ef2aSThomas Huth     ChscResp *res;
650c10b7087SJanosch Frank     uint64_t addr = 0;
651fcf5ef2aSThomas Huth     int reg;
652fcf5ef2aSThomas Huth     uint16_t len;
653fcf5ef2aSThomas Huth     uint16_t command;
654fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
655fcf5ef2aSThomas Huth     uint8_t buf[TARGET_PAGE_SIZE];
656fcf5ef2aSThomas Huth 
657fcf5ef2aSThomas Huth     trace_ioinst("chsc");
658fcf5ef2aSThomas Huth     reg = (ipb >> 20) & 0x00f;
659c10b7087SJanosch Frank     if (!s390_is_pv()) {
660fcf5ef2aSThomas Huth         addr = env->regs[reg];
661c10b7087SJanosch Frank     }
662fcf5ef2aSThomas Huth     /* Page boundary? */
663fcf5ef2aSThomas Huth     if (addr & 0xfff) {
66477b703f8SRichard Henderson         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
665fcf5ef2aSThomas Huth         return;
666fcf5ef2aSThomas Huth     }
667fcf5ef2aSThomas Huth     /*
668fcf5ef2aSThomas Huth      * Reading sizeof(ChscReq) bytes is currently enough for all of our
669fcf5ef2aSThomas Huth      * present CHSC sub-handlers ... if we ever need more, we should take
670fcf5ef2aSThomas Huth      * care of req->len here first.
671fcf5ef2aSThomas Huth      */
672fcc10c14SJanosch Frank     if (s390_is_pv()) {
673fcc10c14SJanosch Frank         s390_cpu_pv_mem_read(cpu, addr, buf, sizeof(ChscReq));
674fcc10c14SJanosch Frank     } else if (s390_cpu_virt_mem_read(cpu, addr, reg, buf, sizeof(ChscReq))) {
67598ee9bedSDavid Hildenbrand         s390_cpu_virt_mem_handle_exc(cpu, ra);
676fcf5ef2aSThomas Huth         return;
677fcf5ef2aSThomas Huth     }
678fcf5ef2aSThomas Huth     req = (ChscReq *)buf;
679fcf5ef2aSThomas Huth     len = be16_to_cpu(req->len);
680fcf5ef2aSThomas Huth     /* Length field valid? */
681fcf5ef2aSThomas Huth     if ((len < 16) || (len > 4088) || (len & 7)) {
68277b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
683fcf5ef2aSThomas Huth         return;
684fcf5ef2aSThomas Huth     }
685fcf5ef2aSThomas Huth     memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
686fcf5ef2aSThomas Huth     res = (void *)((char *)req + len);
687fcf5ef2aSThomas Huth     command = be16_to_cpu(req->command);
688fcf5ef2aSThomas Huth     trace_ioinst_chsc_cmd(command, len);
689fcf5ef2aSThomas Huth     switch (command) {
690fcf5ef2aSThomas Huth     case CHSC_SCSC:
691fcf5ef2aSThomas Huth         ioinst_handle_chsc_scsc(req, res);
692fcf5ef2aSThomas Huth         break;
693fcf5ef2aSThomas Huth     case CHSC_SCPD:
694fcf5ef2aSThomas Huth         ioinst_handle_chsc_scpd(req, res);
695fcf5ef2aSThomas Huth         break;
696fcf5ef2aSThomas Huth     case CHSC_SDA:
697fcf5ef2aSThomas Huth         ioinst_handle_chsc_sda(req, res);
698fcf5ef2aSThomas Huth         break;
699fcf5ef2aSThomas Huth     case CHSC_SEI:
700fcf5ef2aSThomas Huth         ioinst_handle_chsc_sei(req, res);
701fcf5ef2aSThomas Huth         break;
702fcf5ef2aSThomas Huth     default:
703fcf5ef2aSThomas Huth         ioinst_handle_chsc_unimplemented(res);
704fcf5ef2aSThomas Huth         break;
705fcf5ef2aSThomas Huth     }
706fcf5ef2aSThomas Huth 
707fcc10c14SJanosch Frank     if (s390_is_pv()) {
708fcc10c14SJanosch Frank         s390_cpu_pv_mem_write(cpu, addr + len, res, be16_to_cpu(res->len));
709fcc10c14SJanosch Frank         setcc(cpu, 0);    /* Command execution complete */
710fcc10c14SJanosch Frank     } else {
711fcf5ef2aSThomas Huth         if (!s390_cpu_virt_mem_write(cpu, addr + len, reg, res,
712fcf5ef2aSThomas Huth                                      be16_to_cpu(res->len))) {
713fcf5ef2aSThomas Huth             setcc(cpu, 0);    /* Command execution complete */
71498ee9bedSDavid Hildenbrand         } else {
71598ee9bedSDavid Hildenbrand             s390_cpu_virt_mem_handle_exc(cpu, ra);
716fcf5ef2aSThomas Huth         }
717fcf5ef2aSThomas Huth     }
718fcc10c14SJanosch Frank }
719fcf5ef2aSThomas Huth 
720fcf5ef2aSThomas Huth #define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc)
721fcf5ef2aSThomas Huth #define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28)
722fcf5ef2aSThomas Huth #define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1)
723fcf5ef2aSThomas Huth #define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001)
724fcf5ef2aSThomas Huth 
ioinst_handle_schm(S390CPU * cpu,uint64_t reg1,uint64_t reg2,uint32_t ipb,uintptr_t ra)725fcf5ef2aSThomas Huth void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
7261b98fb99SDavid Hildenbrand                         uint32_t ipb, uintptr_t ra)
727fcf5ef2aSThomas Huth {
728fcf5ef2aSThomas Huth     uint8_t mbk;
729fcf5ef2aSThomas Huth     int update;
730fcf5ef2aSThomas Huth     int dct;
731fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
732fcf5ef2aSThomas Huth 
733fcf5ef2aSThomas Huth     trace_ioinst("schm");
734fcf5ef2aSThomas Huth 
735fcf5ef2aSThomas Huth     if (SCHM_REG1_RES(reg1)) {
73677b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
737fcf5ef2aSThomas Huth         return;
738fcf5ef2aSThomas Huth     }
739fcf5ef2aSThomas Huth 
740fcf5ef2aSThomas Huth     mbk = SCHM_REG1_MBK(reg1);
741fcf5ef2aSThomas Huth     update = SCHM_REG1_UPD(reg1);
742fcf5ef2aSThomas Huth     dct = SCHM_REG1_DCT(reg1);
743fcf5ef2aSThomas Huth 
744fcf5ef2aSThomas Huth     if (update && (reg2 & 0x000000000000001f)) {
74577b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
746fcf5ef2aSThomas Huth         return;
747fcf5ef2aSThomas Huth     }
748fcf5ef2aSThomas Huth 
749fcf5ef2aSThomas Huth     css_do_schm(mbk, update, dct, update ? reg2 : 0);
750fcf5ef2aSThomas Huth }
751fcf5ef2aSThomas Huth 
ioinst_handle_rsch(S390CPU * cpu,uint64_t reg1,uintptr_t ra)7521b98fb99SDavid Hildenbrand void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
753fcf5ef2aSThomas Huth {
754fcf5ef2aSThomas Huth     int cssid, ssid, schid, m;
755fcf5ef2aSThomas Huth     SubchDev *sch;
756fcf5ef2aSThomas Huth 
757fcf5ef2aSThomas Huth     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
75877b703f8SRichard Henderson         s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
759fcf5ef2aSThomas Huth         return;
760fcf5ef2aSThomas Huth     }
761fcf5ef2aSThomas Huth     trace_ioinst_sch_id("rsch", cssid, ssid, schid);
762fcf5ef2aSThomas Huth     sch = css_find_subch(m, cssid, ssid, schid);
76366dc50f7SHalil Pasic     if (!sch || !css_subch_visible(sch)) {
76466dc50f7SHalil Pasic         setcc(cpu, 3);
76566dc50f7SHalil Pasic         return;
766fcf5ef2aSThomas Huth     }
76766dc50f7SHalil Pasic     setcc(cpu, css_do_rsch(sch));
768fcf5ef2aSThomas Huth }
769fcf5ef2aSThomas Huth 
770fcf5ef2aSThomas Huth #define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00)
771fcf5ef2aSThomas Huth #define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16)
772fcf5ef2aSThomas Huth #define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff)
ioinst_handle_rchp(S390CPU * cpu,uint64_t reg1,uintptr_t ra)7731b98fb99SDavid Hildenbrand void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
774fcf5ef2aSThomas Huth {
775fcf5ef2aSThomas Huth     int cc;
776fcf5ef2aSThomas Huth     uint8_t cssid;
777fcf5ef2aSThomas Huth     uint8_t chpid;
778fcf5ef2aSThomas Huth     int ret;
779fcf5ef2aSThomas Huth     CPUS390XState *env = &cpu->env;
780fcf5ef2aSThomas Huth 
781fcf5ef2aSThomas Huth     if (RCHP_REG1_RES(reg1)) {
78277b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
783fcf5ef2aSThomas Huth         return;
784fcf5ef2aSThomas Huth     }
785fcf5ef2aSThomas Huth 
786fcf5ef2aSThomas Huth     cssid = RCHP_REG1_CSSID(reg1);
787fcf5ef2aSThomas Huth     chpid = RCHP_REG1_CHPID(reg1);
788fcf5ef2aSThomas Huth 
789fcf5ef2aSThomas Huth     trace_ioinst_chp_id("rchp", cssid, chpid);
790fcf5ef2aSThomas Huth 
791fcf5ef2aSThomas Huth     ret = css_do_rchp(cssid, chpid);
792fcf5ef2aSThomas Huth 
793fcf5ef2aSThomas Huth     switch (ret) {
794fcf5ef2aSThomas Huth     case -ENODEV:
795fcf5ef2aSThomas Huth         cc = 3;
796fcf5ef2aSThomas Huth         break;
797fcf5ef2aSThomas Huth     case -EBUSY:
798fcf5ef2aSThomas Huth         cc = 2;
799fcf5ef2aSThomas Huth         break;
800fcf5ef2aSThomas Huth     case 0:
801fcf5ef2aSThomas Huth         cc = 0;
802fcf5ef2aSThomas Huth         break;
803fcf5ef2aSThomas Huth     default:
804fcf5ef2aSThomas Huth         /* Invalid channel subsystem. */
80577b703f8SRichard Henderson         s390_program_interrupt(env, PGM_OPERAND, ra);
806fcf5ef2aSThomas Huth         return;
807fcf5ef2aSThomas Huth     }
808fcf5ef2aSThomas Huth     setcc(cpu, cc);
809fcf5ef2aSThomas Huth }
810fcf5ef2aSThomas Huth 
811fcf5ef2aSThomas Huth #define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000)
ioinst_handle_sal(S390CPU * cpu,uint64_t reg1,uintptr_t ra)8121b98fb99SDavid Hildenbrand void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
813fcf5ef2aSThomas Huth {
814fcf5ef2aSThomas Huth     /* We do not provide address limit checking, so let's suppress it. */
815fcf5ef2aSThomas Huth     if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
81677b703f8SRichard Henderson         s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
817fcf5ef2aSThomas Huth     }
818fcf5ef2aSThomas Huth }
819