xref: /qemu/target/s390x/helper.c (revision b5bd2e91a6e5c7b87f1ea592c8604af59e87bf37)
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 
219615495aSPeter Maydell #include "qemu/osdep.h"
22da34e65cSMarkus Armbruster #include "qapi/error.h"
2310ec5117SAlexander Graf #include "cpu.h"
24022c62cbSPaolo Bonzini #include "exec/gdbstub.h"
251de7afc9SPaolo Bonzini #include "qemu/timer.h"
2663c91552SPaolo Bonzini #include "exec/exec-all.h"
27f08b6170SPaolo Bonzini #include "exec/cpu_ldst.h"
28bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h"
29ef81522bSAlexander Graf #ifndef CONFIG_USER_ONLY
309c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
31ef81522bSAlexander Graf #endif
3210ec5117SAlexander Graf 
33d5a43964SAlexander Graf //#define DEBUG_S390
34d5a43964SAlexander Graf //#define DEBUG_S390_STDOUT
35d5a43964SAlexander Graf 
36d5a43964SAlexander Graf #ifdef DEBUG_S390
37d5a43964SAlexander Graf #ifdef DEBUG_S390_STDOUT
38d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
39d5a43964SAlexander Graf     do { fprintf(stderr, fmt, ## __VA_ARGS__); \
40013a2942SPaolo Bonzini          if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0)
41d5a43964SAlexander Graf #else
42d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
43d5a43964SAlexander Graf     do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
44d5a43964SAlexander Graf #endif
45d5a43964SAlexander Graf #else
46d5a43964SAlexander Graf #define DPRINTF(fmt, ...) \
47d5a43964SAlexander Graf     do { } while (0)
48d5a43964SAlexander Graf #endif
49d5a43964SAlexander Graf 
50d5a43964SAlexander Graf 
51d5a43964SAlexander Graf #ifndef CONFIG_USER_ONLY
528f22e0dfSAndreas Färber void s390x_tod_timer(void *opaque)
53d5a43964SAlexander Graf {
54b8ba6799SAndreas Färber     S390CPU *cpu = opaque;
55b8ba6799SAndreas Färber     CPUS390XState *env = &cpu->env;
56d5a43964SAlexander Graf 
57d5a43964SAlexander Graf     env->pending_int |= INTERRUPT_TOD;
58c3affe56SAndreas Färber     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
59d5a43964SAlexander Graf }
60d5a43964SAlexander Graf 
618f22e0dfSAndreas Färber void s390x_cpu_timer(void *opaque)
62d5a43964SAlexander Graf {
63b8ba6799SAndreas Färber     S390CPU *cpu = opaque;
64b8ba6799SAndreas Färber     CPUS390XState *env = &cpu->env;
65d5a43964SAlexander Graf 
66d5a43964SAlexander Graf     env->pending_int |= INTERRUPT_CPUTIMER;
67c3affe56SAndreas Färber     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
68d5a43964SAlexander Graf }
69d5a43964SAlexander Graf #endif
7010c339a0SAlexander Graf 
7196b1a8bbSMatthew Rosato S390CPU *cpu_s390x_create(const char *cpu_model, Error **errp)
7210ec5117SAlexander Graf {
7341868f84SDavid Hildenbrand     static bool features_parsed;
7441868f84SDavid Hildenbrand     char *name, *features;
7541868f84SDavid Hildenbrand     const char *typename;
7641868f84SDavid Hildenbrand     ObjectClass *oc;
7741868f84SDavid Hildenbrand     CPUClass *cc;
7841868f84SDavid Hildenbrand 
7941868f84SDavid Hildenbrand     name = g_strdup(cpu_model);
8041868f84SDavid Hildenbrand     features = strchr(name, ',');
8141868f84SDavid Hildenbrand     if (features) {
8241868f84SDavid Hildenbrand         features[0] = 0;
8341868f84SDavid Hildenbrand         features++;
8441868f84SDavid Hildenbrand     }
8541868f84SDavid Hildenbrand 
8641868f84SDavid Hildenbrand     oc = cpu_class_by_name(TYPE_S390_CPU, name);
8741868f84SDavid Hildenbrand     if (!oc) {
8841868f84SDavid Hildenbrand         error_setg(errp, "Unknown CPU definition \'%s\'", name);
8941868f84SDavid Hildenbrand         g_free(name);
9041868f84SDavid Hildenbrand         return NULL;
9141868f84SDavid Hildenbrand     }
9241868f84SDavid Hildenbrand     typename = object_class_get_name(oc);
9341868f84SDavid Hildenbrand 
9441868f84SDavid Hildenbrand     if (!features_parsed) {
9541868f84SDavid Hildenbrand         features_parsed = true;
9641868f84SDavid Hildenbrand         cc = CPU_CLASS(oc);
9741868f84SDavid Hildenbrand         cc->parse_features(typename, features, errp);
9841868f84SDavid Hildenbrand     }
9941868f84SDavid Hildenbrand     g_free(name);
10041868f84SDavid Hildenbrand 
10141868f84SDavid Hildenbrand     if (*errp) {
10241868f84SDavid Hildenbrand         return NULL;
10341868f84SDavid Hildenbrand     }
10441868f84SDavid Hildenbrand     return S390_CPU(CPU(object_new(typename)));
10596b1a8bbSMatthew Rosato }
1061f136632SAndreas Färber 
10796b1a8bbSMatthew Rosato S390CPU *s390x_new_cpu(const char *cpu_model, int64_t id, Error **errp)
10896b1a8bbSMatthew Rosato {
10996b1a8bbSMatthew Rosato     S390CPU *cpu;
11096b1a8bbSMatthew Rosato     Error *err = NULL;
11196b1a8bbSMatthew Rosato 
11296b1a8bbSMatthew Rosato     cpu = cpu_s390x_create(cpu_model, &err);
11396b1a8bbSMatthew Rosato     if (err != NULL) {
11496b1a8bbSMatthew Rosato         goto out;
11596b1a8bbSMatthew Rosato     }
11696b1a8bbSMatthew Rosato 
11796b1a8bbSMatthew Rosato     object_property_set_int(OBJECT(cpu), id, "id", &err);
11896b1a8bbSMatthew Rosato     if (err != NULL) {
11996b1a8bbSMatthew Rosato         goto out;
12096b1a8bbSMatthew Rosato     }
12196b1a8bbSMatthew Rosato     object_property_set_bool(OBJECT(cpu), true, "realized", &err);
12296b1a8bbSMatthew Rosato 
12396b1a8bbSMatthew Rosato out:
12496b1a8bbSMatthew Rosato     if (err) {
12596b1a8bbSMatthew Rosato         error_propagate(errp, err);
12696b1a8bbSMatthew Rosato         object_unref(OBJECT(cpu));
12796b1a8bbSMatthew Rosato         cpu = NULL;
12896b1a8bbSMatthew Rosato     }
12996b1a8bbSMatthew Rosato     return cpu;
13096b1a8bbSMatthew Rosato }
13196b1a8bbSMatthew Rosato 
13296b1a8bbSMatthew Rosato S390CPU *cpu_s390x_init(const char *cpu_model)
13396b1a8bbSMatthew Rosato {
13496b1a8bbSMatthew Rosato     Error *err = NULL;
13596b1a8bbSMatthew Rosato     S390CPU *cpu;
13696b1a8bbSMatthew Rosato     /* Use to track CPU ID for linux-user only */
13796b1a8bbSMatthew Rosato     static int64_t next_cpu_id;
13896b1a8bbSMatthew Rosato 
13996b1a8bbSMatthew Rosato     cpu = s390x_new_cpu(cpu_model, next_cpu_id++, &err);
14096b1a8bbSMatthew Rosato     if (err) {
14196b1a8bbSMatthew Rosato         error_report_err(err);
14296b1a8bbSMatthew Rosato     }
143564b863dSAndreas Färber     return cpu;
14410ec5117SAlexander Graf }
14510ec5117SAlexander Graf 
146d5a43964SAlexander Graf #if defined(CONFIG_USER_ONLY)
147d5a43964SAlexander Graf 
14897a8ea5aSAndreas Färber void s390_cpu_do_interrupt(CPUState *cs)
149d5a43964SAlexander Graf {
15027103424SAndreas Färber     cs->exception_index = -1;
151d5a43964SAlexander Graf }
152d5a43964SAlexander Graf 
1537510454eSAndreas Färber int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
15471e47088SBlue Swirl                               int rw, int mmu_idx)
155d5a43964SAlexander Graf {
1567510454eSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
1577510454eSAndreas Färber 
15827103424SAndreas Färber     cs->exception_index = EXCP_PGM;
1597510454eSAndreas Färber     cpu->env.int_pgm_code = PGM_ADDRESSING;
160d5a103cdSRichard Henderson     /* On real machines this value is dropped into LowMem.  Since this
161d5a103cdSRichard Henderson        is userland, simply put this someplace that cpu_loop can find it.  */
1627510454eSAndreas Färber     cpu->env.__excp_addr = address;
163d5a43964SAlexander Graf     return 1;
164d5a43964SAlexander Graf }
165d5a43964SAlexander Graf 
166b7e516ceSAndreas Färber #else /* !CONFIG_USER_ONLY */
16710c339a0SAlexander Graf 
168d5a43964SAlexander Graf /* Ensure to exit the TB after this call! */
169dfebd7a7SThomas Huth void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
17010c339a0SAlexander Graf {
17127103424SAndreas Färber     CPUState *cs = CPU(s390_env_get_cpu(env));
17227103424SAndreas Färber 
17327103424SAndreas Färber     cs->exception_index = EXCP_PGM;
174d5a43964SAlexander Graf     env->int_pgm_code = code;
175d5a103cdSRichard Henderson     env->int_pgm_ilen = ilen;
176d5a43964SAlexander Graf }
17710c339a0SAlexander Graf 
1787510454eSAndreas Färber int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
17971e47088SBlue Swirl                               int rw, int mmu_idx)
180d5a43964SAlexander Graf {
1817510454eSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
1827510454eSAndreas Färber     CPUS390XState *env = &cpu->env;
183c255ac60SAurelien Jarno     uint64_t asc = cpu_mmu_idx_to_asc(mmu_idx);
184d5a43964SAlexander Graf     target_ulong vaddr, raddr;
185d5a43964SAlexander Graf     int prot;
186d5a43964SAlexander Graf 
1877510454eSAndreas Färber     DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
18807cc7d12SAndreas Färber             __func__, orig_vaddr, rw, mmu_idx);
189d5a43964SAlexander Graf 
19071e47088SBlue Swirl     orig_vaddr &= TARGET_PAGE_MASK;
19171e47088SBlue Swirl     vaddr = orig_vaddr;
192d5a43964SAlexander Graf 
193d5a43964SAlexander Graf     /* 31-Bit mode */
194d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_64)) {
195d5a43964SAlexander Graf         vaddr &= 0x7fffffff;
196d5a43964SAlexander Graf     }
197d5a43964SAlexander Graf 
198e3e09d87SThomas Huth     if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) {
199d5a43964SAlexander Graf         /* Translation ended in exception */
200d5a43964SAlexander Graf         return 1;
201d5a43964SAlexander Graf     }
202d5a43964SAlexander Graf 
203d5a43964SAlexander Graf     /* check out of RAM access */
2047b3fdbd9SPierre Morel     if (raddr > ram_size) {
205a6f921b0SAndreas Färber         DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
206a6f921b0SAndreas Färber                 (uint64_t)raddr, (uint64_t)ram_size);
207becf8217SDavid Hildenbrand         trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO);
208d5a43964SAlexander Graf         return 1;
209d5a43964SAlexander Graf     }
210d5a43964SAlexander Graf 
211339aaf5bSAntony Pavlov     qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
212339aaf5bSAntony Pavlov             __func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
213d5a43964SAlexander Graf 
2140c591eb0SAndreas Färber     tlb_set_page(cs, orig_vaddr, raddr, prot,
215d5a43964SAlexander Graf                  mmu_idx, TARGET_PAGE_SIZE);
216d5a43964SAlexander Graf 
217d5a43964SAlexander Graf     return 0;
218d5a43964SAlexander Graf }
219d5a43964SAlexander Graf 
22000b941e5SAndreas Färber hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
221d5a43964SAlexander Graf {
22200b941e5SAndreas Färber     S390CPU *cpu = S390_CPU(cs);
22300b941e5SAndreas Färber     CPUS390XState *env = &cpu->env;
224d5a43964SAlexander Graf     target_ulong raddr;
225e3e09d87SThomas Huth     int prot;
226d5a43964SAlexander Graf     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
227d5a43964SAlexander Graf 
228d5a43964SAlexander Graf     /* 31-Bit mode */
229d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_64)) {
230d5a43964SAlexander Graf         vaddr &= 0x7fffffff;
231d5a43964SAlexander Graf     }
232d5a43964SAlexander Graf 
233234779a2SDavid Hildenbrand     if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) {
234234779a2SDavid Hildenbrand         return -1;
235234779a2SDavid Hildenbrand     }
236d5a43964SAlexander Graf     return raddr;
237d5a43964SAlexander Graf }
238d5a43964SAlexander Graf 
239770a6379SDavid Hildenbrand hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
240770a6379SDavid Hildenbrand {
241770a6379SDavid Hildenbrand     hwaddr phys_addr;
242770a6379SDavid Hildenbrand     target_ulong page;
243770a6379SDavid Hildenbrand 
244770a6379SDavid Hildenbrand     page = vaddr & TARGET_PAGE_MASK;
245770a6379SDavid Hildenbrand     phys_addr = cpu_get_phys_page_debug(cs, page);
246770a6379SDavid Hildenbrand     phys_addr += (vaddr & ~TARGET_PAGE_MASK);
247770a6379SDavid Hildenbrand 
248770a6379SDavid Hildenbrand     return phys_addr;
249770a6379SDavid Hildenbrand }
250770a6379SDavid Hildenbrand 
251a4e3ad19SAndreas Färber void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
252d5a43964SAlexander Graf {
253311918b9SAurelien Jarno     uint64_t old_mask = env->psw.mask;
254311918b9SAurelien Jarno 
255eb24f7c6SDavid Hildenbrand     env->psw.addr = addr;
256eb24f7c6SDavid Hildenbrand     env->psw.mask = mask;
2573f10341fSDavid Hildenbrand     if (tcg_enabled()) {
258eb24f7c6SDavid Hildenbrand         env->cc_op = (mask >> 44) & 3;
2593f10341fSDavid Hildenbrand     }
260eb24f7c6SDavid Hildenbrand 
261311918b9SAurelien Jarno     if ((old_mask ^ mask) & PSW_MASK_PER) {
262311918b9SAurelien Jarno         s390_cpu_recompute_watchpoints(CPU(s390_env_get_cpu(env)));
263311918b9SAurelien Jarno     }
264311918b9SAurelien Jarno 
265d5a43964SAlexander Graf     if (mask & PSW_MASK_WAIT) {
26649e15878SAndreas Färber         S390CPU *cpu = s390_env_get_cpu(env);
267eb24f7c6SDavid Hildenbrand         if (s390_cpu_halt(cpu) == 0) {
268ef81522bSAlexander Graf #ifndef CONFIG_USER_ONLY
269cf83f140SEric Blake             qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
270ef81522bSAlexander Graf #endif
271ef81522bSAlexander Graf         }
272ef81522bSAlexander Graf     }
273d5a43964SAlexander Graf }
274d5a43964SAlexander Graf 
275a4e3ad19SAndreas Färber static uint64_t get_psw_mask(CPUS390XState *env)
276d5a43964SAlexander Graf {
2773f10341fSDavid Hildenbrand     uint64_t r = env->psw.mask;
278d5a43964SAlexander Graf 
2793f10341fSDavid Hildenbrand     if (tcg_enabled()) {
2803f10341fSDavid Hildenbrand         env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst,
2813f10341fSDavid Hildenbrand                              env->cc_vr);
282d5a43964SAlexander Graf 
28351855ecfSRichard Henderson         r &= ~PSW_MASK_CC;
284d5a43964SAlexander Graf         assert(!(env->cc_op & ~3));
28551855ecfSRichard Henderson         r |= (uint64_t)env->cc_op << 44;
2863f10341fSDavid Hildenbrand     }
287d5a43964SAlexander Graf 
288d5a43964SAlexander Graf     return r;
289d5a43964SAlexander Graf }
290d5a43964SAlexander Graf 
2914782a23bSCornelia Huck static LowCore *cpu_map_lowcore(CPUS390XState *env)
2924782a23bSCornelia Huck {
293a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
2944782a23bSCornelia Huck     LowCore *lowcore;
2954782a23bSCornelia Huck     hwaddr len = sizeof(LowCore);
2964782a23bSCornelia Huck 
2974782a23bSCornelia Huck     lowcore = cpu_physical_memory_map(env->psa, &len, 1);
2984782a23bSCornelia Huck 
2994782a23bSCornelia Huck     if (len < sizeof(LowCore)) {
300a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Could not map lowcore\n");
3014782a23bSCornelia Huck     }
3024782a23bSCornelia Huck 
3034782a23bSCornelia Huck     return lowcore;
3044782a23bSCornelia Huck }
3054782a23bSCornelia Huck 
3064782a23bSCornelia Huck static void cpu_unmap_lowcore(LowCore *lowcore)
3074782a23bSCornelia Huck {
3084782a23bSCornelia Huck     cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore));
3094782a23bSCornelia Huck }
3104782a23bSCornelia Huck 
3113f10341fSDavid Hildenbrand void do_restart_interrupt(CPUS390XState *env)
3123f10341fSDavid Hildenbrand {
3133f10341fSDavid Hildenbrand     uint64_t mask, addr;
3143f10341fSDavid Hildenbrand     LowCore *lowcore;
3153f10341fSDavid Hildenbrand 
3163f10341fSDavid Hildenbrand     lowcore = cpu_map_lowcore(env);
3173f10341fSDavid Hildenbrand 
3183f10341fSDavid Hildenbrand     lowcore->restart_old_psw.mask = cpu_to_be64(get_psw_mask(env));
3193f10341fSDavid Hildenbrand     lowcore->restart_old_psw.addr = cpu_to_be64(env->psw.addr);
3203f10341fSDavid Hildenbrand     mask = be64_to_cpu(lowcore->restart_new_psw.mask);
3213f10341fSDavid Hildenbrand     addr = be64_to_cpu(lowcore->restart_new_psw.addr);
3223f10341fSDavid Hildenbrand 
3233f10341fSDavid Hildenbrand     cpu_unmap_lowcore(lowcore);
3243f10341fSDavid Hildenbrand 
3253f10341fSDavid Hildenbrand     load_psw(env, mask, addr);
3263f10341fSDavid Hildenbrand }
3273f10341fSDavid Hildenbrand 
328a4e3ad19SAndreas Färber static void do_program_interrupt(CPUS390XState *env)
329d5a43964SAlexander Graf {
330d5a43964SAlexander Graf     uint64_t mask, addr;
331d5a43964SAlexander Graf     LowCore *lowcore;
332d5a103cdSRichard Henderson     int ilen = env->int_pgm_ilen;
333d5a43964SAlexander Graf 
334becf8217SDavid Hildenbrand     if (ilen == ILEN_AUTO) {
335d5a103cdSRichard Henderson         ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
336becf8217SDavid Hildenbrand     }
337becf8217SDavid Hildenbrand     assert(ilen == 2 || ilen == 4 || ilen == 6);
338becf8217SDavid Hildenbrand 
339becf8217SDavid Hildenbrand     switch (env->int_pgm_code) {
340becf8217SDavid Hildenbrand     case PGM_PER:
341becf8217SDavid Hildenbrand         if (env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION) {
342d5a43964SAlexander Graf             break;
343becf8217SDavid Hildenbrand         }
344becf8217SDavid Hildenbrand         /* FALL THROUGH */
345becf8217SDavid Hildenbrand     case PGM_OPERATION:
346becf8217SDavid Hildenbrand     case PGM_PRIVILEGED:
347becf8217SDavid Hildenbrand     case PGM_EXECUTE:
348becf8217SDavid Hildenbrand     case PGM_PROTECTION:
349becf8217SDavid Hildenbrand     case PGM_ADDRESSING:
350becf8217SDavid Hildenbrand     case PGM_SPECIFICATION:
351becf8217SDavid Hildenbrand     case PGM_DATA:
352becf8217SDavid Hildenbrand     case PGM_FIXPT_OVERFLOW:
353becf8217SDavid Hildenbrand     case PGM_FIXPT_DIVIDE:
354becf8217SDavid Hildenbrand     case PGM_DEC_OVERFLOW:
355becf8217SDavid Hildenbrand     case PGM_DEC_DIVIDE:
356becf8217SDavid Hildenbrand     case PGM_HFP_EXP_OVERFLOW:
357becf8217SDavid Hildenbrand     case PGM_HFP_EXP_UNDERFLOW:
358becf8217SDavid Hildenbrand     case PGM_HFP_SIGNIFICANCE:
359becf8217SDavid Hildenbrand     case PGM_HFP_DIVIDE:
360becf8217SDavid Hildenbrand     case PGM_TRANS_SPEC:
361becf8217SDavid Hildenbrand     case PGM_SPECIAL_OP:
362becf8217SDavid Hildenbrand     case PGM_OPERAND:
363becf8217SDavid Hildenbrand     case PGM_HFP_SQRT:
364becf8217SDavid Hildenbrand     case PGM_PC_TRANS_SPEC:
365becf8217SDavid Hildenbrand     case PGM_ALET_SPEC:
366becf8217SDavid Hildenbrand     case PGM_MONITOR:
367becf8217SDavid Hildenbrand         /* advance the PSW if our exception is not nullifying */
368d5a103cdSRichard Henderson         env->psw.addr += ilen;
369d5a43964SAlexander Graf         break;
370d5a43964SAlexander Graf     }
371d5a43964SAlexander Graf 
372d5a103cdSRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
373d5a103cdSRichard Henderson                   __func__, env->int_pgm_code, ilen);
374d5a43964SAlexander Graf 
3754782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
376d5a43964SAlexander Graf 
377777c98c3SAurelien Jarno     /* Signal PER events with the exception.  */
378777c98c3SAurelien Jarno     if (env->per_perc_atmid) {
379777c98c3SAurelien Jarno         env->int_pgm_code |= PGM_PER;
380777c98c3SAurelien Jarno         lowcore->per_address = cpu_to_be64(env->per_address);
381777c98c3SAurelien Jarno         lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid);
382777c98c3SAurelien Jarno         env->per_perc_atmid = 0;
383777c98c3SAurelien Jarno     }
384777c98c3SAurelien Jarno 
385d5a103cdSRichard Henderson     lowcore->pgm_ilen = cpu_to_be16(ilen);
386d5a43964SAlexander Graf     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
387d5a43964SAlexander Graf     lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
388d5a43964SAlexander Graf     lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
389d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->program_new_psw.mask);
390d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->program_new_psw.addr);
3913da0ab35SAurelien Jarno     lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
392d5a43964SAlexander Graf 
3934782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
394d5a43964SAlexander Graf 
39571e47088SBlue Swirl     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
396d5a103cdSRichard Henderson             env->int_pgm_code, ilen, env->psw.mask,
397d5a43964SAlexander Graf             env->psw.addr);
398d5a43964SAlexander Graf 
399d5a43964SAlexander Graf     load_psw(env, mask, addr);
400d5a43964SAlexander Graf }
401d5a43964SAlexander Graf 
402777c98c3SAurelien Jarno static void do_svc_interrupt(CPUS390XState *env)
403777c98c3SAurelien Jarno {
404777c98c3SAurelien Jarno     uint64_t mask, addr;
405777c98c3SAurelien Jarno     LowCore *lowcore;
406777c98c3SAurelien Jarno 
407777c98c3SAurelien Jarno     lowcore = cpu_map_lowcore(env);
408777c98c3SAurelien Jarno 
409777c98c3SAurelien Jarno     lowcore->svc_code = cpu_to_be16(env->int_svc_code);
410777c98c3SAurelien Jarno     lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
411777c98c3SAurelien Jarno     lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
412777c98c3SAurelien Jarno     lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
413777c98c3SAurelien Jarno     mask = be64_to_cpu(lowcore->svc_new_psw.mask);
414777c98c3SAurelien Jarno     addr = be64_to_cpu(lowcore->svc_new_psw.addr);
415777c98c3SAurelien Jarno 
416777c98c3SAurelien Jarno     cpu_unmap_lowcore(lowcore);
417777c98c3SAurelien Jarno 
418777c98c3SAurelien Jarno     load_psw(env, mask, addr);
419777c98c3SAurelien Jarno 
420777c98c3SAurelien Jarno     /* When a PER event is pending, the PER exception has to happen
421777c98c3SAurelien Jarno        immediately after the SERVICE CALL one.  */
422777c98c3SAurelien Jarno     if (env->per_perc_atmid) {
423777c98c3SAurelien Jarno         env->int_pgm_code = PGM_PER;
424777c98c3SAurelien Jarno         env->int_pgm_ilen = env->int_svc_ilen;
425777c98c3SAurelien Jarno         do_program_interrupt(env);
426777c98c3SAurelien Jarno     }
427777c98c3SAurelien Jarno }
428777c98c3SAurelien Jarno 
429d5a43964SAlexander Graf #define VIRTIO_SUBCODE_64 0x0D00
430d5a43964SAlexander Graf 
431a4e3ad19SAndreas Färber static void do_ext_interrupt(CPUS390XState *env)
432d5a43964SAlexander Graf {
433a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
434d5a43964SAlexander Graf     uint64_t mask, addr;
435d5a43964SAlexander Graf     LowCore *lowcore;
436d5a43964SAlexander Graf     ExtQueue *q;
437d5a43964SAlexander Graf 
438d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_EXT)) {
439a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
440d5a43964SAlexander Graf     }
441d5a43964SAlexander Graf 
4421a719923Szhanghailiang     if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) {
443a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
444d5a43964SAlexander Graf     }
445d5a43964SAlexander Graf 
446d5a43964SAlexander Graf     q = &env->ext_queue[env->ext_index];
4474782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
448d5a43964SAlexander Graf 
449d5a43964SAlexander Graf     lowcore->ext_int_code = cpu_to_be16(q->code);
450d5a43964SAlexander Graf     lowcore->ext_params = cpu_to_be32(q->param);
451d5a43964SAlexander Graf     lowcore->ext_params2 = cpu_to_be64(q->param64);
452d5a43964SAlexander Graf     lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
453d5a43964SAlexander Graf     lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
454d5a43964SAlexander Graf     lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
455d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->external_new_psw.mask);
456d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->external_new_psw.addr);
457d5a43964SAlexander Graf 
4584782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
459d5a43964SAlexander Graf 
460d5a43964SAlexander Graf     env->ext_index--;
461d5a43964SAlexander Graf     if (env->ext_index == -1) {
462d5a43964SAlexander Graf         env->pending_int &= ~INTERRUPT_EXT;
463d5a43964SAlexander Graf     }
464d5a43964SAlexander Graf 
46571e47088SBlue Swirl     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
466d5a43964SAlexander Graf             env->psw.mask, env->psw.addr);
467d5a43964SAlexander Graf 
468d5a43964SAlexander Graf     load_psw(env, mask, addr);
469d5a43964SAlexander Graf }
4703110e292SAlexander Graf 
4715d69c547SCornelia Huck static void do_io_interrupt(CPUS390XState *env)
4725d69c547SCornelia Huck {
473a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
4745d69c547SCornelia Huck     LowCore *lowcore;
4755d69c547SCornelia Huck     IOIntQueue *q;
4765d69c547SCornelia Huck     uint8_t isc;
4775d69c547SCornelia Huck     int disable = 1;
4785d69c547SCornelia Huck     int found = 0;
4795d69c547SCornelia Huck 
4805d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_IO)) {
481a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n");
4825d69c547SCornelia Huck     }
4835d69c547SCornelia Huck 
4845d69c547SCornelia Huck     for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
48591b0a8f3SCornelia Huck         uint64_t isc_bits;
48691b0a8f3SCornelia Huck 
4875d69c547SCornelia Huck         if (env->io_index[isc] < 0) {
4885d69c547SCornelia Huck             continue;
4895d69c547SCornelia Huck         }
4901a719923Szhanghailiang         if (env->io_index[isc] >= MAX_IO_QUEUE) {
491a47dddd7SAndreas Färber             cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n",
4925d69c547SCornelia Huck                       isc, env->io_index[isc]);
4935d69c547SCornelia Huck         }
4945d69c547SCornelia Huck 
4955d69c547SCornelia Huck         q = &env->io_queue[env->io_index[isc]][isc];
49691b0a8f3SCornelia Huck         isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
49791b0a8f3SCornelia Huck         if (!(env->cregs[6] & isc_bits)) {
4985d69c547SCornelia Huck             disable = 0;
4995d69c547SCornelia Huck             continue;
5005d69c547SCornelia Huck         }
501bd9a8d85SCornelia Huck         if (!found) {
502bd9a8d85SCornelia Huck             uint64_t mask, addr;
503bd9a8d85SCornelia Huck 
5045d69c547SCornelia Huck             found = 1;
5055d69c547SCornelia Huck             lowcore = cpu_map_lowcore(env);
5065d69c547SCornelia Huck 
5075d69c547SCornelia Huck             lowcore->subchannel_id = cpu_to_be16(q->id);
5085d69c547SCornelia Huck             lowcore->subchannel_nr = cpu_to_be16(q->nr);
5095d69c547SCornelia Huck             lowcore->io_int_parm = cpu_to_be32(q->parm);
5105d69c547SCornelia Huck             lowcore->io_int_word = cpu_to_be32(q->word);
5115d69c547SCornelia Huck             lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
5125d69c547SCornelia Huck             lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
5135d69c547SCornelia Huck             mask = be64_to_cpu(lowcore->io_new_psw.mask);
5145d69c547SCornelia Huck             addr = be64_to_cpu(lowcore->io_new_psw.addr);
5155d69c547SCornelia Huck 
5165d69c547SCornelia Huck             cpu_unmap_lowcore(lowcore);
5175d69c547SCornelia Huck 
5185d69c547SCornelia Huck             env->io_index[isc]--;
519bd9a8d85SCornelia Huck 
520bd9a8d85SCornelia Huck             DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
521bd9a8d85SCornelia Huck                     env->psw.mask, env->psw.addr);
522bd9a8d85SCornelia Huck             load_psw(env, mask, addr);
523bd9a8d85SCornelia Huck         }
524b22dd124SStefan Weil         if (env->io_index[isc] >= 0) {
5255d69c547SCornelia Huck             disable = 0;
5265d69c547SCornelia Huck         }
527bd9a8d85SCornelia Huck         continue;
5285d69c547SCornelia Huck     }
5295d69c547SCornelia Huck 
5305d69c547SCornelia Huck     if (disable) {
5315d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_IO;
5325d69c547SCornelia Huck     }
5335d69c547SCornelia Huck 
5345d69c547SCornelia Huck }
5355d69c547SCornelia Huck 
5365d69c547SCornelia Huck static void do_mchk_interrupt(CPUS390XState *env)
5375d69c547SCornelia Huck {
538a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
5395d69c547SCornelia Huck     uint64_t mask, addr;
5405d69c547SCornelia Huck     LowCore *lowcore;
5415d69c547SCornelia Huck     MchkQueue *q;
5425d69c547SCornelia Huck     int i;
5435d69c547SCornelia Huck 
5445d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_MCHECK)) {
545a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n");
5465d69c547SCornelia Huck     }
5475d69c547SCornelia Huck 
5481a719923Szhanghailiang     if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) {
549a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
5505d69c547SCornelia Huck     }
5515d69c547SCornelia Huck 
5525d69c547SCornelia Huck     q = &env->mchk_queue[env->mchk_index];
5535d69c547SCornelia Huck 
5545d69c547SCornelia Huck     if (q->type != 1) {
5555d69c547SCornelia Huck         /* Don't know how to handle this... */
556a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
5575d69c547SCornelia Huck     }
5585d69c547SCornelia Huck     if (!(env->cregs[14] & (1 << 28))) {
5595d69c547SCornelia Huck         /* CRW machine checks disabled */
5605d69c547SCornelia Huck         return;
5615d69c547SCornelia Huck     }
5625d69c547SCornelia Huck 
5635d69c547SCornelia Huck     lowcore = cpu_map_lowcore(env);
5645d69c547SCornelia Huck 
5655d69c547SCornelia Huck     for (i = 0; i < 16; i++) {
566c498d8e3SEric Farman         lowcore->floating_pt_save_area[i] = cpu_to_be64(get_freg(env, i)->ll);
5675d69c547SCornelia Huck         lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
5685d69c547SCornelia Huck         lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
5695d69c547SCornelia Huck         lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
5705d69c547SCornelia Huck     }
5715d69c547SCornelia Huck     lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
5725d69c547SCornelia Huck     lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
5735d69c547SCornelia Huck     lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
5745d69c547SCornelia Huck     lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
5755d69c547SCornelia Huck     lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
5765d69c547SCornelia Huck     lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
5775d69c547SCornelia Huck     lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
5785d69c547SCornelia Huck 
5795d69c547SCornelia Huck     lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
5805d69c547SCornelia Huck     lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
5815d69c547SCornelia Huck     lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
5825d69c547SCornelia Huck     lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
5835d69c547SCornelia Huck     mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
5845d69c547SCornelia Huck     addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
5855d69c547SCornelia Huck 
5865d69c547SCornelia Huck     cpu_unmap_lowcore(lowcore);
5875d69c547SCornelia Huck 
5885d69c547SCornelia Huck     env->mchk_index--;
5895d69c547SCornelia Huck     if (env->mchk_index == -1) {
5905d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_MCHK;
5915d69c547SCornelia Huck     }
5925d69c547SCornelia Huck 
5935d69c547SCornelia Huck     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
5945d69c547SCornelia Huck             env->psw.mask, env->psw.addr);
5955d69c547SCornelia Huck 
5965d69c547SCornelia Huck     load_psw(env, mask, addr);
5975d69c547SCornelia Huck }
5985d69c547SCornelia Huck 
59997a8ea5aSAndreas Färber void s390_cpu_do_interrupt(CPUState *cs)
6003110e292SAlexander Graf {
60197a8ea5aSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
60297a8ea5aSAndreas Färber     CPUS390XState *env = &cpu->env;
603f9466733SAndreas Färber 
6040d404541SRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
60527103424SAndreas Färber                   __func__, cs->exception_index, env->psw.addr);
606d5a43964SAlexander Graf 
607eb24f7c6SDavid Hildenbrand     s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
6085d69c547SCornelia Huck     /* handle machine checks */
6095d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_MCHECK) &&
61027103424SAndreas Färber         (cs->exception_index == -1)) {
6115d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_MCHK) {
61227103424SAndreas Färber             cs->exception_index = EXCP_MCHK;
6135d69c547SCornelia Huck         }
6145d69c547SCornelia Huck     }
615d5a43964SAlexander Graf     /* handle external interrupts */
616d5a43964SAlexander Graf     if ((env->psw.mask & PSW_MASK_EXT) &&
61727103424SAndreas Färber         cs->exception_index == -1) {
618d5a43964SAlexander Graf         if (env->pending_int & INTERRUPT_EXT) {
619d5a43964SAlexander Graf             /* code is already in env */
62027103424SAndreas Färber             cs->exception_index = EXCP_EXT;
621d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_TOD) {
622f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1004, 0, 0);
62327103424SAndreas Färber             cs->exception_index = EXCP_EXT;
624d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
625d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
626d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_CPUTIMER) {
627f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1005, 0, 0);
62827103424SAndreas Färber             cs->exception_index = EXCP_EXT;
629d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
630d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
6313110e292SAlexander Graf         }
632d5a43964SAlexander Graf     }
6335d69c547SCornelia Huck     /* handle I/O interrupts */
6345d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_IO) &&
63527103424SAndreas Färber         (cs->exception_index == -1)) {
6365d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_IO) {
63727103424SAndreas Färber             cs->exception_index = EXCP_IO;
6385d69c547SCornelia Huck         }
6395d69c547SCornelia Huck     }
640d5a43964SAlexander Graf 
64127103424SAndreas Färber     switch (cs->exception_index) {
642d5a43964SAlexander Graf     case EXCP_PGM:
643d5a43964SAlexander Graf         do_program_interrupt(env);
644d5a43964SAlexander Graf         break;
645d5a43964SAlexander Graf     case EXCP_SVC:
646d5a43964SAlexander Graf         do_svc_interrupt(env);
647d5a43964SAlexander Graf         break;
648d5a43964SAlexander Graf     case EXCP_EXT:
649d5a43964SAlexander Graf         do_ext_interrupt(env);
650d5a43964SAlexander Graf         break;
6515d69c547SCornelia Huck     case EXCP_IO:
6525d69c547SCornelia Huck         do_io_interrupt(env);
6535d69c547SCornelia Huck         break;
6545d69c547SCornelia Huck     case EXCP_MCHK:
6555d69c547SCornelia Huck         do_mchk_interrupt(env);
6565d69c547SCornelia Huck         break;
657d5a43964SAlexander Graf     }
65827103424SAndreas Färber     cs->exception_index = -1;
659d5a43964SAlexander Graf 
660d5a43964SAlexander Graf     if (!env->pending_int) {
661259186a7SAndreas Färber         cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
662d5a43964SAlexander Graf     }
663d5a43964SAlexander Graf }
664d5a43964SAlexander Graf 
66502bb9bbfSRichard Henderson bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
66602bb9bbfSRichard Henderson {
66702bb9bbfSRichard Henderson     if (interrupt_request & CPU_INTERRUPT_HARD) {
66802bb9bbfSRichard Henderson         S390CPU *cpu = S390_CPU(cs);
66902bb9bbfSRichard Henderson         CPUS390XState *env = &cpu->env;
67002bb9bbfSRichard Henderson 
671303c681aSRichard Henderson         if (env->ex_value) {
672303c681aSRichard Henderson             /* Execution of the target insn is indivisible from
673303c681aSRichard Henderson                the parent EXECUTE insn.  */
674303c681aSRichard Henderson             return false;
675303c681aSRichard Henderson         }
67602bb9bbfSRichard Henderson         if (env->psw.mask & PSW_MASK_EXT) {
67702bb9bbfSRichard Henderson             s390_cpu_do_interrupt(cs);
67802bb9bbfSRichard Henderson             return true;
67902bb9bbfSRichard Henderson         }
68002bb9bbfSRichard Henderson     }
68102bb9bbfSRichard Henderson     return false;
68202bb9bbfSRichard Henderson }
683311918b9SAurelien Jarno 
684311918b9SAurelien Jarno void s390_cpu_recompute_watchpoints(CPUState *cs)
685311918b9SAurelien Jarno {
686311918b9SAurelien Jarno     const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS;
687311918b9SAurelien Jarno     S390CPU *cpu = S390_CPU(cs);
688311918b9SAurelien Jarno     CPUS390XState *env = &cpu->env;
689311918b9SAurelien Jarno 
690311918b9SAurelien Jarno     /* We are called when the watchpoints have changed. First
691311918b9SAurelien Jarno        remove them all.  */
692311918b9SAurelien Jarno     cpu_watchpoint_remove_all(cs, BP_CPU);
693311918b9SAurelien Jarno 
694311918b9SAurelien Jarno     /* Return if PER is not enabled */
695311918b9SAurelien Jarno     if (!(env->psw.mask & PSW_MASK_PER)) {
696311918b9SAurelien Jarno         return;
697311918b9SAurelien Jarno     }
698311918b9SAurelien Jarno 
699311918b9SAurelien Jarno     /* Return if storage-alteration event is not enabled.  */
700311918b9SAurelien Jarno     if (!(env->cregs[9] & PER_CR9_EVENT_STORE)) {
701311918b9SAurelien Jarno         return;
702311918b9SAurelien Jarno     }
703311918b9SAurelien Jarno 
704311918b9SAurelien Jarno     if (env->cregs[10] == 0 && env->cregs[11] == -1LL) {
705311918b9SAurelien Jarno         /* We can't create a watchoint spanning the whole memory range, so
706311918b9SAurelien Jarno            split it in two parts.   */
707311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 0, 1ULL << 63, wp_flags, NULL);
708311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 1ULL << 63, 1ULL << 63, wp_flags, NULL);
709311918b9SAurelien Jarno     } else if (env->cregs[10] > env->cregs[11]) {
710311918b9SAurelien Jarno         /* The address range loops, create two watchpoints.  */
711311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, env->cregs[10], -env->cregs[10],
712311918b9SAurelien Jarno                               wp_flags, NULL);
713311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 0, env->cregs[11] + 1, wp_flags, NULL);
714311918b9SAurelien Jarno 
715311918b9SAurelien Jarno     } else {
716311918b9SAurelien Jarno         /* Default case, create a single watchpoint.  */
717311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, env->cregs[10],
718311918b9SAurelien Jarno                               env->cregs[11] - env->cregs[10] + 1,
719311918b9SAurelien Jarno                               wp_flags, NULL);
720311918b9SAurelien Jarno     }
721311918b9SAurelien Jarno }
722311918b9SAurelien Jarno 
723311918b9SAurelien Jarno void s390x_cpu_debug_excp_handler(CPUState *cs)
724311918b9SAurelien Jarno {
725311918b9SAurelien Jarno     S390CPU *cpu = S390_CPU(cs);
726311918b9SAurelien Jarno     CPUS390XState *env = &cpu->env;
727311918b9SAurelien Jarno     CPUWatchpoint *wp_hit = cs->watchpoint_hit;
728311918b9SAurelien Jarno 
729311918b9SAurelien Jarno     if (wp_hit && wp_hit->flags & BP_CPU) {
730311918b9SAurelien Jarno         /* FIXME: When the storage-alteration-space control bit is set,
731311918b9SAurelien Jarno            the exception should only be triggered if the memory access
732311918b9SAurelien Jarno            is done using an address space with the storage-alteration-event
733311918b9SAurelien Jarno            bit set.  We have no way to detect that with the current
734311918b9SAurelien Jarno            watchpoint code.  */
735311918b9SAurelien Jarno         cs->watchpoint_hit = NULL;
736311918b9SAurelien Jarno 
737311918b9SAurelien Jarno         env->per_address = env->psw.addr;
738311918b9SAurelien Jarno         env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env);
739311918b9SAurelien Jarno         /* FIXME: We currently no way to detect the address space used
740311918b9SAurelien Jarno            to trigger the watchpoint.  For now just consider it is the
741311918b9SAurelien Jarno            current default ASC. This turn to be true except when MVCP
742311918b9SAurelien Jarno            and MVCS instrutions are not used.  */
743311918b9SAurelien Jarno         env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46;
744311918b9SAurelien Jarno 
745311918b9SAurelien Jarno         /* Remove all watchpoints to re-execute the code.  A PER exception
746311918b9SAurelien Jarno            will be triggered, it will call load_psw which will recompute
747311918b9SAurelien Jarno            the watchpoints.  */
748311918b9SAurelien Jarno         cpu_watchpoint_remove_all(cs, BP_CPU);
7496886b980SPeter Maydell         cpu_loop_exit_noexc(cs);
750311918b9SAurelien Jarno     }
751311918b9SAurelien Jarno }
75244977a8fSRichard Henderson 
75344977a8fSRichard Henderson /* Unaligned accesses are only diagnosed with MO_ALIGN.  At the moment,
75444977a8fSRichard Henderson    this is only for the atomic operations, for which we want to raise a
75544977a8fSRichard Henderson    specification exception.  */
75644977a8fSRichard Henderson void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
75744977a8fSRichard Henderson                                    MMUAccessType access_type,
75844977a8fSRichard Henderson                                    int mmu_idx, uintptr_t retaddr)
75944977a8fSRichard Henderson {
76044977a8fSRichard Henderson     S390CPU *cpu = S390_CPU(cs);
76144977a8fSRichard Henderson     CPUS390XState *env = &cpu->env;
76244977a8fSRichard Henderson 
76344977a8fSRichard Henderson     if (retaddr) {
76444977a8fSRichard Henderson         cpu_restore_state(cs, retaddr);
76544977a8fSRichard Henderson     }
766becf8217SDavid Hildenbrand     program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
76744977a8fSRichard Henderson }
768d5a43964SAlexander Graf #endif /* CONFIG_USER_ONLY */
769*b5bd2e91SThomas Huth 
770*b5bd2e91SThomas Huth void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
771*b5bd2e91SThomas Huth                          int flags)
772*b5bd2e91SThomas Huth {
773*b5bd2e91SThomas Huth     S390CPU *cpu = S390_CPU(cs);
774*b5bd2e91SThomas Huth     CPUS390XState *env = &cpu->env;
775*b5bd2e91SThomas Huth     int i;
776*b5bd2e91SThomas Huth 
777*b5bd2e91SThomas Huth     if (env->cc_op > 3) {
778*b5bd2e91SThomas Huth         cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n",
779*b5bd2e91SThomas Huth                     env->psw.mask, env->psw.addr, cc_name(env->cc_op));
780*b5bd2e91SThomas Huth     } else {
781*b5bd2e91SThomas Huth         cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n",
782*b5bd2e91SThomas Huth                     env->psw.mask, env->psw.addr, env->cc_op);
783*b5bd2e91SThomas Huth     }
784*b5bd2e91SThomas Huth 
785*b5bd2e91SThomas Huth     for (i = 0; i < 16; i++) {
786*b5bd2e91SThomas Huth         cpu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]);
787*b5bd2e91SThomas Huth         if ((i % 4) == 3) {
788*b5bd2e91SThomas Huth             cpu_fprintf(f, "\n");
789*b5bd2e91SThomas Huth         } else {
790*b5bd2e91SThomas Huth             cpu_fprintf(f, " ");
791*b5bd2e91SThomas Huth         }
792*b5bd2e91SThomas Huth     }
793*b5bd2e91SThomas Huth 
794*b5bd2e91SThomas Huth     for (i = 0; i < 16; i++) {
795*b5bd2e91SThomas Huth         cpu_fprintf(f, "F%02d=%016" PRIx64, i, get_freg(env, i)->ll);
796*b5bd2e91SThomas Huth         if ((i % 4) == 3) {
797*b5bd2e91SThomas Huth             cpu_fprintf(f, "\n");
798*b5bd2e91SThomas Huth         } else {
799*b5bd2e91SThomas Huth             cpu_fprintf(f, " ");
800*b5bd2e91SThomas Huth         }
801*b5bd2e91SThomas Huth     }
802*b5bd2e91SThomas Huth 
803*b5bd2e91SThomas Huth     for (i = 0; i < 32; i++) {
804*b5bd2e91SThomas Huth         cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i,
805*b5bd2e91SThomas Huth                     env->vregs[i][0].ll, env->vregs[i][1].ll);
806*b5bd2e91SThomas Huth         cpu_fprintf(f, (i % 2) ? "\n" : " ");
807*b5bd2e91SThomas Huth     }
808*b5bd2e91SThomas Huth 
809*b5bd2e91SThomas Huth #ifndef CONFIG_USER_ONLY
810*b5bd2e91SThomas Huth     for (i = 0; i < 16; i++) {
811*b5bd2e91SThomas Huth         cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]);
812*b5bd2e91SThomas Huth         if ((i % 4) == 3) {
813*b5bd2e91SThomas Huth             cpu_fprintf(f, "\n");
814*b5bd2e91SThomas Huth         } else {
815*b5bd2e91SThomas Huth             cpu_fprintf(f, " ");
816*b5bd2e91SThomas Huth         }
817*b5bd2e91SThomas Huth     }
818*b5bd2e91SThomas Huth #endif
819*b5bd2e91SThomas Huth 
820*b5bd2e91SThomas Huth #ifdef DEBUG_INLINE_BRANCHES
821*b5bd2e91SThomas Huth     for (i = 0; i < CC_OP_MAX; i++) {
822*b5bd2e91SThomas Huth         cpu_fprintf(f, "  %15s = %10ld\t%10ld\n", cc_name(i),
823*b5bd2e91SThomas Huth                     inline_branch_miss[i], inline_branch_hit[i]);
824*b5bd2e91SThomas Huth     }
825*b5bd2e91SThomas Huth #endif
826*b5bd2e91SThomas Huth 
827*b5bd2e91SThomas Huth     cpu_fprintf(f, "\n");
828*b5bd2e91SThomas Huth }
829