xref: /qemu/target/s390x/helper.c (revision f08b617018e424134a0a012b08253d567c62f7ee)
110ec5117SAlexander Graf /*
210ec5117SAlexander Graf  *  S/390 helpers
310ec5117SAlexander Graf  *
410ec5117SAlexander Graf  *  Copyright (c) 2009 Ulrich Hecht
5d5a43964SAlexander Graf  *  Copyright (c) 2011 Alexander Graf
610ec5117SAlexander Graf  *
710ec5117SAlexander Graf  * This library is free software; you can redistribute it and/or
810ec5117SAlexander Graf  * modify it under the terms of the GNU Lesser General Public
910ec5117SAlexander Graf  * License as published by the Free Software Foundation; either
1010ec5117SAlexander Graf  * version 2 of the License, or (at your option) any later version.
1110ec5117SAlexander Graf  *
1210ec5117SAlexander Graf  * This library is distributed in the hope that it will be useful,
1310ec5117SAlexander Graf  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1410ec5117SAlexander Graf  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1510ec5117SAlexander Graf  * Lesser General Public License for more details.
1610ec5117SAlexander Graf  *
1710ec5117SAlexander Graf  * You should have received a copy of the GNU Lesser General Public
1870539e18SBlue Swirl  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1910ec5117SAlexander Graf  */
2010ec5117SAlexander Graf 
2110ec5117SAlexander Graf #include "cpu.h"
22022c62cbSPaolo Bonzini #include "exec/gdbstub.h"
231de7afc9SPaolo Bonzini #include "qemu/timer.h"
24f08b6170SPaolo Bonzini #include "exec/cpu_ldst.h"
25ef81522bSAlexander Graf #ifndef CONFIG_USER_ONLY
269c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
27ef81522bSAlexander Graf #endif
2810ec5117SAlexander Graf 
29d5a43964SAlexander Graf //#define DEBUG_S390
30d5a43964SAlexander Graf //#define DEBUG_S390_PTE
31d5a43964SAlexander Graf //#define DEBUG_S390_STDOUT
32d5a43964SAlexander Graf 
33d5a43964SAlexander Graf #ifdef DEBUG_S390
34d5a43964SAlexander Graf #ifdef DEBUG_S390_STDOUT
35d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
36d5a43964SAlexander Graf     do { fprintf(stderr, fmt, ## __VA_ARGS__); \
37d5a43964SAlexander Graf          qemu_log(fmt, ##__VA_ARGS__); } while (0)
38d5a43964SAlexander Graf #else
39d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
40d5a43964SAlexander Graf     do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
41d5a43964SAlexander Graf #endif
42d5a43964SAlexander Graf #else
43d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
44d5a43964SAlexander Graf     do { } while (0)
45d5a43964SAlexander Graf #endif
46d5a43964SAlexander Graf 
47d5a43964SAlexander Graf #ifdef DEBUG_S390_PTE
48d5a43964SAlexander Graf #define PTE_DPRINTF DPRINTF
49d5a43964SAlexander Graf #else
50d5a43964SAlexander Graf #define PTE_DPRINTF(fmt, ...) \
51d5a43964SAlexander Graf     do { } while (0)
52d5a43964SAlexander Graf #endif
53d5a43964SAlexander Graf 
54d5a43964SAlexander Graf #ifndef CONFIG_USER_ONLY
558f22e0dfSAndreas Färber void s390x_tod_timer(void *opaque)
56d5a43964SAlexander Graf {
57b8ba6799SAndreas Färber     S390CPU *cpu = opaque;
58b8ba6799SAndreas Färber     CPUS390XState *env = &cpu->env;
59d5a43964SAlexander Graf 
60d5a43964SAlexander Graf     env->pending_int |= INTERRUPT_TOD;
61c3affe56SAndreas Färber     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
62d5a43964SAlexander Graf }
63d5a43964SAlexander Graf 
648f22e0dfSAndreas Färber void s390x_cpu_timer(void *opaque)
65d5a43964SAlexander Graf {
66b8ba6799SAndreas Färber     S390CPU *cpu = opaque;
67b8ba6799SAndreas Färber     CPUS390XState *env = &cpu->env;
68d5a43964SAlexander Graf 
69d5a43964SAlexander Graf     env->pending_int |= INTERRUPT_CPUTIMER;
70c3affe56SAndreas Färber     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
71d5a43964SAlexander Graf }
72d5a43964SAlexander Graf #endif
7310c339a0SAlexander Graf 
74564b863dSAndreas Färber S390CPU *cpu_s390x_init(const char *cpu_model)
7510ec5117SAlexander Graf {
7629e4bcb2SAndreas Färber     S390CPU *cpu;
7710ec5117SAlexander Graf 
7829e4bcb2SAndreas Färber     cpu = S390_CPU(object_new(TYPE_S390_CPU));
791f136632SAndreas Färber 
801f136632SAndreas Färber     object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
811f136632SAndreas Färber 
82564b863dSAndreas Färber     return cpu;
8310ec5117SAlexander Graf }
8410ec5117SAlexander Graf 
85d5a43964SAlexander Graf #if defined(CONFIG_USER_ONLY)
86d5a43964SAlexander Graf 
8797a8ea5aSAndreas Färber void s390_cpu_do_interrupt(CPUState *cs)
88d5a43964SAlexander Graf {
8927103424SAndreas Färber     cs->exception_index = -1;
90d5a43964SAlexander Graf }
91d5a43964SAlexander Graf 
927510454eSAndreas Färber int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
9371e47088SBlue Swirl                               int rw, int mmu_idx)
94d5a43964SAlexander Graf {
957510454eSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
967510454eSAndreas Färber 
9727103424SAndreas Färber     cs->exception_index = EXCP_PGM;
987510454eSAndreas Färber     cpu->env.int_pgm_code = PGM_ADDRESSING;
99d5a103cdSRichard Henderson     /* On real machines this value is dropped into LowMem.  Since this
100d5a103cdSRichard Henderson        is userland, simply put this someplace that cpu_loop can find it.  */
1017510454eSAndreas Färber     cpu->env.__excp_addr = address;
102d5a43964SAlexander Graf     return 1;
103d5a43964SAlexander Graf }
104d5a43964SAlexander Graf 
105b7e516ceSAndreas Färber #else /* !CONFIG_USER_ONLY */
10610c339a0SAlexander Graf 
107d5a43964SAlexander Graf /* Ensure to exit the TB after this call! */
10871e47088SBlue Swirl static void trigger_pgm_exception(CPUS390XState *env, uint32_t code,
109d5a103cdSRichard Henderson                                   uint32_t ilen)
11010c339a0SAlexander Graf {
11127103424SAndreas Färber     CPUState *cs = CPU(s390_env_get_cpu(env));
11227103424SAndreas Färber 
11327103424SAndreas Färber     cs->exception_index = EXCP_PGM;
114d5a43964SAlexander Graf     env->int_pgm_code = code;
115d5a103cdSRichard Henderson     env->int_pgm_ilen = ilen;
116d5a43964SAlexander Graf }
11710c339a0SAlexander Graf 
118a4e3ad19SAndreas Färber static int trans_bits(CPUS390XState *env, uint64_t mode)
119d5a43964SAlexander Graf {
120a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
121d5a43964SAlexander Graf     int bits = 0;
12210c339a0SAlexander Graf 
123d5a43964SAlexander Graf     switch (mode) {
124d5a43964SAlexander Graf     case PSW_ASC_PRIMARY:
125d5a43964SAlexander Graf         bits = 1;
126d5a43964SAlexander Graf         break;
127d5a43964SAlexander Graf     case PSW_ASC_SECONDARY:
128d5a43964SAlexander Graf         bits = 2;
129d5a43964SAlexander Graf         break;
130d5a43964SAlexander Graf     case PSW_ASC_HOME:
131d5a43964SAlexander Graf         bits = 3;
132d5a43964SAlexander Graf         break;
133d5a43964SAlexander Graf     default:
134a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "unknown asc mode\n");
135d5a43964SAlexander Graf         break;
136d5a43964SAlexander Graf     }
13710c339a0SAlexander Graf 
138d5a43964SAlexander Graf     return bits;
139d5a43964SAlexander Graf }
140d5a43964SAlexander Graf 
14171e47088SBlue Swirl static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
14271e47088SBlue Swirl                                uint64_t mode)
143d5a43964SAlexander Graf {
1442efc6be2SAndreas Färber     CPUState *cs = CPU(s390_env_get_cpu(env));
145d5a103cdSRichard Henderson     int ilen = ILEN_LATER_INC;
146d5a43964SAlexander Graf     int bits = trans_bits(env, mode) | 4;
147d5a43964SAlexander Graf 
14871e47088SBlue Swirl     DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
149d5a43964SAlexander Graf 
150f606604fSEdgar E. Iglesias     stq_phys(cs->as,
151f606604fSEdgar E. Iglesias              env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
152d5a103cdSRichard Henderson     trigger_pgm_exception(env, PGM_PROTECTION, ilen);
153d5a43964SAlexander Graf }
154d5a43964SAlexander Graf 
15571e47088SBlue Swirl static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
15671e47088SBlue Swirl                                uint32_t type, uint64_t asc, int rw)
157d5a43964SAlexander Graf {
1582efc6be2SAndreas Färber     CPUState *cs = CPU(s390_env_get_cpu(env));
159d5a103cdSRichard Henderson     int ilen = ILEN_LATER;
160d5a43964SAlexander Graf     int bits = trans_bits(env, asc);
161d5a43964SAlexander Graf 
162d5a103cdSRichard Henderson     /* Code accesses have an undefined ilc.  */
163d5a43964SAlexander Graf     if (rw == 2) {
164d5a103cdSRichard Henderson         ilen = 2;
165d5a43964SAlexander Graf     }
166d5a43964SAlexander Graf 
16771e47088SBlue Swirl     DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
168d5a43964SAlexander Graf 
169f606604fSEdgar E. Iglesias     stq_phys(cs->as,
170f606604fSEdgar E. Iglesias              env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
171d5a103cdSRichard Henderson     trigger_pgm_exception(env, type, ilen);
172d5a43964SAlexander Graf }
173d5a43964SAlexander Graf 
174422f32c5SThomas Huth /**
175422f32c5SThomas Huth  * Translate real address to absolute (= physical)
176422f32c5SThomas Huth  * address by taking care of the prefix mapping.
177422f32c5SThomas Huth  */
178422f32c5SThomas Huth static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
179422f32c5SThomas Huth {
180422f32c5SThomas Huth     if (raddr < 0x2000) {
181422f32c5SThomas Huth         return raddr + env->psa;    /* Map the lowcore. */
182422f32c5SThomas Huth     } else if (raddr >= env->psa && raddr < env->psa + 0x2000) {
183422f32c5SThomas Huth         return raddr - env->psa;    /* Map the 0 page. */
184422f32c5SThomas Huth     }
185422f32c5SThomas Huth     return raddr;
186422f32c5SThomas Huth }
187422f32c5SThomas Huth 
188c4400206SThomas Huth /* Decode page table entry (normal 4KB page) */
189c4400206SThomas Huth static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
190c4400206SThomas Huth                              uint64_t asc, uint64_t asce,
191c4400206SThomas Huth                              target_ulong *raddr, int *flags, int rw)
192c4400206SThomas Huth {
193c4400206SThomas Huth     if (asce & _PAGE_INVALID) {
194c4400206SThomas Huth         DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, asce);
195c4400206SThomas Huth         trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw);
196c4400206SThomas Huth         return -1;
197c4400206SThomas Huth     }
198c4400206SThomas Huth 
199c4400206SThomas Huth     if (asce & _PAGE_RO) {
200c4400206SThomas Huth         *flags &= ~PAGE_WRITE;
201c4400206SThomas Huth     }
202c4400206SThomas Huth 
203c4400206SThomas Huth     *raddr = asce & _ASCE_ORIGIN;
204c4400206SThomas Huth 
205c4400206SThomas Huth     PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, asce);
206c4400206SThomas Huth 
207c4400206SThomas Huth     return 0;
208c4400206SThomas Huth }
209c4400206SThomas Huth 
210c4400206SThomas Huth /* Decode EDAT1 segment frame absolute address (1MB page) */
211c4400206SThomas Huth static int mmu_translate_sfaa(CPUS390XState *env, target_ulong vaddr,
212c4400206SThomas Huth                               uint64_t asc, uint64_t asce, target_ulong *raddr,
213c4400206SThomas Huth                               int *flags, int rw)
214c4400206SThomas Huth {
215c4400206SThomas Huth     if (asce & _SEGMENT_ENTRY_INV) {
216c4400206SThomas Huth         DPRINTF("%s: SEG=0x%" PRIx64 " invalid\n", __func__, asce);
217c4400206SThomas Huth         trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
218c4400206SThomas Huth         return -1;
219c4400206SThomas Huth     }
220c4400206SThomas Huth 
221c4400206SThomas Huth     if (asce & _SEGMENT_ENTRY_RO) {
222c4400206SThomas Huth         *flags &= ~PAGE_WRITE;
223c4400206SThomas Huth     }
224c4400206SThomas Huth 
225c4400206SThomas Huth     *raddr = (asce & 0xfffffffffff00000ULL) | (vaddr & 0xfffff);
226c4400206SThomas Huth 
227c4400206SThomas Huth     PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, asce);
228c4400206SThomas Huth 
229c4400206SThomas Huth     return 0;
230c4400206SThomas Huth }
231c4400206SThomas Huth 
23271e47088SBlue Swirl static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
23371e47088SBlue Swirl                               uint64_t asc, uint64_t asce, int level,
23471e47088SBlue Swirl                               target_ulong *raddr, int *flags, int rw)
235d5a43964SAlexander Graf {
2362efc6be2SAndreas Färber     CPUState *cs = CPU(s390_env_get_cpu(env));
237d5a43964SAlexander Graf     uint64_t offs = 0;
238d5a43964SAlexander Graf     uint64_t origin;
239d5a43964SAlexander Graf     uint64_t new_asce;
240d5a43964SAlexander Graf 
24171e47088SBlue Swirl     PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, asce);
242d5a43964SAlexander Graf 
243d5a43964SAlexander Graf     if (((level != _ASCE_TYPE_SEGMENT) && (asce & _REGION_ENTRY_INV)) ||
244d5a43964SAlexander Graf         ((level == _ASCE_TYPE_SEGMENT) && (asce & _SEGMENT_ENTRY_INV))) {
245d5a43964SAlexander Graf         /* XXX different regions have different faults */
24671e47088SBlue Swirl         DPRINTF("%s: invalid region\n", __func__);
247d5a43964SAlexander Graf         trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
248d5a43964SAlexander Graf         return -1;
249d5a43964SAlexander Graf     }
250d5a43964SAlexander Graf 
251d5a43964SAlexander Graf     if ((level <= _ASCE_TYPE_MASK) && ((asce & _ASCE_TYPE_MASK) != level)) {
252d5a43964SAlexander Graf         trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
253d5a43964SAlexander Graf         return -1;
254d5a43964SAlexander Graf     }
255d5a43964SAlexander Graf 
256d5a43964SAlexander Graf     if (asce & _ASCE_REAL_SPACE) {
257d5a43964SAlexander Graf         /* direct mapping */
258d5a43964SAlexander Graf 
259d5a43964SAlexander Graf         *raddr = vaddr;
260d4c430a8SPaul Brook         return 0;
26110c339a0SAlexander Graf     }
262d5a43964SAlexander Graf 
263d5a43964SAlexander Graf     origin = asce & _ASCE_ORIGIN;
264d5a43964SAlexander Graf 
265d5a43964SAlexander Graf     switch (level) {
266d5a43964SAlexander Graf     case _ASCE_TYPE_REGION1 + 4:
267d5a43964SAlexander Graf         offs = (vaddr >> 50) & 0x3ff8;
268d5a43964SAlexander Graf         break;
269d5a43964SAlexander Graf     case _ASCE_TYPE_REGION1:
270d5a43964SAlexander Graf         offs = (vaddr >> 39) & 0x3ff8;
271d5a43964SAlexander Graf         break;
272d5a43964SAlexander Graf     case _ASCE_TYPE_REGION2:
273d5a43964SAlexander Graf         offs = (vaddr >> 28) & 0x3ff8;
274d5a43964SAlexander Graf         break;
275d5a43964SAlexander Graf     case _ASCE_TYPE_REGION3:
276d5a43964SAlexander Graf         offs = (vaddr >> 17) & 0x3ff8;
277d5a43964SAlexander Graf         break;
278d5a43964SAlexander Graf     case _ASCE_TYPE_SEGMENT:
279d5a43964SAlexander Graf         offs = (vaddr >> 9) & 0x07f8;
280d5a43964SAlexander Graf         origin = asce & _SEGMENT_ENTRY_ORIGIN;
281d5a43964SAlexander Graf         break;
282d5a43964SAlexander Graf     }
283d5a43964SAlexander Graf 
284d5a43964SAlexander Graf     /* XXX region protection flags */
285d5a43964SAlexander Graf     /* *flags &= ~PAGE_WRITE */
286d5a43964SAlexander Graf 
2872c17449bSEdgar E. Iglesias     new_asce = ldq_phys(cs->as, origin + offs);
288d5a43964SAlexander Graf     PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
28971e47088SBlue Swirl                 __func__, origin, offs, new_asce);
290d5a43964SAlexander Graf 
291c4400206SThomas Huth     if (level == _ASCE_TYPE_SEGMENT) {
292c4400206SThomas Huth         /* 4KB page */
293c4400206SThomas Huth         return mmu_translate_pte(env, vaddr, asc, new_asce, raddr, flags, rw);
294c4400206SThomas Huth     } else if (level - 4 == _ASCE_TYPE_SEGMENT &&
295c4400206SThomas Huth                (new_asce & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
296c4400206SThomas Huth         /* 1MB page */
297c4400206SThomas Huth         return mmu_translate_sfaa(env, vaddr, asc, new_asce, raddr, flags, rw);
298c4400206SThomas Huth     } else {
299d5a43964SAlexander Graf         /* yet another region */
300d5a43964SAlexander Graf         return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr,
301d5a43964SAlexander Graf                                   flags, rw);
302d5a43964SAlexander Graf     }
303d5a43964SAlexander Graf }
304d5a43964SAlexander Graf 
30571e47088SBlue Swirl static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
30671e47088SBlue Swirl                              uint64_t asc, target_ulong *raddr, int *flags,
30771e47088SBlue Swirl                              int rw)
308d5a43964SAlexander Graf {
309d5a43964SAlexander Graf     uint64_t asce = 0;
310d5a43964SAlexander Graf     int level, new_level;
311d5a43964SAlexander Graf     int r;
312d5a43964SAlexander Graf 
313d5a43964SAlexander Graf     switch (asc) {
314d5a43964SAlexander Graf     case PSW_ASC_PRIMARY:
31571e47088SBlue Swirl         PTE_DPRINTF("%s: asc=primary\n", __func__);
316d5a43964SAlexander Graf         asce = env->cregs[1];
317d5a43964SAlexander Graf         break;
318d5a43964SAlexander Graf     case PSW_ASC_SECONDARY:
31971e47088SBlue Swirl         PTE_DPRINTF("%s: asc=secondary\n", __func__);
320d5a43964SAlexander Graf         asce = env->cregs[7];
321d5a43964SAlexander Graf         break;
322d5a43964SAlexander Graf     case PSW_ASC_HOME:
32371e47088SBlue Swirl         PTE_DPRINTF("%s: asc=home\n", __func__);
324d5a43964SAlexander Graf         asce = env->cregs[13];
325d5a43964SAlexander Graf         break;
326d5a43964SAlexander Graf     }
327d5a43964SAlexander Graf 
328d5a43964SAlexander Graf     switch (asce & _ASCE_TYPE_MASK) {
329d5a43964SAlexander Graf     case _ASCE_TYPE_REGION1:
330d5a43964SAlexander Graf         break;
331d5a43964SAlexander Graf     case _ASCE_TYPE_REGION2:
332d5a43964SAlexander Graf         if (vaddr & 0xffe0000000000000ULL) {
333d5a43964SAlexander Graf             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
33471e47088SBlue Swirl                     " 0xffe0000000000000ULL\n", __func__, vaddr);
335d5a43964SAlexander Graf             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
336d5a43964SAlexander Graf             return -1;
337d5a43964SAlexander Graf         }
338d5a43964SAlexander Graf         break;
339d5a43964SAlexander Graf     case _ASCE_TYPE_REGION3:
340d5a43964SAlexander Graf         if (vaddr & 0xfffffc0000000000ULL) {
341d5a43964SAlexander Graf             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
34271e47088SBlue Swirl                     " 0xfffffc0000000000ULL\n", __func__, vaddr);
343d5a43964SAlexander Graf             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
344d5a43964SAlexander Graf             return -1;
345d5a43964SAlexander Graf         }
346d5a43964SAlexander Graf         break;
347d5a43964SAlexander Graf     case _ASCE_TYPE_SEGMENT:
348d5a43964SAlexander Graf         if (vaddr & 0xffffffff80000000ULL) {
349d5a43964SAlexander Graf             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
35071e47088SBlue Swirl                     " 0xffffffff80000000ULL\n", __func__, vaddr);
351d5a43964SAlexander Graf             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
352d5a43964SAlexander Graf             return -1;
353d5a43964SAlexander Graf         }
354d5a43964SAlexander Graf         break;
355d5a43964SAlexander Graf     }
356d5a43964SAlexander Graf 
357d5a43964SAlexander Graf     /* fake level above current */
358d5a43964SAlexander Graf     level = asce & _ASCE_TYPE_MASK;
359d5a43964SAlexander Graf     new_level = level + 4;
360d5a43964SAlexander Graf     asce = (asce & ~_ASCE_TYPE_MASK) | (new_level & _ASCE_TYPE_MASK);
361d5a43964SAlexander Graf 
362d5a43964SAlexander Graf     r = mmu_translate_asce(env, vaddr, asc, asce, new_level, raddr, flags, rw);
363d5a43964SAlexander Graf 
364d5a43964SAlexander Graf     if ((rw == 1) && !(*flags & PAGE_WRITE)) {
365d5a43964SAlexander Graf         trigger_prot_fault(env, vaddr, asc);
366d5a43964SAlexander Graf         return -1;
367d5a43964SAlexander Graf     }
368d5a43964SAlexander Graf 
369d5a43964SAlexander Graf     return r;
370d5a43964SAlexander Graf }
371d5a43964SAlexander Graf 
372a4e3ad19SAndreas Färber int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
373d5a43964SAlexander Graf                   target_ulong *raddr, int *flags)
374d5a43964SAlexander Graf {
375d5a43964SAlexander Graf     int r = -1;
376b9959138SAlexander Graf     uint8_t *sk;
377d5a43964SAlexander Graf 
378d5a43964SAlexander Graf     *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
379d5a43964SAlexander Graf     vaddr &= TARGET_PAGE_MASK;
380d5a43964SAlexander Graf 
381d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_DAT)) {
382d5a43964SAlexander Graf         *raddr = vaddr;
383d5a43964SAlexander Graf         r = 0;
384d5a43964SAlexander Graf         goto out;
385d5a43964SAlexander Graf     }
386d5a43964SAlexander Graf 
387d5a43964SAlexander Graf     switch (asc) {
388d5a43964SAlexander Graf     case PSW_ASC_PRIMARY:
389d5a43964SAlexander Graf     case PSW_ASC_HOME:
390d5a43964SAlexander Graf         r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw);
391d5a43964SAlexander Graf         break;
392d5a43964SAlexander Graf     case PSW_ASC_SECONDARY:
393d5a43964SAlexander Graf         /*
394d5a43964SAlexander Graf          * Instruction: Primary
395d5a43964SAlexander Graf          * Data: Secondary
396d5a43964SAlexander Graf          */
397d5a43964SAlexander Graf         if (rw == 2) {
398d5a43964SAlexander Graf             r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags,
399d5a43964SAlexander Graf                                   rw);
400d5a43964SAlexander Graf             *flags &= ~(PAGE_READ | PAGE_WRITE);
401d5a43964SAlexander Graf         } else {
402d5a43964SAlexander Graf             r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags,
403d5a43964SAlexander Graf                                   rw);
404d5a43964SAlexander Graf             *flags &= ~(PAGE_EXEC);
405d5a43964SAlexander Graf         }
406d5a43964SAlexander Graf         break;
407d5a43964SAlexander Graf     case PSW_ASC_ACCREG:
408d5a43964SAlexander Graf     default:
409d5a43964SAlexander Graf         hw_error("guest switched to unknown asc mode\n");
410d5a43964SAlexander Graf         break;
411d5a43964SAlexander Graf     }
412d5a43964SAlexander Graf 
413d5a43964SAlexander Graf  out:
414d5a43964SAlexander Graf     /* Convert real address -> absolute address */
415422f32c5SThomas Huth     *raddr = mmu_real2abs(env, *raddr);
416d5a43964SAlexander Graf 
417b9959138SAlexander Graf     if (*raddr <= ram_size) {
418b9959138SAlexander Graf         sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE];
419b9959138SAlexander Graf         if (*flags & PAGE_READ) {
420b9959138SAlexander Graf             *sk |= SK_R;
421b9959138SAlexander Graf         }
422b9959138SAlexander Graf 
423b9959138SAlexander Graf         if (*flags & PAGE_WRITE) {
424b9959138SAlexander Graf             *sk |= SK_C;
425b9959138SAlexander Graf         }
426b9959138SAlexander Graf     }
427b9959138SAlexander Graf 
428d5a43964SAlexander Graf     return r;
429d5a43964SAlexander Graf }
430d5a43964SAlexander Graf 
4317510454eSAndreas Färber int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
43271e47088SBlue Swirl                               int rw, int mmu_idx)
433d5a43964SAlexander Graf {
4347510454eSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
4357510454eSAndreas Färber     CPUS390XState *env = &cpu->env;
436d5a43964SAlexander Graf     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
437d5a43964SAlexander Graf     target_ulong vaddr, raddr;
438d5a43964SAlexander Graf     int prot;
439d5a43964SAlexander Graf 
4407510454eSAndreas Färber     DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
44107cc7d12SAndreas Färber             __func__, orig_vaddr, rw, mmu_idx);
442d5a43964SAlexander Graf 
44371e47088SBlue Swirl     orig_vaddr &= TARGET_PAGE_MASK;
44471e47088SBlue Swirl     vaddr = orig_vaddr;
445d5a43964SAlexander Graf 
446d5a43964SAlexander Graf     /* 31-Bit mode */
447d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_64)) {
448d5a43964SAlexander Graf         vaddr &= 0x7fffffff;
449d5a43964SAlexander Graf     }
450d5a43964SAlexander Graf 
451d5a43964SAlexander Graf     if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) {
452d5a43964SAlexander Graf         /* Translation ended in exception */
453d5a43964SAlexander Graf         return 1;
454d5a43964SAlexander Graf     }
455d5a43964SAlexander Graf 
456d5a43964SAlexander Graf     /* check out of RAM access */
457d5a43964SAlexander Graf     if (raddr > (ram_size + virtio_size)) {
458a6f921b0SAndreas Färber         DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
459a6f921b0SAndreas Färber                 (uint64_t)raddr, (uint64_t)ram_size);
460d5a103cdSRichard Henderson         trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
461d5a43964SAlexander Graf         return 1;
462d5a43964SAlexander Graf     }
463d5a43964SAlexander Graf 
46471e47088SBlue Swirl     DPRINTF("%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __func__,
465d5a43964SAlexander Graf             (uint64_t)vaddr, (uint64_t)raddr, prot);
466d5a43964SAlexander Graf 
4670c591eb0SAndreas Färber     tlb_set_page(cs, orig_vaddr, raddr, prot,
468d5a43964SAlexander Graf                  mmu_idx, TARGET_PAGE_SIZE);
469d5a43964SAlexander Graf 
470d5a43964SAlexander Graf     return 0;
471d5a43964SAlexander Graf }
472d5a43964SAlexander Graf 
47300b941e5SAndreas Färber hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
474d5a43964SAlexander Graf {
47500b941e5SAndreas Färber     S390CPU *cpu = S390_CPU(cs);
47600b941e5SAndreas Färber     CPUS390XState *env = &cpu->env;
477d5a43964SAlexander Graf     target_ulong raddr;
478d5a43964SAlexander Graf     int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
47927103424SAndreas Färber     int old_exc = cs->exception_index;
480d5a43964SAlexander Graf     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
481d5a43964SAlexander Graf 
482d5a43964SAlexander Graf     /* 31-Bit mode */
483d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_64)) {
484d5a43964SAlexander Graf         vaddr &= 0x7fffffff;
485d5a43964SAlexander Graf     }
486d5a43964SAlexander Graf 
487d5a43964SAlexander Graf     mmu_translate(env, vaddr, 2, asc, &raddr, &prot);
48827103424SAndreas Färber     cs->exception_index = old_exc;
489d5a43964SAlexander Graf 
490d5a43964SAlexander Graf     return raddr;
491d5a43964SAlexander Graf }
492d5a43964SAlexander Graf 
493770a6379SDavid Hildenbrand hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
494770a6379SDavid Hildenbrand {
495770a6379SDavid Hildenbrand     hwaddr phys_addr;
496770a6379SDavid Hildenbrand     target_ulong page;
497770a6379SDavid Hildenbrand 
498770a6379SDavid Hildenbrand     page = vaddr & TARGET_PAGE_MASK;
499770a6379SDavid Hildenbrand     phys_addr = cpu_get_phys_page_debug(cs, page);
500770a6379SDavid Hildenbrand     phys_addr += (vaddr & ~TARGET_PAGE_MASK);
501770a6379SDavid Hildenbrand 
502770a6379SDavid Hildenbrand     return phys_addr;
503770a6379SDavid Hildenbrand }
504770a6379SDavid Hildenbrand 
505a4e3ad19SAndreas Färber void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
506d5a43964SAlexander Graf {
507d5a43964SAlexander Graf     if (mask & PSW_MASK_WAIT) {
50849e15878SAndreas Färber         S390CPU *cpu = s390_env_get_cpu(env);
509259186a7SAndreas Färber         CPUState *cs = CPU(cpu);
510ef81522bSAlexander Graf         if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) {
51149e15878SAndreas Färber             if (s390_del_running_cpu(cpu) == 0) {
512ef81522bSAlexander Graf #ifndef CONFIG_USER_ONLY
513ef81522bSAlexander Graf                 qemu_system_shutdown_request();
514ef81522bSAlexander Graf #endif
515ef81522bSAlexander Graf             }
516ef81522bSAlexander Graf         }
517259186a7SAndreas Färber         cs->halted = 1;
51827103424SAndreas Färber         cs->exception_index = EXCP_HLT;
519d5a43964SAlexander Graf     }
520d5a43964SAlexander Graf 
521d5a43964SAlexander Graf     env->psw.addr = addr;
522d5a43964SAlexander Graf     env->psw.mask = mask;
52351855ecfSRichard Henderson     env->cc_op = (mask >> 44) & 3;
524d5a43964SAlexander Graf }
525d5a43964SAlexander Graf 
526a4e3ad19SAndreas Färber static uint64_t get_psw_mask(CPUS390XState *env)
527d5a43964SAlexander Graf {
52851855ecfSRichard Henderson     uint64_t r;
529d5a43964SAlexander Graf 
530d5a43964SAlexander Graf     env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);
531d5a43964SAlexander Graf 
53251855ecfSRichard Henderson     r = env->psw.mask;
53351855ecfSRichard Henderson     r &= ~PSW_MASK_CC;
534d5a43964SAlexander Graf     assert(!(env->cc_op & ~3));
53551855ecfSRichard Henderson     r |= (uint64_t)env->cc_op << 44;
536d5a43964SAlexander Graf 
537d5a43964SAlexander Graf     return r;
538d5a43964SAlexander Graf }
539d5a43964SAlexander Graf 
5404782a23bSCornelia Huck static LowCore *cpu_map_lowcore(CPUS390XState *env)
5414782a23bSCornelia Huck {
542a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
5434782a23bSCornelia Huck     LowCore *lowcore;
5444782a23bSCornelia Huck     hwaddr len = sizeof(LowCore);
5454782a23bSCornelia Huck 
5464782a23bSCornelia Huck     lowcore = cpu_physical_memory_map(env->psa, &len, 1);
5474782a23bSCornelia Huck 
5484782a23bSCornelia Huck     if (len < sizeof(LowCore)) {
549a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Could not map lowcore\n");
5504782a23bSCornelia Huck     }
5514782a23bSCornelia Huck 
5524782a23bSCornelia Huck     return lowcore;
5534782a23bSCornelia Huck }
5544782a23bSCornelia Huck 
5554782a23bSCornelia Huck static void cpu_unmap_lowcore(LowCore *lowcore)
5564782a23bSCornelia Huck {
5574782a23bSCornelia Huck     cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore));
5584782a23bSCornelia Huck }
5594782a23bSCornelia Huck 
56038322ed6SCornelia Huck void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
56138322ed6SCornelia Huck                                    int is_write)
56238322ed6SCornelia Huck {
56338322ed6SCornelia Huck     hwaddr start = addr;
56438322ed6SCornelia Huck 
56538322ed6SCornelia Huck     /* Mind the prefix area. */
56638322ed6SCornelia Huck     if (addr < 8192) {
56738322ed6SCornelia Huck         /* Map the lowcore. */
56838322ed6SCornelia Huck         start += env->psa;
56938322ed6SCornelia Huck         *len = MIN(*len, 8192 - addr);
57038322ed6SCornelia Huck     } else if ((addr >= env->psa) && (addr < env->psa + 8192)) {
57138322ed6SCornelia Huck         /* Map the 0 page. */
57238322ed6SCornelia Huck         start -= env->psa;
57338322ed6SCornelia Huck         *len = MIN(*len, 8192 - start);
57438322ed6SCornelia Huck     }
57538322ed6SCornelia Huck 
57638322ed6SCornelia Huck     return cpu_physical_memory_map(start, len, is_write);
57738322ed6SCornelia Huck }
57838322ed6SCornelia Huck 
57938322ed6SCornelia Huck void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
58038322ed6SCornelia Huck                                     int is_write)
58138322ed6SCornelia Huck {
58238322ed6SCornelia Huck     cpu_physical_memory_unmap(addr, len, is_write, len);
58338322ed6SCornelia Huck }
58438322ed6SCornelia Huck 
585a4e3ad19SAndreas Färber static void do_svc_interrupt(CPUS390XState *env)
586d5a43964SAlexander Graf {
587d5a43964SAlexander Graf     uint64_t mask, addr;
588d5a43964SAlexander Graf     LowCore *lowcore;
589d5a43964SAlexander Graf 
5904782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
591d5a43964SAlexander Graf 
592d5a43964SAlexander Graf     lowcore->svc_code = cpu_to_be16(env->int_svc_code);
593d5a103cdSRichard Henderson     lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
594d5a43964SAlexander Graf     lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
595d5a103cdSRichard Henderson     lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
596d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->svc_new_psw.mask);
597d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->svc_new_psw.addr);
598d5a43964SAlexander Graf 
5994782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
600d5a43964SAlexander Graf 
601d5a43964SAlexander Graf     load_psw(env, mask, addr);
602d5a43964SAlexander Graf }
603d5a43964SAlexander Graf 
604a4e3ad19SAndreas Färber static void do_program_interrupt(CPUS390XState *env)
605d5a43964SAlexander Graf {
606d5a43964SAlexander Graf     uint64_t mask, addr;
607d5a43964SAlexander Graf     LowCore *lowcore;
608d5a103cdSRichard Henderson     int ilen = env->int_pgm_ilen;
609d5a43964SAlexander Graf 
610d5a103cdSRichard Henderson     switch (ilen) {
611d5a103cdSRichard Henderson     case ILEN_LATER:
612d5a103cdSRichard Henderson         ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
613d5a43964SAlexander Graf         break;
614d5a103cdSRichard Henderson     case ILEN_LATER_INC:
615d5a103cdSRichard Henderson         ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
616d5a103cdSRichard Henderson         env->psw.addr += ilen;
617d5a43964SAlexander Graf         break;
618d5a103cdSRichard Henderson     default:
619d5a103cdSRichard Henderson         assert(ilen == 2 || ilen == 4 || ilen == 6);
620d5a43964SAlexander Graf     }
621d5a43964SAlexander Graf 
622d5a103cdSRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
623d5a103cdSRichard Henderson                   __func__, env->int_pgm_code, ilen);
624d5a43964SAlexander Graf 
6254782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
626d5a43964SAlexander Graf 
627d5a103cdSRichard Henderson     lowcore->pgm_ilen = cpu_to_be16(ilen);
628d5a43964SAlexander Graf     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
629d5a43964SAlexander Graf     lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
630d5a43964SAlexander Graf     lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
631d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->program_new_psw.mask);
632d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->program_new_psw.addr);
633d5a43964SAlexander Graf 
6344782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
635d5a43964SAlexander Graf 
63671e47088SBlue Swirl     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
637d5a103cdSRichard Henderson             env->int_pgm_code, ilen, env->psw.mask,
638d5a43964SAlexander Graf             env->psw.addr);
639d5a43964SAlexander Graf 
640d5a43964SAlexander Graf     load_psw(env, mask, addr);
641d5a43964SAlexander Graf }
642d5a43964SAlexander Graf 
643d5a43964SAlexander Graf #define VIRTIO_SUBCODE_64 0x0D00
644d5a43964SAlexander Graf 
645a4e3ad19SAndreas Färber static void do_ext_interrupt(CPUS390XState *env)
646d5a43964SAlexander Graf {
647a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
648d5a43964SAlexander Graf     uint64_t mask, addr;
649d5a43964SAlexander Graf     LowCore *lowcore;
650d5a43964SAlexander Graf     ExtQueue *q;
651d5a43964SAlexander Graf 
652d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_EXT)) {
653a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
654d5a43964SAlexander Graf     }
655d5a43964SAlexander Graf 
656d5a43964SAlexander Graf     if (env->ext_index < 0 || env->ext_index > MAX_EXT_QUEUE) {
657a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
658d5a43964SAlexander Graf     }
659d5a43964SAlexander Graf 
660d5a43964SAlexander Graf     q = &env->ext_queue[env->ext_index];
6614782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
662d5a43964SAlexander Graf 
663d5a43964SAlexander Graf     lowcore->ext_int_code = cpu_to_be16(q->code);
664d5a43964SAlexander Graf     lowcore->ext_params = cpu_to_be32(q->param);
665d5a43964SAlexander Graf     lowcore->ext_params2 = cpu_to_be64(q->param64);
666d5a43964SAlexander Graf     lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
667d5a43964SAlexander Graf     lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
668d5a43964SAlexander Graf     lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
669d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->external_new_psw.mask);
670d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->external_new_psw.addr);
671d5a43964SAlexander Graf 
6724782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
673d5a43964SAlexander Graf 
674d5a43964SAlexander Graf     env->ext_index--;
675d5a43964SAlexander Graf     if (env->ext_index == -1) {
676d5a43964SAlexander Graf         env->pending_int &= ~INTERRUPT_EXT;
677d5a43964SAlexander Graf     }
678d5a43964SAlexander Graf 
67971e47088SBlue Swirl     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
680d5a43964SAlexander Graf             env->psw.mask, env->psw.addr);
681d5a43964SAlexander Graf 
682d5a43964SAlexander Graf     load_psw(env, mask, addr);
683d5a43964SAlexander Graf }
6843110e292SAlexander Graf 
6855d69c547SCornelia Huck static void do_io_interrupt(CPUS390XState *env)
6865d69c547SCornelia Huck {
687a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
6885d69c547SCornelia Huck     LowCore *lowcore;
6895d69c547SCornelia Huck     IOIntQueue *q;
6905d69c547SCornelia Huck     uint8_t isc;
6915d69c547SCornelia Huck     int disable = 1;
6925d69c547SCornelia Huck     int found = 0;
6935d69c547SCornelia Huck 
6945d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_IO)) {
695a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n");
6965d69c547SCornelia Huck     }
6975d69c547SCornelia Huck 
6985d69c547SCornelia Huck     for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
69991b0a8f3SCornelia Huck         uint64_t isc_bits;
70091b0a8f3SCornelia Huck 
7015d69c547SCornelia Huck         if (env->io_index[isc] < 0) {
7025d69c547SCornelia Huck             continue;
7035d69c547SCornelia Huck         }
7045d69c547SCornelia Huck         if (env->io_index[isc] > MAX_IO_QUEUE) {
705a47dddd7SAndreas Färber             cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n",
7065d69c547SCornelia Huck                       isc, env->io_index[isc]);
7075d69c547SCornelia Huck         }
7085d69c547SCornelia Huck 
7095d69c547SCornelia Huck         q = &env->io_queue[env->io_index[isc]][isc];
71091b0a8f3SCornelia Huck         isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
71191b0a8f3SCornelia Huck         if (!(env->cregs[6] & isc_bits)) {
7125d69c547SCornelia Huck             disable = 0;
7135d69c547SCornelia Huck             continue;
7145d69c547SCornelia Huck         }
715bd9a8d85SCornelia Huck         if (!found) {
716bd9a8d85SCornelia Huck             uint64_t mask, addr;
717bd9a8d85SCornelia Huck 
7185d69c547SCornelia Huck             found = 1;
7195d69c547SCornelia Huck             lowcore = cpu_map_lowcore(env);
7205d69c547SCornelia Huck 
7215d69c547SCornelia Huck             lowcore->subchannel_id = cpu_to_be16(q->id);
7225d69c547SCornelia Huck             lowcore->subchannel_nr = cpu_to_be16(q->nr);
7235d69c547SCornelia Huck             lowcore->io_int_parm = cpu_to_be32(q->parm);
7245d69c547SCornelia Huck             lowcore->io_int_word = cpu_to_be32(q->word);
7255d69c547SCornelia Huck             lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
7265d69c547SCornelia Huck             lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
7275d69c547SCornelia Huck             mask = be64_to_cpu(lowcore->io_new_psw.mask);
7285d69c547SCornelia Huck             addr = be64_to_cpu(lowcore->io_new_psw.addr);
7295d69c547SCornelia Huck 
7305d69c547SCornelia Huck             cpu_unmap_lowcore(lowcore);
7315d69c547SCornelia Huck 
7325d69c547SCornelia Huck             env->io_index[isc]--;
733bd9a8d85SCornelia Huck 
734bd9a8d85SCornelia Huck             DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
735bd9a8d85SCornelia Huck                     env->psw.mask, env->psw.addr);
736bd9a8d85SCornelia Huck             load_psw(env, mask, addr);
737bd9a8d85SCornelia Huck         }
738b22dd124SStefan Weil         if (env->io_index[isc] >= 0) {
7395d69c547SCornelia Huck             disable = 0;
7405d69c547SCornelia Huck         }
741bd9a8d85SCornelia Huck         continue;
7425d69c547SCornelia Huck     }
7435d69c547SCornelia Huck 
7445d69c547SCornelia Huck     if (disable) {
7455d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_IO;
7465d69c547SCornelia Huck     }
7475d69c547SCornelia Huck 
7485d69c547SCornelia Huck }
7495d69c547SCornelia Huck 
7505d69c547SCornelia Huck static void do_mchk_interrupt(CPUS390XState *env)
7515d69c547SCornelia Huck {
752a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
7535d69c547SCornelia Huck     uint64_t mask, addr;
7545d69c547SCornelia Huck     LowCore *lowcore;
7555d69c547SCornelia Huck     MchkQueue *q;
7565d69c547SCornelia Huck     int i;
7575d69c547SCornelia Huck 
7585d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_MCHECK)) {
759a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n");
7605d69c547SCornelia Huck     }
7615d69c547SCornelia Huck 
7625d69c547SCornelia Huck     if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) {
763a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
7645d69c547SCornelia Huck     }
7655d69c547SCornelia Huck 
7665d69c547SCornelia Huck     q = &env->mchk_queue[env->mchk_index];
7675d69c547SCornelia Huck 
7685d69c547SCornelia Huck     if (q->type != 1) {
7695d69c547SCornelia Huck         /* Don't know how to handle this... */
770a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
7715d69c547SCornelia Huck     }
7725d69c547SCornelia Huck     if (!(env->cregs[14] & (1 << 28))) {
7735d69c547SCornelia Huck         /* CRW machine checks disabled */
7745d69c547SCornelia Huck         return;
7755d69c547SCornelia Huck     }
7765d69c547SCornelia Huck 
7775d69c547SCornelia Huck     lowcore = cpu_map_lowcore(env);
7785d69c547SCornelia Huck 
7795d69c547SCornelia Huck     for (i = 0; i < 16; i++) {
7805d69c547SCornelia Huck         lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll);
7815d69c547SCornelia Huck         lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
7825d69c547SCornelia Huck         lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
7835d69c547SCornelia Huck         lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
7845d69c547SCornelia Huck     }
7855d69c547SCornelia Huck     lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
7865d69c547SCornelia Huck     lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
7875d69c547SCornelia Huck     lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
7885d69c547SCornelia Huck     lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
7895d69c547SCornelia Huck     lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
7905d69c547SCornelia Huck     lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
7915d69c547SCornelia Huck     lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
7925d69c547SCornelia Huck 
7935d69c547SCornelia Huck     lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
7945d69c547SCornelia Huck     lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
7955d69c547SCornelia Huck     lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
7965d69c547SCornelia Huck     lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
7975d69c547SCornelia Huck     mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
7985d69c547SCornelia Huck     addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
7995d69c547SCornelia Huck 
8005d69c547SCornelia Huck     cpu_unmap_lowcore(lowcore);
8015d69c547SCornelia Huck 
8025d69c547SCornelia Huck     env->mchk_index--;
8035d69c547SCornelia Huck     if (env->mchk_index == -1) {
8045d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_MCHK;
8055d69c547SCornelia Huck     }
8065d69c547SCornelia Huck 
8075d69c547SCornelia Huck     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
8085d69c547SCornelia Huck             env->psw.mask, env->psw.addr);
8095d69c547SCornelia Huck 
8105d69c547SCornelia Huck     load_psw(env, mask, addr);
8115d69c547SCornelia Huck }
8125d69c547SCornelia Huck 
81397a8ea5aSAndreas Färber void s390_cpu_do_interrupt(CPUState *cs)
8143110e292SAlexander Graf {
81597a8ea5aSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
81697a8ea5aSAndreas Färber     CPUS390XState *env = &cpu->env;
817f9466733SAndreas Färber 
8180d404541SRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
81927103424SAndreas Färber                   __func__, cs->exception_index, env->psw.addr);
820d5a43964SAlexander Graf 
82149e15878SAndreas Färber     s390_add_running_cpu(cpu);
8225d69c547SCornelia Huck     /* handle machine checks */
8235d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_MCHECK) &&
82427103424SAndreas Färber         (cs->exception_index == -1)) {
8255d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_MCHK) {
82627103424SAndreas Färber             cs->exception_index = EXCP_MCHK;
8275d69c547SCornelia Huck         }
8285d69c547SCornelia Huck     }
829d5a43964SAlexander Graf     /* handle external interrupts */
830d5a43964SAlexander Graf     if ((env->psw.mask & PSW_MASK_EXT) &&
83127103424SAndreas Färber         cs->exception_index == -1) {
832d5a43964SAlexander Graf         if (env->pending_int & INTERRUPT_EXT) {
833d5a43964SAlexander Graf             /* code is already in env */
83427103424SAndreas Färber             cs->exception_index = EXCP_EXT;
835d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_TOD) {
836f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1004, 0, 0);
83727103424SAndreas Färber             cs->exception_index = EXCP_EXT;
838d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
839d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
840d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_CPUTIMER) {
841f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1005, 0, 0);
84227103424SAndreas Färber             cs->exception_index = EXCP_EXT;
843d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
844d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
8453110e292SAlexander Graf         }
846d5a43964SAlexander Graf     }
8475d69c547SCornelia Huck     /* handle I/O interrupts */
8485d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_IO) &&
84927103424SAndreas Färber         (cs->exception_index == -1)) {
8505d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_IO) {
85127103424SAndreas Färber             cs->exception_index = EXCP_IO;
8525d69c547SCornelia Huck         }
8535d69c547SCornelia Huck     }
854d5a43964SAlexander Graf 
85527103424SAndreas Färber     switch (cs->exception_index) {
856d5a43964SAlexander Graf     case EXCP_PGM:
857d5a43964SAlexander Graf         do_program_interrupt(env);
858d5a43964SAlexander Graf         break;
859d5a43964SAlexander Graf     case EXCP_SVC:
860d5a43964SAlexander Graf         do_svc_interrupt(env);
861d5a43964SAlexander Graf         break;
862d5a43964SAlexander Graf     case EXCP_EXT:
863d5a43964SAlexander Graf         do_ext_interrupt(env);
864d5a43964SAlexander Graf         break;
8655d69c547SCornelia Huck     case EXCP_IO:
8665d69c547SCornelia Huck         do_io_interrupt(env);
8675d69c547SCornelia Huck         break;
8685d69c547SCornelia Huck     case EXCP_MCHK:
8695d69c547SCornelia Huck         do_mchk_interrupt(env);
8705d69c547SCornelia Huck         break;
871d5a43964SAlexander Graf     }
87227103424SAndreas Färber     cs->exception_index = -1;
873d5a43964SAlexander Graf 
874d5a43964SAlexander Graf     if (!env->pending_int) {
875259186a7SAndreas Färber         cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
876d5a43964SAlexander Graf     }
877d5a43964SAlexander Graf }
878d5a43964SAlexander Graf 
879d5a43964SAlexander Graf #endif /* CONFIG_USER_ONLY */
880