]>
Commit | Line | Data |
---|---|---|
29e4bcb2 AF |
1 | /* |
2 | * QEMU S/390 CPU | |
3 | * | |
1ac1a749 AF |
4 | * Copyright (c) 2009 Ulrich Hecht |
5 | * Copyright (c) 2011 Alexander Graf | |
29e4bcb2 | 6 | * Copyright (c) 2012 SUSE LINUX Products GmbH |
70bada03 | 7 | * Copyright (c) 2012 IBM Corp. |
29e4bcb2 | 8 | * |
44699e1c TH |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
29e4bcb2 | 13 | * |
44699e1c | 14 | * This program is distributed in the hope that it will be useful, |
29e4bcb2 AF |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
44699e1c | 17 | * General Public License for more details. |
29e4bcb2 | 18 | * |
44699e1c TH |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
29e4bcb2 AF |
21 | */ |
22 | ||
9615495a | 23 | #include "qemu/osdep.h" |
da34e65c | 24 | #include "qapi/error.h" |
564b863d | 25 | #include "cpu.h" |
b6b47223 | 26 | #include "s390x-internal.h" |
67043607 | 27 | #include "kvm/kvm_s390x.h" |
f16bbb9b | 28 | #include "sysemu/kvm.h" |
0b8fa32f | 29 | #include "qemu/module.h" |
eb24f7c6 | 30 | #include "trace.h" |
8ac25c84 | 31 | #include "qapi/qapi-types-machine.h" |
4ada99ad | 32 | #include "sysemu/hw_accel.h" |
ca5c1457 | 33 | #include "hw/qdev-properties.h" |
5f8ab000 | 34 | #include "fpu/softfloat-helpers.h" |
3d562845 | 35 | #include "disas/capstone.h" |
98be64d2 | 36 | #include "sysemu/tcg.h" |
057733f2 PMD |
37 | #ifndef CONFIG_USER_ONLY |
38 | #include "sysemu/reset.h" | |
39 | #endif | |
904e5fd5 | 40 | |
70bada03 JF |
41 | #define CR0_RESET 0xE0UL |
42 | #define CR14_RESET 0xC2000000UL; | |
43 | ||
199c42a6 IL |
44 | #ifndef CONFIG_USER_ONLY |
45 | static bool is_early_exception_psw(uint64_t mask, uint64_t addr) | |
46 | { | |
47 | if (mask & PSW_MASK_RESERVED) { | |
48 | return true; | |
49 | } | |
50 | ||
51 | switch (mask & (PSW_MASK_32 | PSW_MASK_64)) { | |
52 | case 0: | |
53 | return addr & ~0xffffffULL; | |
54 | case PSW_MASK_32: | |
55 | return addr & ~0x7fffffffULL; | |
56 | case PSW_MASK_32 | PSW_MASK_64: | |
57 | return false; | |
58 | default: /* PSW_MASK_64 */ | |
59 | return true; | |
60 | } | |
61 | } | |
62 | #endif | |
63 | ||
98be64d2 CYC |
64 | void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) |
65 | { | |
66 | #ifndef CONFIG_USER_ONLY | |
67 | uint64_t old_mask = env->psw.mask; | |
68 | #endif | |
69 | ||
70 | env->psw.addr = addr; | |
71 | env->psw.mask = mask; | |
72 | ||
73 | /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ | |
74 | if (!tcg_enabled()) { | |
75 | return; | |
76 | } | |
77 | env->cc_op = (mask >> 44) & 3; | |
78 | ||
79 | #ifndef CONFIG_USER_ONLY | |
199c42a6 IL |
80 | if (is_early_exception_psw(mask, addr)) { |
81 | env->int_pgm_ilen = 0; | |
82 | trigger_pgm_exception(env, PGM_SPECIFICATION); | |
83 | return; | |
84 | } | |
85 | ||
98be64d2 CYC |
86 | if ((old_mask ^ mask) & PSW_MASK_PER) { |
87 | s390_cpu_recompute_watchpoints(env_cpu(env)); | |
88 | } | |
89 | ||
90 | if (mask & PSW_MASK_WAIT) { | |
91 | s390_handle_wait(env_archcpu(env)); | |
92 | } | |
93 | #endif | |
94 | } | |
95 | ||
96 | uint64_t s390_cpu_get_psw_mask(CPUS390XState *env) | |
97 | { | |
98 | uint64_t r = env->psw.mask; | |
99 | ||
100 | if (tcg_enabled()) { | |
101 | uint64_t cc = calc_cc(env, env->cc_op, env->cc_src, | |
102 | env->cc_dst, env->cc_vr); | |
103 | ||
104 | assert(cc <= 3); | |
105 | r &= ~PSW_MASK_CC; | |
106 | r |= cc << 44; | |
107 | } | |
108 | ||
109 | return r; | |
110 | } | |
111 | ||
f45748f1 AF |
112 | static void s390_cpu_set_pc(CPUState *cs, vaddr value) |
113 | { | |
114 | S390CPU *cpu = S390_CPU(cs); | |
115 | ||
116 | cpu->env.psw.addr = value; | |
117 | } | |
118 | ||
e4fdf9df RH |
119 | static vaddr s390_cpu_get_pc(CPUState *cs) |
120 | { | |
121 | S390CPU *cpu = S390_CPU(cs); | |
122 | ||
123 | return cpu->env.psw.addr; | |
124 | } | |
125 | ||
8c2e1b00 AF |
126 | static bool s390_cpu_has_work(CPUState *cs) |
127 | { | |
128 | S390CPU *cpu = S390_CPU(cs); | |
8c2e1b00 | 129 | |
4beab671 | 130 | /* STOPPED cpus can never wake up */ |
9d0306df VM |
131 | if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && |
132 | s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { | |
4beab671 DH |
133 | return false; |
134 | } | |
135 | ||
8417f904 DH |
136 | if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { |
137 | return false; | |
138 | } | |
139 | ||
140 | return s390_cpu_has_int(cpu); | |
8c2e1b00 AF |
141 | } |
142 | ||
5503da4a TH |
143 | static void s390_query_cpu_fast(CPUState *cpu, CpuInfoFast *value) |
144 | { | |
145 | S390CPU *s390_cpu = S390_CPU(cpu); | |
146 | ||
147 | value->u.s390x.cpu_state = s390_cpu->env.cpu_state; | |
148 | } | |
149 | ||
eac4f827 JF |
150 | /* S390CPUClass::reset() */ |
151 | static void s390_cpu_reset(CPUState *s, cpu_reset_type type) | |
29e4bcb2 AF |
152 | { |
153 | S390CPU *cpu = S390_CPU(s); | |
154 | S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); | |
155 | CPUS390XState *env = &cpu->env; | |
781c67ca | 156 | DeviceState *dev = DEVICE(s); |
29e4bcb2 | 157 | |
781c67ca | 158 | scc->parent_reset(dev); |
18ff9494 | 159 | cpu->env.sigp_order = 0; |
9d0306df | 160 | s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); |
eac4f827 JF |
161 | |
162 | switch (type) { | |
eb8adcc3 JF |
163 | case S390_CPU_RESET_CLEAR: |
164 | memset(env, 0, offsetof(CPUS390XState, start_initial_reset_fields)); | |
165 | /* fall through */ | |
81b92223 JF |
166 | case S390_CPU_RESET_INITIAL: |
167 | /* initial reset does not clear everything! */ | |
168 | memset(&env->start_initial_reset_fields, 0, | |
e893baee | 169 | offsetof(CPUS390XState, start_normal_reset_fields) - |
81b92223 JF |
170 | offsetof(CPUS390XState, start_initial_reset_fields)); |
171 | ||
172 | /* architectured initial value for Breaking-Event-Address register */ | |
173 | env->gbea = 1; | |
174 | ||
175 | /* architectured initial values for CR 0 and 14 */ | |
176 | env->cregs[0] = CR0_RESET; | |
177 | env->cregs[14] = CR14_RESET; | |
178 | ||
eb8adcc3 JF |
179 | #if defined(CONFIG_USER_ONLY) |
180 | /* user mode should always be allowed to use the full FPU */ | |
181 | env->cregs[0] |= CR0_AFP; | |
182 | if (s390_has_feat(S390_FEAT_VECTOR)) { | |
183 | env->cregs[0] |= CR0_VECTOR; | |
184 | } | |
185 | #endif | |
186 | ||
81b92223 JF |
187 | /* tininess for underflow is detected before rounding */ |
188 | set_float_detect_tininess(float_tininess_before_rounding, | |
189 | &env->fpu_status); | |
190 | /* fall through */ | |
eac4f827 | 191 | case S390_CPU_RESET_NORMAL: |
e893baee JF |
192 | env->psw.mask &= ~PSW_MASK_RI; |
193 | memset(&env->start_normal_reset_fields, 0, | |
194 | offsetof(CPUS390XState, end_reset_fields) - | |
195 | offsetof(CPUS390XState, start_normal_reset_fields)); | |
196 | ||
eac4f827 JF |
197 | env->pfault_token = -1UL; |
198 | env->bpbc = false; | |
199 | break; | |
200 | default: | |
201 | g_assert_not_reached(); | |
202 | } | |
4a33565f | 203 | |
49f5c9e9 | 204 | /* Reset state inside the kernel that we cannot access yet from QEMU. */ |
b91a0394 JF |
205 | if (kvm_enabled()) { |
206 | switch (type) { | |
207 | case S390_CPU_RESET_CLEAR: | |
208 | kvm_s390_reset_vcpu_clear(cpu); | |
209 | break; | |
210 | case S390_CPU_RESET_INITIAL: | |
211 | kvm_s390_reset_vcpu_initial(cpu); | |
212 | break; | |
213 | case S390_CPU_RESET_NORMAL: | |
214 | kvm_s390_reset_vcpu_normal(cpu); | |
215 | break; | |
216 | } | |
49f5c9e9 | 217 | } |
f5ae2a4f CB |
218 | } |
219 | ||
dbad6b74 PC |
220 | static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) |
221 | { | |
222 | info->mach = bfd_mach_s390_64; | |
3d562845 RH |
223 | info->cap_arch = CS_ARCH_SYSZ; |
224 | info->cap_insn_unit = 2; | |
225 | info->cap_insn_split = 6; | |
dbad6b74 PC |
226 | } |
227 | ||
1f136632 AF |
228 | static void s390_cpu_realizefn(DeviceState *dev, Error **errp) |
229 | { | |
14a10fc3 | 230 | CPUState *cs = CPU(dev); |
1f136632 | 231 | S390CPUClass *scc = S390_CPU_GET_CLASS(dev); |
c6644fc8 MR |
232 | Error *err = NULL; |
233 | ||
41868f84 DH |
234 | /* the model has to be realized before qemu_init_vcpu() due to kvm */ |
235 | s390_realize_cpu_model(cs, &err); | |
236 | if (err) { | |
237 | goto out; | |
238 | } | |
239 | ||
96b1a8bb | 240 | #if !defined(CONFIG_USER_ONLY) |
72ffb631 | 241 | if (!s390_cpu_realize_sysemu(dev, &err)) { |
96b1a8bb MR |
242 | goto out; |
243 | } | |
1e70ba24 DH |
244 | #endif |
245 | ||
ce5b1bbf | 246 | cpu_exec_realizefn(cs, &err); |
c6644fc8 | 247 | if (err != NULL) { |
96b1a8bb | 248 | goto out; |
c6644fc8 | 249 | } |
1f136632 | 250 | |
c6644fc8 | 251 | #if !defined(CONFIG_USER_ONLY) |
72ffb631 | 252 | qemu_register_reset(s390_cpu_machine_reset_cb, S390_CPU(dev)); |
c6644fc8 | 253 | #endif |
73d510c9 | 254 | s390_cpu_gdb_init(cs); |
14a10fc3 | 255 | qemu_init_vcpu(cs); |
d66b43c8 DH |
256 | |
257 | /* | |
258 | * KVM requires the initial CPU reset ioctl to be executed on the target | |
259 | * CPU thread. CPU hotplug under single-threaded TCG will not work with | |
260 | * run_on_cpu(), as run_on_cpu() will not work properly if called while | |
261 | * the main thread is already running but the CPU hasn't been realized. | |
262 | */ | |
263 | if (kvm_enabled()) { | |
264 | run_on_cpu(cs, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); | |
265 | } else { | |
266 | cpu_reset(cs); | |
267 | } | |
1f136632 | 268 | |
96b1a8bb | 269 | scc->parent_realize(dev, &err); |
96b1a8bb MR |
270 | out: |
271 | error_propagate(errp, err); | |
272 | } | |
273 | ||
8f22e0df AF |
274 | static void s390_cpu_initfn(Object *obj) |
275 | { | |
c05efcb1 | 276 | CPUState *cs = CPU(obj); |
8f22e0df | 277 | |
ef3027af | 278 | cs->exception_index = EXCP_HLT; |
8f22e0df | 279 | |
d5627ce8 | 280 | #if !defined(CONFIG_USER_ONLY) |
72ffb631 | 281 | s390_cpu_init_sysemu(obj); |
d5627ce8 AF |
282 | #endif |
283 | } | |
284 | ||
a6506838 | 285 | static const gchar *s390_gdb_arch_name(CPUState *cs) |
b3820e6c | 286 | { |
a6506838 | 287 | return "s390:64-bit"; |
b3820e6c DH |
288 | } |
289 | ||
ca5c1457 | 290 | static Property s390x_cpu_properties[] = { |
1e70ba24 | 291 | #if !defined(CONFIG_USER_ONLY) |
ca5c1457 | 292 | DEFINE_PROP_UINT32("core-id", S390CPU, env.core_id, 0), |
1e70ba24 | 293 | #endif |
ca5c1457 DH |
294 | DEFINE_PROP_END_OF_LIST() |
295 | }; | |
296 | ||
781c67ca | 297 | static void s390_cpu_reset_full(DeviceState *dev) |
eb8adcc3 | 298 | { |
781c67ca | 299 | CPUState *s = CPU(dev); |
eb8adcc3 JF |
300 | return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); |
301 | } | |
302 | ||
78271684 CF |
303 | #ifdef CONFIG_TCG |
304 | #include "hw/core/tcg-cpu-ops.h" | |
305 | ||
11906557 | 306 | static const struct TCGCPUOps s390_tcg_ops = { |
78271684 | 307 | .initialize = s390x_translate_init, |
3479783b | 308 | .restore_state_to_opc = s390x_restore_state_to_opc, |
78271684 | 309 | |
c8e7fef1 RH |
310 | #ifdef CONFIG_USER_ONLY |
311 | .record_sigsegv = s390_cpu_record_sigsegv, | |
5bcbf356 | 312 | .record_sigbus = s390_cpu_record_sigbus, |
c8e7fef1 RH |
313 | #else |
314 | .tlb_fill = s390_cpu_tlb_fill, | |
78271684 CF |
315 | .cpu_exec_interrupt = s390_cpu_exec_interrupt, |
316 | .do_interrupt = s390_cpu_do_interrupt, | |
317 | .debug_excp_handler = s390x_cpu_debug_excp_handler, | |
318 | .do_unaligned_access = s390x_cpu_do_unaligned_access, | |
319 | #endif /* !CONFIG_USER_ONLY */ | |
320 | }; | |
321 | #endif /* CONFIG_TCG */ | |
322 | ||
29e4bcb2 AF |
323 | static void s390_cpu_class_init(ObjectClass *oc, void *data) |
324 | { | |
325 | S390CPUClass *scc = S390_CPU_CLASS(oc); | |
326 | CPUClass *cc = CPU_CLASS(scc); | |
c7396bbb | 327 | DeviceClass *dc = DEVICE_CLASS(oc); |
29e4bcb2 | 328 | |
bf853881 PMD |
329 | device_class_set_parent_realize(dc, s390_cpu_realizefn, |
330 | &scc->parent_realize); | |
4f67d30b | 331 | device_class_set_props(dc, s390x_cpu_properties); |
0347ab84 | 332 | dc->user_creatable = true; |
1f136632 | 333 | |
781c67ca | 334 | device_class_set_parent_reset(dc, s390_cpu_reset_full, &scc->parent_reset); |
72ffb631 | 335 | |
eac4f827 | 336 | scc->reset = s390_cpu_reset; |
41868f84 | 337 | cc->class_by_name = s390_cpu_class_by_name, |
8c2e1b00 | 338 | cc->has_work = s390_cpu_has_work; |
878096ee | 339 | cc->dump_state = s390_cpu_dump_state; |
5503da4a | 340 | cc->query_cpu_fast = s390_query_cpu_fast; |
f45748f1 | 341 | cc->set_pc = s390_cpu_set_pc; |
e4fdf9df | 342 | cc->get_pc = s390_cpu_get_pc; |
5b50e790 AF |
343 | cc->gdb_read_register = s390_cpu_gdb_read_register; |
344 | cc->gdb_write_register = s390_cpu_gdb_write_register; | |
82851985 | 345 | #ifndef CONFIG_USER_ONLY |
72ffb631 | 346 | s390_cpu_class_init_sysemu(cc); |
00b941e5 | 347 | #endif |
dbad6b74 | 348 | cc->disas_set_info = s390_cpu_disas_set_info; |
73d510c9 DH |
349 | cc->gdb_num_core_regs = S390_NUM_CORE_REGS; |
350 | cc->gdb_core_xml_file = "s390x-core64.xml"; | |
b3820e6c | 351 | cc->gdb_arch_name = s390_gdb_arch_name; |
4c315c27 | 352 | |
6efadc90 | 353 | s390_cpu_model_class_register_props(oc); |
78271684 CF |
354 | |
355 | #ifdef CONFIG_TCG | |
356 | cc->tcg_ops = &s390_tcg_ops; | |
357 | #endif /* CONFIG_TCG */ | |
29e4bcb2 AF |
358 | } |
359 | ||
360 | static const TypeInfo s390_cpu_type_info = { | |
361 | .name = TYPE_S390_CPU, | |
362 | .parent = TYPE_CPU, | |
363 | .instance_size = sizeof(S390CPU), | |
f62192a2 | 364 | .instance_align = __alignof__(S390CPU), |
8f22e0df | 365 | .instance_init = s390_cpu_initfn, |
72ffb631 CYC |
366 | |
367 | #ifndef CONFIG_USER_ONLY | |
d5627ce8 | 368 | .instance_finalize = s390_cpu_finalize, |
72ffb631 CYC |
369 | #endif /* !CONFIG_USER_ONLY */ |
370 | ||
41868f84 | 371 | .abstract = true, |
29e4bcb2 AF |
372 | .class_size = sizeof(S390CPUClass), |
373 | .class_init = s390_cpu_class_init, | |
374 | }; | |
375 | ||
376 | static void s390_cpu_register_types(void) | |
377 | { | |
378 | type_register_static(&s390_cpu_type_info); | |
379 | } | |
380 | ||
381 | type_init(s390_cpu_register_types) |