xref: /qemu/target/s390x/helper.c (revision 303c681a8f50eb88fbafc2bf6a7e4c5813ac2be0)
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);
207d5a103cdSRichard Henderson         trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
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 
334d5a103cdSRichard Henderson     switch (ilen) {
335d5a103cdSRichard Henderson     case ILEN_LATER:
336d5a103cdSRichard Henderson         ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
337d5a43964SAlexander Graf         break;
338d5a103cdSRichard Henderson     case ILEN_LATER_INC:
339d5a103cdSRichard Henderson         ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
340d5a103cdSRichard Henderson         env->psw.addr += ilen;
341d5a43964SAlexander Graf         break;
342d5a103cdSRichard Henderson     default:
343d5a103cdSRichard Henderson         assert(ilen == 2 || ilen == 4 || ilen == 6);
344d5a43964SAlexander Graf     }
345d5a43964SAlexander Graf 
346d5a103cdSRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
347d5a103cdSRichard Henderson                   __func__, env->int_pgm_code, ilen);
348d5a43964SAlexander Graf 
3494782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
350d5a43964SAlexander Graf 
351777c98c3SAurelien Jarno     /* Signal PER events with the exception.  */
352777c98c3SAurelien Jarno     if (env->per_perc_atmid) {
353777c98c3SAurelien Jarno         env->int_pgm_code |= PGM_PER;
354777c98c3SAurelien Jarno         lowcore->per_address = cpu_to_be64(env->per_address);
355777c98c3SAurelien Jarno         lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid);
356777c98c3SAurelien Jarno         env->per_perc_atmid = 0;
357777c98c3SAurelien Jarno     }
358777c98c3SAurelien Jarno 
359d5a103cdSRichard Henderson     lowcore->pgm_ilen = cpu_to_be16(ilen);
360d5a43964SAlexander Graf     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
361d5a43964SAlexander Graf     lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
362d5a43964SAlexander Graf     lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
363d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->program_new_psw.mask);
364d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->program_new_psw.addr);
3653da0ab35SAurelien Jarno     lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
366d5a43964SAlexander Graf 
3674782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
368d5a43964SAlexander Graf 
36971e47088SBlue Swirl     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
370d5a103cdSRichard Henderson             env->int_pgm_code, ilen, env->psw.mask,
371d5a43964SAlexander Graf             env->psw.addr);
372d5a43964SAlexander Graf 
373d5a43964SAlexander Graf     load_psw(env, mask, addr);
374d5a43964SAlexander Graf }
375d5a43964SAlexander Graf 
376777c98c3SAurelien Jarno static void do_svc_interrupt(CPUS390XState *env)
377777c98c3SAurelien Jarno {
378777c98c3SAurelien Jarno     uint64_t mask, addr;
379777c98c3SAurelien Jarno     LowCore *lowcore;
380777c98c3SAurelien Jarno 
381777c98c3SAurelien Jarno     lowcore = cpu_map_lowcore(env);
382777c98c3SAurelien Jarno 
383777c98c3SAurelien Jarno     lowcore->svc_code = cpu_to_be16(env->int_svc_code);
384777c98c3SAurelien Jarno     lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
385777c98c3SAurelien Jarno     lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
386777c98c3SAurelien Jarno     lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
387777c98c3SAurelien Jarno     mask = be64_to_cpu(lowcore->svc_new_psw.mask);
388777c98c3SAurelien Jarno     addr = be64_to_cpu(lowcore->svc_new_psw.addr);
389777c98c3SAurelien Jarno 
390777c98c3SAurelien Jarno     cpu_unmap_lowcore(lowcore);
391777c98c3SAurelien Jarno 
392777c98c3SAurelien Jarno     load_psw(env, mask, addr);
393777c98c3SAurelien Jarno 
394777c98c3SAurelien Jarno     /* When a PER event is pending, the PER exception has to happen
395777c98c3SAurelien Jarno        immediately after the SERVICE CALL one.  */
396777c98c3SAurelien Jarno     if (env->per_perc_atmid) {
397777c98c3SAurelien Jarno         env->int_pgm_code = PGM_PER;
398777c98c3SAurelien Jarno         env->int_pgm_ilen = env->int_svc_ilen;
399777c98c3SAurelien Jarno         do_program_interrupt(env);
400777c98c3SAurelien Jarno     }
401777c98c3SAurelien Jarno }
402777c98c3SAurelien Jarno 
403d5a43964SAlexander Graf #define VIRTIO_SUBCODE_64 0x0D00
404d5a43964SAlexander Graf 
405a4e3ad19SAndreas Färber static void do_ext_interrupt(CPUS390XState *env)
406d5a43964SAlexander Graf {
407a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
408d5a43964SAlexander Graf     uint64_t mask, addr;
409d5a43964SAlexander Graf     LowCore *lowcore;
410d5a43964SAlexander Graf     ExtQueue *q;
411d5a43964SAlexander Graf 
412d5a43964SAlexander Graf     if (!(env->psw.mask & PSW_MASK_EXT)) {
413a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
414d5a43964SAlexander Graf     }
415d5a43964SAlexander Graf 
4161a719923Szhanghailiang     if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) {
417a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
418d5a43964SAlexander Graf     }
419d5a43964SAlexander Graf 
420d5a43964SAlexander Graf     q = &env->ext_queue[env->ext_index];
4214782a23bSCornelia Huck     lowcore = cpu_map_lowcore(env);
422d5a43964SAlexander Graf 
423d5a43964SAlexander Graf     lowcore->ext_int_code = cpu_to_be16(q->code);
424d5a43964SAlexander Graf     lowcore->ext_params = cpu_to_be32(q->param);
425d5a43964SAlexander Graf     lowcore->ext_params2 = cpu_to_be64(q->param64);
426d5a43964SAlexander Graf     lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
427d5a43964SAlexander Graf     lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
428d5a43964SAlexander Graf     lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
429d5a43964SAlexander Graf     mask = be64_to_cpu(lowcore->external_new_psw.mask);
430d5a43964SAlexander Graf     addr = be64_to_cpu(lowcore->external_new_psw.addr);
431d5a43964SAlexander Graf 
4324782a23bSCornelia Huck     cpu_unmap_lowcore(lowcore);
433d5a43964SAlexander Graf 
434d5a43964SAlexander Graf     env->ext_index--;
435d5a43964SAlexander Graf     if (env->ext_index == -1) {
436d5a43964SAlexander Graf         env->pending_int &= ~INTERRUPT_EXT;
437d5a43964SAlexander Graf     }
438d5a43964SAlexander Graf 
43971e47088SBlue Swirl     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
440d5a43964SAlexander Graf             env->psw.mask, env->psw.addr);
441d5a43964SAlexander Graf 
442d5a43964SAlexander Graf     load_psw(env, mask, addr);
443d5a43964SAlexander Graf }
4443110e292SAlexander Graf 
4455d69c547SCornelia Huck static void do_io_interrupt(CPUS390XState *env)
4465d69c547SCornelia Huck {
447a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
4485d69c547SCornelia Huck     LowCore *lowcore;
4495d69c547SCornelia Huck     IOIntQueue *q;
4505d69c547SCornelia Huck     uint8_t isc;
4515d69c547SCornelia Huck     int disable = 1;
4525d69c547SCornelia Huck     int found = 0;
4535d69c547SCornelia Huck 
4545d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_IO)) {
455a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n");
4565d69c547SCornelia Huck     }
4575d69c547SCornelia Huck 
4585d69c547SCornelia Huck     for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
45991b0a8f3SCornelia Huck         uint64_t isc_bits;
46091b0a8f3SCornelia Huck 
4615d69c547SCornelia Huck         if (env->io_index[isc] < 0) {
4625d69c547SCornelia Huck             continue;
4635d69c547SCornelia Huck         }
4641a719923Szhanghailiang         if (env->io_index[isc] >= MAX_IO_QUEUE) {
465a47dddd7SAndreas Färber             cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n",
4665d69c547SCornelia Huck                       isc, env->io_index[isc]);
4675d69c547SCornelia Huck         }
4685d69c547SCornelia Huck 
4695d69c547SCornelia Huck         q = &env->io_queue[env->io_index[isc]][isc];
47091b0a8f3SCornelia Huck         isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
47191b0a8f3SCornelia Huck         if (!(env->cregs[6] & isc_bits)) {
4725d69c547SCornelia Huck             disable = 0;
4735d69c547SCornelia Huck             continue;
4745d69c547SCornelia Huck         }
475bd9a8d85SCornelia Huck         if (!found) {
476bd9a8d85SCornelia Huck             uint64_t mask, addr;
477bd9a8d85SCornelia Huck 
4785d69c547SCornelia Huck             found = 1;
4795d69c547SCornelia Huck             lowcore = cpu_map_lowcore(env);
4805d69c547SCornelia Huck 
4815d69c547SCornelia Huck             lowcore->subchannel_id = cpu_to_be16(q->id);
4825d69c547SCornelia Huck             lowcore->subchannel_nr = cpu_to_be16(q->nr);
4835d69c547SCornelia Huck             lowcore->io_int_parm = cpu_to_be32(q->parm);
4845d69c547SCornelia Huck             lowcore->io_int_word = cpu_to_be32(q->word);
4855d69c547SCornelia Huck             lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
4865d69c547SCornelia Huck             lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
4875d69c547SCornelia Huck             mask = be64_to_cpu(lowcore->io_new_psw.mask);
4885d69c547SCornelia Huck             addr = be64_to_cpu(lowcore->io_new_psw.addr);
4895d69c547SCornelia Huck 
4905d69c547SCornelia Huck             cpu_unmap_lowcore(lowcore);
4915d69c547SCornelia Huck 
4925d69c547SCornelia Huck             env->io_index[isc]--;
493bd9a8d85SCornelia Huck 
494bd9a8d85SCornelia Huck             DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
495bd9a8d85SCornelia Huck                     env->psw.mask, env->psw.addr);
496bd9a8d85SCornelia Huck             load_psw(env, mask, addr);
497bd9a8d85SCornelia Huck         }
498b22dd124SStefan Weil         if (env->io_index[isc] >= 0) {
4995d69c547SCornelia Huck             disable = 0;
5005d69c547SCornelia Huck         }
501bd9a8d85SCornelia Huck         continue;
5025d69c547SCornelia Huck     }
5035d69c547SCornelia Huck 
5045d69c547SCornelia Huck     if (disable) {
5055d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_IO;
5065d69c547SCornelia Huck     }
5075d69c547SCornelia Huck 
5085d69c547SCornelia Huck }
5095d69c547SCornelia Huck 
5105d69c547SCornelia Huck static void do_mchk_interrupt(CPUS390XState *env)
5115d69c547SCornelia Huck {
512a47dddd7SAndreas Färber     S390CPU *cpu = s390_env_get_cpu(env);
5135d69c547SCornelia Huck     uint64_t mask, addr;
5145d69c547SCornelia Huck     LowCore *lowcore;
5155d69c547SCornelia Huck     MchkQueue *q;
5165d69c547SCornelia Huck     int i;
5175d69c547SCornelia Huck 
5185d69c547SCornelia Huck     if (!(env->psw.mask & PSW_MASK_MCHECK)) {
519a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n");
5205d69c547SCornelia Huck     }
5215d69c547SCornelia Huck 
5221a719923Szhanghailiang     if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) {
523a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
5245d69c547SCornelia Huck     }
5255d69c547SCornelia Huck 
5265d69c547SCornelia Huck     q = &env->mchk_queue[env->mchk_index];
5275d69c547SCornelia Huck 
5285d69c547SCornelia Huck     if (q->type != 1) {
5295d69c547SCornelia Huck         /* Don't know how to handle this... */
530a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
5315d69c547SCornelia Huck     }
5325d69c547SCornelia Huck     if (!(env->cregs[14] & (1 << 28))) {
5335d69c547SCornelia Huck         /* CRW machine checks disabled */
5345d69c547SCornelia Huck         return;
5355d69c547SCornelia Huck     }
5365d69c547SCornelia Huck 
5375d69c547SCornelia Huck     lowcore = cpu_map_lowcore(env);
5385d69c547SCornelia Huck 
5395d69c547SCornelia Huck     for (i = 0; i < 16; i++) {
540c498d8e3SEric Farman         lowcore->floating_pt_save_area[i] = cpu_to_be64(get_freg(env, i)->ll);
5415d69c547SCornelia Huck         lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
5425d69c547SCornelia Huck         lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
5435d69c547SCornelia Huck         lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
5445d69c547SCornelia Huck     }
5455d69c547SCornelia Huck     lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
5465d69c547SCornelia Huck     lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
5475d69c547SCornelia Huck     lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
5485d69c547SCornelia Huck     lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
5495d69c547SCornelia Huck     lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
5505d69c547SCornelia Huck     lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
5515d69c547SCornelia Huck     lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
5525d69c547SCornelia Huck 
5535d69c547SCornelia Huck     lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
5545d69c547SCornelia Huck     lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
5555d69c547SCornelia Huck     lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
5565d69c547SCornelia Huck     lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
5575d69c547SCornelia Huck     mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
5585d69c547SCornelia Huck     addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
5595d69c547SCornelia Huck 
5605d69c547SCornelia Huck     cpu_unmap_lowcore(lowcore);
5615d69c547SCornelia Huck 
5625d69c547SCornelia Huck     env->mchk_index--;
5635d69c547SCornelia Huck     if (env->mchk_index == -1) {
5645d69c547SCornelia Huck         env->pending_int &= ~INTERRUPT_MCHK;
5655d69c547SCornelia Huck     }
5665d69c547SCornelia Huck 
5675d69c547SCornelia Huck     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
5685d69c547SCornelia Huck             env->psw.mask, env->psw.addr);
5695d69c547SCornelia Huck 
5705d69c547SCornelia Huck     load_psw(env, mask, addr);
5715d69c547SCornelia Huck }
5725d69c547SCornelia Huck 
57397a8ea5aSAndreas Färber void s390_cpu_do_interrupt(CPUState *cs)
5743110e292SAlexander Graf {
57597a8ea5aSAndreas Färber     S390CPU *cpu = S390_CPU(cs);
57697a8ea5aSAndreas Färber     CPUS390XState *env = &cpu->env;
577f9466733SAndreas Färber 
5780d404541SRichard Henderson     qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
57927103424SAndreas Färber                   __func__, cs->exception_index, env->psw.addr);
580d5a43964SAlexander Graf 
581eb24f7c6SDavid Hildenbrand     s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
5825d69c547SCornelia Huck     /* handle machine checks */
5835d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_MCHECK) &&
58427103424SAndreas Färber         (cs->exception_index == -1)) {
5855d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_MCHK) {
58627103424SAndreas Färber             cs->exception_index = EXCP_MCHK;
5875d69c547SCornelia Huck         }
5885d69c547SCornelia Huck     }
589d5a43964SAlexander Graf     /* handle external interrupts */
590d5a43964SAlexander Graf     if ((env->psw.mask & PSW_MASK_EXT) &&
59127103424SAndreas Färber         cs->exception_index == -1) {
592d5a43964SAlexander Graf         if (env->pending_int & INTERRUPT_EXT) {
593d5a43964SAlexander Graf             /* code is already in env */
59427103424SAndreas Färber             cs->exception_index = EXCP_EXT;
595d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_TOD) {
596f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1004, 0, 0);
59727103424SAndreas Färber             cs->exception_index = EXCP_EXT;
598d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
599d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
600d5a43964SAlexander Graf         } else if (env->pending_int & INTERRUPT_CPUTIMER) {
601f9466733SAndreas Färber             cpu_inject_ext(cpu, 0x1005, 0, 0);
60227103424SAndreas Färber             cs->exception_index = EXCP_EXT;
603d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_EXT;
604d5a43964SAlexander Graf             env->pending_int &= ~INTERRUPT_TOD;
6053110e292SAlexander Graf         }
606d5a43964SAlexander Graf     }
6075d69c547SCornelia Huck     /* handle I/O interrupts */
6085d69c547SCornelia Huck     if ((env->psw.mask & PSW_MASK_IO) &&
60927103424SAndreas Färber         (cs->exception_index == -1)) {
6105d69c547SCornelia Huck         if (env->pending_int & INTERRUPT_IO) {
61127103424SAndreas Färber             cs->exception_index = EXCP_IO;
6125d69c547SCornelia Huck         }
6135d69c547SCornelia Huck     }
614d5a43964SAlexander Graf 
61527103424SAndreas Färber     switch (cs->exception_index) {
616d5a43964SAlexander Graf     case EXCP_PGM:
617d5a43964SAlexander Graf         do_program_interrupt(env);
618d5a43964SAlexander Graf         break;
619d5a43964SAlexander Graf     case EXCP_SVC:
620d5a43964SAlexander Graf         do_svc_interrupt(env);
621d5a43964SAlexander Graf         break;
622d5a43964SAlexander Graf     case EXCP_EXT:
623d5a43964SAlexander Graf         do_ext_interrupt(env);
624d5a43964SAlexander Graf         break;
6255d69c547SCornelia Huck     case EXCP_IO:
6265d69c547SCornelia Huck         do_io_interrupt(env);
6275d69c547SCornelia Huck         break;
6285d69c547SCornelia Huck     case EXCP_MCHK:
6295d69c547SCornelia Huck         do_mchk_interrupt(env);
6305d69c547SCornelia Huck         break;
631d5a43964SAlexander Graf     }
63227103424SAndreas Färber     cs->exception_index = -1;
633d5a43964SAlexander Graf 
634d5a43964SAlexander Graf     if (!env->pending_int) {
635259186a7SAndreas Färber         cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
636d5a43964SAlexander Graf     }
637d5a43964SAlexander Graf }
638d5a43964SAlexander Graf 
63902bb9bbfSRichard Henderson bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
64002bb9bbfSRichard Henderson {
64102bb9bbfSRichard Henderson     if (interrupt_request & CPU_INTERRUPT_HARD) {
64202bb9bbfSRichard Henderson         S390CPU *cpu = S390_CPU(cs);
64302bb9bbfSRichard Henderson         CPUS390XState *env = &cpu->env;
64402bb9bbfSRichard Henderson 
645*303c681aSRichard Henderson         if (env->ex_value) {
646*303c681aSRichard Henderson             /* Execution of the target insn is indivisible from
647*303c681aSRichard Henderson                the parent EXECUTE insn.  */
648*303c681aSRichard Henderson             return false;
649*303c681aSRichard Henderson         }
65002bb9bbfSRichard Henderson         if (env->psw.mask & PSW_MASK_EXT) {
65102bb9bbfSRichard Henderson             s390_cpu_do_interrupt(cs);
65202bb9bbfSRichard Henderson             return true;
65302bb9bbfSRichard Henderson         }
65402bb9bbfSRichard Henderson     }
65502bb9bbfSRichard Henderson     return false;
65602bb9bbfSRichard Henderson }
657311918b9SAurelien Jarno 
658311918b9SAurelien Jarno void s390_cpu_recompute_watchpoints(CPUState *cs)
659311918b9SAurelien Jarno {
660311918b9SAurelien Jarno     const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS;
661311918b9SAurelien Jarno     S390CPU *cpu = S390_CPU(cs);
662311918b9SAurelien Jarno     CPUS390XState *env = &cpu->env;
663311918b9SAurelien Jarno 
664311918b9SAurelien Jarno     /* We are called when the watchpoints have changed. First
665311918b9SAurelien Jarno        remove them all.  */
666311918b9SAurelien Jarno     cpu_watchpoint_remove_all(cs, BP_CPU);
667311918b9SAurelien Jarno 
668311918b9SAurelien Jarno     /* Return if PER is not enabled */
669311918b9SAurelien Jarno     if (!(env->psw.mask & PSW_MASK_PER)) {
670311918b9SAurelien Jarno         return;
671311918b9SAurelien Jarno     }
672311918b9SAurelien Jarno 
673311918b9SAurelien Jarno     /* Return if storage-alteration event is not enabled.  */
674311918b9SAurelien Jarno     if (!(env->cregs[9] & PER_CR9_EVENT_STORE)) {
675311918b9SAurelien Jarno         return;
676311918b9SAurelien Jarno     }
677311918b9SAurelien Jarno 
678311918b9SAurelien Jarno     if (env->cregs[10] == 0 && env->cregs[11] == -1LL) {
679311918b9SAurelien Jarno         /* We can't create a watchoint spanning the whole memory range, so
680311918b9SAurelien Jarno            split it in two parts.   */
681311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 0, 1ULL << 63, wp_flags, NULL);
682311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 1ULL << 63, 1ULL << 63, wp_flags, NULL);
683311918b9SAurelien Jarno     } else if (env->cregs[10] > env->cregs[11]) {
684311918b9SAurelien Jarno         /* The address range loops, create two watchpoints.  */
685311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, env->cregs[10], -env->cregs[10],
686311918b9SAurelien Jarno                               wp_flags, NULL);
687311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, 0, env->cregs[11] + 1, wp_flags, NULL);
688311918b9SAurelien Jarno 
689311918b9SAurelien Jarno     } else {
690311918b9SAurelien Jarno         /* Default case, create a single watchpoint.  */
691311918b9SAurelien Jarno         cpu_watchpoint_insert(cs, env->cregs[10],
692311918b9SAurelien Jarno                               env->cregs[11] - env->cregs[10] + 1,
693311918b9SAurelien Jarno                               wp_flags, NULL);
694311918b9SAurelien Jarno     }
695311918b9SAurelien Jarno }
696311918b9SAurelien Jarno 
697311918b9SAurelien Jarno void s390x_cpu_debug_excp_handler(CPUState *cs)
698311918b9SAurelien Jarno {
699311918b9SAurelien Jarno     S390CPU *cpu = S390_CPU(cs);
700311918b9SAurelien Jarno     CPUS390XState *env = &cpu->env;
701311918b9SAurelien Jarno     CPUWatchpoint *wp_hit = cs->watchpoint_hit;
702311918b9SAurelien Jarno 
703311918b9SAurelien Jarno     if (wp_hit && wp_hit->flags & BP_CPU) {
704311918b9SAurelien Jarno         /* FIXME: When the storage-alteration-space control bit is set,
705311918b9SAurelien Jarno            the exception should only be triggered if the memory access
706311918b9SAurelien Jarno            is done using an address space with the storage-alteration-event
707311918b9SAurelien Jarno            bit set.  We have no way to detect that with the current
708311918b9SAurelien Jarno            watchpoint code.  */
709311918b9SAurelien Jarno         cs->watchpoint_hit = NULL;
710311918b9SAurelien Jarno 
711311918b9SAurelien Jarno         env->per_address = env->psw.addr;
712311918b9SAurelien Jarno         env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env);
713311918b9SAurelien Jarno         /* FIXME: We currently no way to detect the address space used
714311918b9SAurelien Jarno            to trigger the watchpoint.  For now just consider it is the
715311918b9SAurelien Jarno            current default ASC. This turn to be true except when MVCP
716311918b9SAurelien Jarno            and MVCS instrutions are not used.  */
717311918b9SAurelien Jarno         env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46;
718311918b9SAurelien Jarno 
719311918b9SAurelien Jarno         /* Remove all watchpoints to re-execute the code.  A PER exception
720311918b9SAurelien Jarno            will be triggered, it will call load_psw which will recompute
721311918b9SAurelien Jarno            the watchpoints.  */
722311918b9SAurelien Jarno         cpu_watchpoint_remove_all(cs, BP_CPU);
7236886b980SPeter Maydell         cpu_loop_exit_noexc(cs);
724311918b9SAurelien Jarno     }
725311918b9SAurelien Jarno }
72644977a8fSRichard Henderson 
72744977a8fSRichard Henderson /* Unaligned accesses are only diagnosed with MO_ALIGN.  At the moment,
72844977a8fSRichard Henderson    this is only for the atomic operations, for which we want to raise a
72944977a8fSRichard Henderson    specification exception.  */
73044977a8fSRichard Henderson void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
73144977a8fSRichard Henderson                                    MMUAccessType access_type,
73244977a8fSRichard Henderson                                    int mmu_idx, uintptr_t retaddr)
73344977a8fSRichard Henderson {
73444977a8fSRichard Henderson     S390CPU *cpu = S390_CPU(cs);
73544977a8fSRichard Henderson     CPUS390XState *env = &cpu->env;
73644977a8fSRichard Henderson 
73744977a8fSRichard Henderson     if (retaddr) {
73844977a8fSRichard Henderson         cpu_restore_state(cs, retaddr);
73944977a8fSRichard Henderson     }
74044977a8fSRichard Henderson     program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER);
74144977a8fSRichard Henderson }
742d5a43964SAlexander Graf #endif /* CONFIG_USER_ONLY */
743