]>
Commit | Line | Data |
---|---|---|
10ec5117 AG |
1 | /* |
2 | * S/390 helpers | |
3 | * | |
4 | * Copyright (c) 2009 Ulrich Hecht | |
d5a43964 | 5 | * Copyright (c) 2011 Alexander Graf |
10ec5117 AG |
6 | * |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
41c6a6dd | 10 | * version 2.1 of the License, or (at your option) any later version. |
10ec5117 AG |
11 | * |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
70539e18 | 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
10ec5117 AG |
19 | */ |
20 | ||
9615495a | 21 | #include "qemu/osdep.h" |
10ec5117 | 22 | #include "cpu.h" |
4e58b838 | 23 | #include "internal.h" |
022c62cb | 24 | #include "exec/gdbstub.h" |
1de7afc9 | 25 | #include "qemu/timer.h" |
90c84c56 | 26 | #include "qemu/qemu-print.h" |
bd3f16ac | 27 | #include "hw/s390x/ioinst.h" |
83f7f329 | 28 | #include "sysemu/hw_accel.h" |
54d31236 | 29 | #include "sysemu/runstate.h" |
ef81522b | 30 | #ifndef CONFIG_USER_ONLY |
14a48c1d | 31 | #include "sysemu/tcg.h" |
ef81522b | 32 | #endif |
10ec5117 | 33 | |
d5a43964 | 34 | #ifndef CONFIG_USER_ONLY |
8f22e0df | 35 | void s390x_tod_timer(void *opaque) |
d5a43964 | 36 | { |
6482b0ff | 37 | cpu_inject_clock_comparator((S390CPU *) opaque); |
d5a43964 AG |
38 | } |
39 | ||
8f22e0df | 40 | void s390x_cpu_timer(void *opaque) |
d5a43964 | 41 | { |
6482b0ff | 42 | cpu_inject_cpu_timer((S390CPU *) opaque); |
d5a43964 AG |
43 | } |
44 | #endif | |
10c339a0 | 45 | |
cded4014 | 46 | #ifndef CONFIG_USER_ONLY |
d5a43964 | 47 | |
00b941e5 | 48 | hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) |
d5a43964 | 49 | { |
00b941e5 AF |
50 | S390CPU *cpu = S390_CPU(cs); |
51 | CPUS390XState *env = &cpu->env; | |
d5a43964 | 52 | target_ulong raddr; |
e3e09d87 | 53 | int prot; |
d5a43964 AG |
54 | uint64_t asc = env->psw.mask & PSW_MASK_ASC; |
55 | ||
56 | /* 31-Bit mode */ | |
57 | if (!(env->psw.mask & PSW_MASK_64)) { | |
58 | vaddr &= 0x7fffffff; | |
59 | } | |
60 | ||
234779a2 DH |
61 | if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) { |
62 | return -1; | |
63 | } | |
d5a43964 AG |
64 | return raddr; |
65 | } | |
66 | ||
770a6379 DH |
67 | hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr) |
68 | { | |
69 | hwaddr phys_addr; | |
70 | target_ulong page; | |
71 | ||
72 | page = vaddr & TARGET_PAGE_MASK; | |
73 | phys_addr = cpu_get_phys_page_debug(cs, page); | |
74 | phys_addr += (vaddr & ~TARGET_PAGE_MASK); | |
75 | ||
76 | return phys_addr; | |
77 | } | |
78 | ||
83f7f329 DH |
79 | static inline bool is_special_wait_psw(uint64_t psw_addr) |
80 | { | |
81 | /* signal quiesce */ | |
82 | return psw_addr == 0xfffUL; | |
83 | } | |
84 | ||
85 | void s390_handle_wait(S390CPU *cpu) | |
86 | { | |
4ada99ad CB |
87 | CPUState *cs = CPU(cpu); |
88 | ||
83f7f329 DH |
89 | if (s390_cpu_halt(cpu) == 0) { |
90 | #ifndef CONFIG_USER_ONLY | |
91 | if (is_special_wait_psw(cpu->env.psw.addr)) { | |
92 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); | |
93 | } else { | |
4ada99ad CB |
94 | cpu->env.crash_reason = S390_CRASH_REASON_DISABLED_WAIT; |
95 | qemu_system_guest_panicked(cpu_get_crash_info(cs)); | |
83f7f329 DH |
96 | } |
97 | #endif | |
98 | } | |
99 | } | |
100 | ||
a4e3ad19 | 101 | void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) |
d5a43964 | 102 | { |
311918b9 AJ |
103 | uint64_t old_mask = env->psw.mask; |
104 | ||
eb24f7c6 DH |
105 | env->psw.addr = addr; |
106 | env->psw.mask = mask; | |
b3a184f5 DH |
107 | |
108 | /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ | |
109 | if (!tcg_enabled()) { | |
110 | return; | |
3f10341f | 111 | } |
b3a184f5 | 112 | env->cc_op = (mask >> 44) & 3; |
eb24f7c6 | 113 | |
311918b9 | 114 | if ((old_mask ^ mask) & PSW_MASK_PER) { |
dc79e928 | 115 | s390_cpu_recompute_watchpoints(env_cpu(env)); |
311918b9 AJ |
116 | } |
117 | ||
b3a184f5 | 118 | if (mask & PSW_MASK_WAIT) { |
dc79e928 | 119 | s390_handle_wait(env_archcpu(env)); |
d5a43964 | 120 | } |
d5a43964 AG |
121 | } |
122 | ||
cded4014 | 123 | uint64_t get_psw_mask(CPUS390XState *env) |
d5a43964 | 124 | { |
3f10341f | 125 | uint64_t r = env->psw.mask; |
d5a43964 | 126 | |
3f10341f DH |
127 | if (tcg_enabled()) { |
128 | env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, | |
129 | env->cc_vr); | |
d5a43964 | 130 | |
3f10341f DH |
131 | r &= ~PSW_MASK_CC; |
132 | assert(!(env->cc_op & ~3)); | |
133 | r |= (uint64_t)env->cc_op << 44; | |
134 | } | |
d5a43964 AG |
135 | |
136 | return r; | |
137 | } | |
138 | ||
cded4014 | 139 | LowCore *cpu_map_lowcore(CPUS390XState *env) |
4782a23b CH |
140 | { |
141 | LowCore *lowcore; | |
142 | hwaddr len = sizeof(LowCore); | |
143 | ||
144 | lowcore = cpu_physical_memory_map(env->psa, &len, 1); | |
145 | ||
146 | if (len < sizeof(LowCore)) { | |
dc79e928 | 147 | cpu_abort(env_cpu(env), "Could not map lowcore\n"); |
4782a23b CH |
148 | } |
149 | ||
150 | return lowcore; | |
151 | } | |
152 | ||
cded4014 | 153 | void cpu_unmap_lowcore(LowCore *lowcore) |
4782a23b CH |
154 | { |
155 | cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); | |
156 | } | |
157 | ||
3f10341f DH |
158 | void do_restart_interrupt(CPUS390XState *env) |
159 | { | |
160 | uint64_t mask, addr; | |
161 | LowCore *lowcore; | |
162 | ||
163 | lowcore = cpu_map_lowcore(env); | |
164 | ||
165 | lowcore->restart_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | |
166 | lowcore->restart_old_psw.addr = cpu_to_be64(env->psw.addr); | |
167 | mask = be64_to_cpu(lowcore->restart_new_psw.mask); | |
168 | addr = be64_to_cpu(lowcore->restart_new_psw.addr); | |
169 | ||
170 | cpu_unmap_lowcore(lowcore); | |
b1ab5f60 | 171 | env->pending_int &= ~INTERRUPT_RESTART; |
3f10341f DH |
172 | |
173 | load_psw(env, mask, addr); | |
174 | } | |
175 | ||
311918b9 AJ |
176 | void s390_cpu_recompute_watchpoints(CPUState *cs) |
177 | { | |
178 | const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS; | |
179 | S390CPU *cpu = S390_CPU(cs); | |
180 | CPUS390XState *env = &cpu->env; | |
181 | ||
182 | /* We are called when the watchpoints have changed. First | |
183 | remove them all. */ | |
184 | cpu_watchpoint_remove_all(cs, BP_CPU); | |
185 | ||
186 | /* Return if PER is not enabled */ | |
187 | if (!(env->psw.mask & PSW_MASK_PER)) { | |
188 | return; | |
189 | } | |
190 | ||
191 | /* Return if storage-alteration event is not enabled. */ | |
192 | if (!(env->cregs[9] & PER_CR9_EVENT_STORE)) { | |
193 | return; | |
194 | } | |
195 | ||
196 | if (env->cregs[10] == 0 && env->cregs[11] == -1LL) { | |
197 | /* We can't create a watchoint spanning the whole memory range, so | |
198 | split it in two parts. */ | |
199 | cpu_watchpoint_insert(cs, 0, 1ULL << 63, wp_flags, NULL); | |
200 | cpu_watchpoint_insert(cs, 1ULL << 63, 1ULL << 63, wp_flags, NULL); | |
201 | } else if (env->cregs[10] > env->cregs[11]) { | |
202 | /* The address range loops, create two watchpoints. */ | |
203 | cpu_watchpoint_insert(cs, env->cregs[10], -env->cregs[10], | |
204 | wp_flags, NULL); | |
205 | cpu_watchpoint_insert(cs, 0, env->cregs[11] + 1, wp_flags, NULL); | |
206 | ||
207 | } else { | |
208 | /* Default case, create a single watchpoint. */ | |
209 | cpu_watchpoint_insert(cs, env->cregs[10], | |
210 | env->cregs[11] - env->cregs[10] + 1, | |
211 | wp_flags, NULL); | |
212 | } | |
213 | } | |
214 | ||
257619be | 215 | typedef struct SigpSaveArea { |
cf729baa DH |
216 | uint64_t fprs[16]; /* 0x0000 */ |
217 | uint64_t grs[16]; /* 0x0080 */ | |
218 | PSW psw; /* 0x0100 */ | |
219 | uint8_t pad_0x0110[0x0118 - 0x0110]; /* 0x0110 */ | |
220 | uint32_t prefix; /* 0x0118 */ | |
221 | uint32_t fpc; /* 0x011c */ | |
222 | uint8_t pad_0x0120[0x0124 - 0x0120]; /* 0x0120 */ | |
223 | uint32_t todpr; /* 0x0124 */ | |
224 | uint64_t cputm; /* 0x0128 */ | |
225 | uint64_t ckc; /* 0x0130 */ | |
226 | uint8_t pad_0x0138[0x0140 - 0x0138]; /* 0x0138 */ | |
227 | uint32_t ars[16]; /* 0x0140 */ | |
228 | uint64_t crs[16]; /* 0x0384 */ | |
257619be DH |
229 | } SigpSaveArea; |
230 | QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512); | |
cf729baa DH |
231 | |
232 | int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) | |
233 | { | |
234 | static const uint8_t ar_id = 1; | |
257619be | 235 | SigpSaveArea *sa; |
cf729baa DH |
236 | hwaddr len = sizeof(*sa); |
237 | int i; | |
238 | ||
239 | sa = cpu_physical_memory_map(addr, &len, 1); | |
240 | if (!sa) { | |
241 | return -EFAULT; | |
242 | } | |
243 | if (len != sizeof(*sa)) { | |
244 | cpu_physical_memory_unmap(sa, len, 1, 0); | |
245 | return -EFAULT; | |
246 | } | |
247 | ||
248 | if (store_arch) { | |
249 | cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); | |
250 | } | |
251 | for (i = 0; i < 16; ++i) { | |
4f83d7d2 | 252 | sa->fprs[i] = cpu_to_be64(*get_freg(&cpu->env, i)); |
cf729baa DH |
253 | } |
254 | for (i = 0; i < 16; ++i) { | |
255 | sa->grs[i] = cpu_to_be64(cpu->env.regs[i]); | |
256 | } | |
257 | sa->psw.addr = cpu_to_be64(cpu->env.psw.addr); | |
258 | sa->psw.mask = cpu_to_be64(get_psw_mask(&cpu->env)); | |
259 | sa->prefix = cpu_to_be32(cpu->env.psa); | |
260 | sa->fpc = cpu_to_be32(cpu->env.fpc); | |
261 | sa->todpr = cpu_to_be32(cpu->env.todpr); | |
262 | sa->cputm = cpu_to_be64(cpu->env.cputm); | |
263 | sa->ckc = cpu_to_be64(cpu->env.ckc >> 8); | |
264 | for (i = 0; i < 16; ++i) { | |
265 | sa->ars[i] = cpu_to_be32(cpu->env.aregs[i]); | |
266 | } | |
267 | for (i = 0; i < 16; ++i) { | |
dc0bbef5 | 268 | sa->crs[i] = cpu_to_be64(cpu->env.cregs[i]); |
cf729baa DH |
269 | } |
270 | ||
271 | cpu_physical_memory_unmap(sa, len, 1, len); | |
272 | ||
273 | return 0; | |
274 | } | |
f875cb0c | 275 | |
2cca53fd DH |
276 | typedef struct SigpAdtlSaveArea { |
277 | uint64_t vregs[32][2]; /* 0x0000 */ | |
278 | uint8_t pad_0x0200[0x0400 - 0x0200]; /* 0x0200 */ | |
279 | uint64_t gscb[4]; /* 0x0400 */ | |
280 | uint8_t pad_0x0420[0x1000 - 0x0420]; /* 0x0420 */ | |
281 | } SigpAdtlSaveArea; | |
282 | QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096); | |
283 | ||
f875cb0c DH |
284 | #define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */ |
285 | int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) | |
286 | { | |
2cca53fd | 287 | SigpAdtlSaveArea *sa; |
f875cb0c | 288 | hwaddr save = len; |
2cca53fd | 289 | int i; |
f875cb0c | 290 | |
2cca53fd DH |
291 | sa = cpu_physical_memory_map(addr, &save, 1); |
292 | if (!sa) { | |
f875cb0c DH |
293 | return -EFAULT; |
294 | } | |
295 | if (save != len) { | |
2cca53fd | 296 | cpu_physical_memory_unmap(sa, len, 1, 0); |
f875cb0c DH |
297 | return -EFAULT; |
298 | } | |
299 | ||
f875cb0c | 300 | if (s390_has_feat(S390_FEAT_VECTOR)) { |
2cca53fd | 301 | for (i = 0; i < 32; i++) { |
4f83d7d2 DH |
302 | sa->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i][0]); |
303 | sa->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i][1]); | |
2cca53fd | 304 | } |
f875cb0c DH |
305 | } |
306 | if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) { | |
2cca53fd DH |
307 | for (i = 0; i < 4; i++) { |
308 | sa->gscb[i] = cpu_to_be64(cpu->env.gscb[i]); | |
309 | } | |
f875cb0c DH |
310 | } |
311 | ||
2cca53fd | 312 | cpu_physical_memory_unmap(sa, len, 1, len); |
f875cb0c DH |
313 | return 0; |
314 | } | |
d5a43964 | 315 | #endif /* CONFIG_USER_ONLY */ |
b5bd2e91 | 316 | |
90c84c56 | 317 | void s390_cpu_dump_state(CPUState *cs, FILE *f, int flags) |
b5bd2e91 TH |
318 | { |
319 | S390CPU *cpu = S390_CPU(cs); | |
320 | CPUS390XState *env = &cpu->env; | |
321 | int i; | |
322 | ||
323 | if (env->cc_op > 3) { | |
90c84c56 MA |
324 | qemu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n", |
325 | env->psw.mask, env->psw.addr, cc_name(env->cc_op)); | |
b5bd2e91 | 326 | } else { |
90c84c56 MA |
327 | qemu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n", |
328 | env->psw.mask, env->psw.addr, env->cc_op); | |
b5bd2e91 TH |
329 | } |
330 | ||
331 | for (i = 0; i < 16; i++) { | |
90c84c56 | 332 | qemu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]); |
b5bd2e91 | 333 | if ((i % 4) == 3) { |
90c84c56 | 334 | qemu_fprintf(f, "\n"); |
b5bd2e91 | 335 | } else { |
90c84c56 | 336 | qemu_fprintf(f, " "); |
b5bd2e91 TH |
337 | } |
338 | } | |
339 | ||
af6e5ea2 RH |
340 | if (flags & CPU_DUMP_FPU) { |
341 | if (s390_has_feat(S390_FEAT_VECTOR)) { | |
342 | for (i = 0; i < 32; i++) { | |
90c84c56 | 343 | qemu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64 "%c", |
4f83d7d2 | 344 | i, env->vregs[i][0], env->vregs[i][1], |
90c84c56 | 345 | i % 2 ? '\n' : ' '); |
af6e5ea2 | 346 | } |
b5bd2e91 | 347 | } else { |
af6e5ea2 | 348 | for (i = 0; i < 16; i++) { |
90c84c56 | 349 | qemu_fprintf(f, "F%02d=%016" PRIx64 "%c", |
4f83d7d2 | 350 | i, *get_freg(env, i), |
90c84c56 | 351 | (i % 4) == 3 ? '\n' : ' '); |
af6e5ea2 | 352 | } |
b5bd2e91 TH |
353 | } |
354 | } | |
355 | ||
b5bd2e91 TH |
356 | #ifndef CONFIG_USER_ONLY |
357 | for (i = 0; i < 16; i++) { | |
90c84c56 | 358 | qemu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); |
b5bd2e91 | 359 | if ((i % 4) == 3) { |
90c84c56 | 360 | qemu_fprintf(f, "\n"); |
b5bd2e91 | 361 | } else { |
90c84c56 | 362 | qemu_fprintf(f, " "); |
b5bd2e91 TH |
363 | } |
364 | } | |
365 | #endif | |
366 | ||
367 | #ifdef DEBUG_INLINE_BRANCHES | |
368 | for (i = 0; i < CC_OP_MAX; i++) { | |
90c84c56 MA |
369 | qemu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i), |
370 | inline_branch_miss[i], inline_branch_hit[i]); | |
b5bd2e91 TH |
371 | } |
372 | #endif | |
373 | ||
90c84c56 | 374 | qemu_fprintf(f, "\n"); |
b5bd2e91 | 375 | } |
c5340550 DH |
376 | |
377 | const char *cc_name(enum cc_op cc_op) | |
378 | { | |
379 | static const char * const cc_names[] = { | |
380 | [CC_OP_CONST0] = "CC_OP_CONST0", | |
381 | [CC_OP_CONST1] = "CC_OP_CONST1", | |
382 | [CC_OP_CONST2] = "CC_OP_CONST2", | |
383 | [CC_OP_CONST3] = "CC_OP_CONST3", | |
384 | [CC_OP_DYNAMIC] = "CC_OP_DYNAMIC", | |
385 | [CC_OP_STATIC] = "CC_OP_STATIC", | |
386 | [CC_OP_NZ] = "CC_OP_NZ", | |
387 | [CC_OP_LTGT_32] = "CC_OP_LTGT_32", | |
388 | [CC_OP_LTGT_64] = "CC_OP_LTGT_64", | |
389 | [CC_OP_LTUGTU_32] = "CC_OP_LTUGTU_32", | |
390 | [CC_OP_LTUGTU_64] = "CC_OP_LTUGTU_64", | |
391 | [CC_OP_LTGT0_32] = "CC_OP_LTGT0_32", | |
392 | [CC_OP_LTGT0_64] = "CC_OP_LTGT0_64", | |
393 | [CC_OP_ADD_64] = "CC_OP_ADD_64", | |
394 | [CC_OP_ADDU_64] = "CC_OP_ADDU_64", | |
395 | [CC_OP_ADDC_64] = "CC_OP_ADDC_64", | |
396 | [CC_OP_SUB_64] = "CC_OP_SUB_64", | |
397 | [CC_OP_SUBU_64] = "CC_OP_SUBU_64", | |
398 | [CC_OP_SUBB_64] = "CC_OP_SUBB_64", | |
399 | [CC_OP_ABS_64] = "CC_OP_ABS_64", | |
400 | [CC_OP_NABS_64] = "CC_OP_NABS_64", | |
401 | [CC_OP_ADD_32] = "CC_OP_ADD_32", | |
402 | [CC_OP_ADDU_32] = "CC_OP_ADDU_32", | |
403 | [CC_OP_ADDC_32] = "CC_OP_ADDC_32", | |
404 | [CC_OP_SUB_32] = "CC_OP_SUB_32", | |
405 | [CC_OP_SUBU_32] = "CC_OP_SUBU_32", | |
406 | [CC_OP_SUBB_32] = "CC_OP_SUBB_32", | |
407 | [CC_OP_ABS_32] = "CC_OP_ABS_32", | |
408 | [CC_OP_NABS_32] = "CC_OP_NABS_32", | |
409 | [CC_OP_COMP_32] = "CC_OP_COMP_32", | |
410 | [CC_OP_COMP_64] = "CC_OP_COMP_64", | |
411 | [CC_OP_TM_32] = "CC_OP_TM_32", | |
412 | [CC_OP_TM_64] = "CC_OP_TM_64", | |
413 | [CC_OP_NZ_F32] = "CC_OP_NZ_F32", | |
414 | [CC_OP_NZ_F64] = "CC_OP_NZ_F64", | |
415 | [CC_OP_NZ_F128] = "CC_OP_NZ_F128", | |
416 | [CC_OP_ICM] = "CC_OP_ICM", | |
417 | [CC_OP_SLA_32] = "CC_OP_SLA_32", | |
418 | [CC_OP_SLA_64] = "CC_OP_SLA_64", | |
419 | [CC_OP_FLOGR] = "CC_OP_FLOGR", | |
6d930332 | 420 | [CC_OP_LCBB] = "CC_OP_LCBB", |
ff825c6d | 421 | [CC_OP_VC] = "CC_OP_VC", |
c5340550 DH |
422 | }; |
423 | ||
424 | return cc_names[cc_op]; | |
425 | } |