]> git.proxmox.com Git - mirror_qemu.git/blob - target/s390x/sigp.c
s390x/kvm: factor out SIGP code into sigp.c
[mirror_qemu.git] / target / s390x / sigp.c
1 /*
2 * s390x SIGP instruction handling
3 *
4 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
5 * Copyright IBM Corp. 2012
6 *
7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 * See the COPYING file in the top-level directory.
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu-common.h"
13 #include "cpu.h"
14 #include "internal.h"
15 #include "sysemu/hw_accel.h"
16 #include "exec/address-spaces.h"
17 #include "sysemu/sysemu.h"
18 #include "trace.h"
19
20 QemuMutex qemu_sigp_mutex;
21
22 typedef struct SigpInfo {
23 uint64_t param;
24 int cc;
25 uint64_t *status_reg;
26 } SigpInfo;
27
28 static void set_sigp_status(SigpInfo *si, uint64_t status)
29 {
30 *si->status_reg &= 0xffffffff00000000ULL;
31 *si->status_reg |= status;
32 si->cc = SIGP_CC_STATUS_STORED;
33 }
34
35 static void sigp_start(CPUState *cs, run_on_cpu_data arg)
36 {
37 S390CPU *cpu = S390_CPU(cs);
38 SigpInfo *si = arg.host_ptr;
39
40 if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
41 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
42 return;
43 }
44
45 s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
46 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
47 }
48
49 static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
50 {
51 S390CPU *cpu = S390_CPU(cs);
52 SigpInfo *si = arg.host_ptr;
53
54 if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
55 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
56 return;
57 }
58
59 /* disabled wait - sleeping in user space */
60 if (cs->halted) {
61 s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
62 } else {
63 /* execute the stop function */
64 cpu->env.sigp_order = SIGP_STOP;
65 cpu_inject_stop(cpu);
66 }
67 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
68 }
69
70 static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
71 {
72 S390CPU *cpu = S390_CPU(cs);
73 SigpInfo *si = arg.host_ptr;
74
75 /* disabled wait - sleeping in user space */
76 if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
77 s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
78 }
79
80 switch (s390_cpu_get_state(cpu)) {
81 case CPU_STATE_OPERATING:
82 cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
83 cpu_inject_stop(cpu);
84 /* store will be performed when handling the stop intercept */
85 break;
86 case CPU_STATE_STOPPED:
87 /* already stopped, just store the status */
88 cpu_synchronize_state(cs);
89 s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
90 break;
91 }
92 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
93 }
94
95 static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
96 {
97 S390CPU *cpu = S390_CPU(cs);
98 SigpInfo *si = arg.host_ptr;
99 uint32_t address = si->param & 0x7ffffe00u;
100
101 /* cpu has to be stopped */
102 if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
103 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
104 return;
105 }
106
107 cpu_synchronize_state(cs);
108
109 if (s390_store_status(cpu, address, false)) {
110 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
111 return;
112 }
113 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
114 }
115
116 #define ADTL_SAVE_LC_MASK 0xfUL
117 static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
118 {
119 S390CPU *cpu = S390_CPU(cs);
120 SigpInfo *si = arg.host_ptr;
121 uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
122 hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
123 hwaddr len = 1UL << (lc ? lc : 10);
124
125 if (!s390_has_feat(S390_FEAT_VECTOR) &&
126 !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
127 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
128 return;
129 }
130
131 /* cpu has to be stopped */
132 if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
133 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
134 return;
135 }
136
137 /* address must be aligned to length */
138 if (addr & (len - 1)) {
139 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
140 return;
141 }
142
143 /* no GS: only lc == 0 is valid */
144 if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
145 lc != 0) {
146 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
147 return;
148 }
149
150 /* GS: 0, 10, 11, 12 are valid */
151 if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
152 lc != 0 &&
153 lc != 10 &&
154 lc != 11 &&
155 lc != 12) {
156 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
157 return;
158 }
159
160 cpu_synchronize_state(cs);
161
162 if (s390_store_adtl_status(cpu, addr, len)) {
163 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
164 return;
165 }
166 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
167 }
168
169 static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
170 {
171 S390CPU *cpu = S390_CPU(cs);
172 SigpInfo *si = arg.host_ptr;
173
174 switch (s390_cpu_get_state(cpu)) {
175 case CPU_STATE_STOPPED:
176 /* the restart irq has to be delivered prior to any other pending irq */
177 cpu_synchronize_state(cs);
178 do_restart_interrupt(&cpu->env);
179 s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
180 break;
181 case CPU_STATE_OPERATING:
182 cpu_inject_restart(cpu);
183 break;
184 }
185 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
186 }
187
188 static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
189 {
190 S390CPU *cpu = S390_CPU(cs);
191 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
192 SigpInfo *si = arg.host_ptr;
193
194 cpu_synchronize_state(cs);
195 scc->initial_cpu_reset(cs);
196 cpu_synchronize_post_reset(cs);
197 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
198 }
199
200 static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
201 {
202 S390CPU *cpu = S390_CPU(cs);
203 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
204 SigpInfo *si = arg.host_ptr;
205
206 cpu_synchronize_state(cs);
207 scc->cpu_reset(cs);
208 cpu_synchronize_post_reset(cs);
209 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
210 }
211
212 static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
213 {
214 S390CPU *cpu = S390_CPU(cs);
215 SigpInfo *si = arg.host_ptr;
216 uint32_t addr = si->param & 0x7fffe000u;
217
218 cpu_synchronize_state(cs);
219
220 if (!address_space_access_valid(&address_space_memory, addr,
221 sizeof(struct LowCore), false)) {
222 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
223 return;
224 }
225
226 /* cpu has to be stopped */
227 if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
228 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
229 return;
230 }
231
232 cpu->env.psa = addr;
233 cpu_synchronize_post_init(cs);
234 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
235 }
236
237 static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
238 uint64_t param, uint64_t *status_reg)
239 {
240 SigpInfo si = {
241 .param = param,
242 .status_reg = status_reg,
243 };
244
245 /* cpu available? */
246 if (dst_cpu == NULL) {
247 return SIGP_CC_NOT_OPERATIONAL;
248 }
249
250 /* only resets can break pending orders */
251 if (dst_cpu->env.sigp_order != 0 &&
252 order != SIGP_CPU_RESET &&
253 order != SIGP_INITIAL_CPU_RESET) {
254 return SIGP_CC_BUSY;
255 }
256
257 switch (order) {
258 case SIGP_START:
259 run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
260 break;
261 case SIGP_STOP:
262 run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
263 break;
264 case SIGP_RESTART:
265 run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
266 break;
267 case SIGP_STOP_STORE_STATUS:
268 run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
269 break;
270 case SIGP_STORE_STATUS_ADDR:
271 run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
272 break;
273 case SIGP_STORE_ADTL_STATUS:
274 run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
275 break;
276 case SIGP_SET_PREFIX:
277 run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
278 break;
279 case SIGP_INITIAL_CPU_RESET:
280 run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
281 break;
282 case SIGP_CPU_RESET:
283 run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
284 break;
285 default:
286 set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
287 }
288
289 return si.cc;
290 }
291
292 static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
293 uint64_t *status_reg)
294 {
295 CPUState *cur_cs;
296 S390CPU *cur_cpu;
297 bool all_stopped = true;
298
299 CPU_FOREACH(cur_cs) {
300 cur_cpu = S390_CPU(cur_cs);
301
302 if (cur_cpu == cpu) {
303 continue;
304 }
305 if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) {
306 all_stopped = false;
307 }
308 }
309
310 *status_reg &= 0xffffffff00000000ULL;
311
312 /* Reject set arch order, with czam we're always in z/Arch mode. */
313 *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER :
314 SIGP_STAT_INCORRECT_STATE);
315 return SIGP_CC_STATUS_STORED;
316 }
317
318 int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
319 {
320 uint64_t *status_reg = &env->regs[r1];
321 uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
322 S390CPU *cpu = s390_env_get_cpu(env);
323 S390CPU *dst_cpu = NULL;
324 int ret;
325
326 if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
327 ret = SIGP_CC_BUSY;
328 goto out;
329 }
330
331 switch (order) {
332 case SIGP_SET_ARCH:
333 ret = sigp_set_architecture(cpu, param, status_reg);
334 break;
335 default:
336 /* all other sigp orders target a single vcpu */
337 dst_cpu = s390_cpu_addr2state(env->regs[r3]);
338 ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg);
339 }
340 qemu_mutex_unlock(&qemu_sigp_mutex);
341
342 out:
343 trace_sigp_finished(order, CPU(cpu)->cpu_index,
344 dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
345 g_assert(ret >= 0);
346
347 return ret;
348 }
349
350 int s390_cpu_restart(S390CPU *cpu)
351 {
352 SigpInfo si = {};
353
354 if (tcg_enabled()) {
355 /* FIXME TCG */
356 return -ENOSYS;
357 }
358
359 run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
360 return 0;
361 }
362
363 void s390_init_sigp(void)
364 {
365 qemu_mutex_init(&qemu_sigp_mutex);
366 }