target-s390: Convert STCK
[sdk/emulator/qemu.git] / target-s390x / misc_helper.c
1 /*
2  *  S/390 misc helper routines
3  *
4  *  Copyright (c) 2009 Ulrich Hecht
5  *  Copyright (c) 2009 Alexander Graf
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
10  * version 2 of the License, or (at your option) any later version.
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
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "cpu.h"
22 #include "exec/memory.h"
23 #include "qemu/host-utils.h"
24 #include "helper.h"
25 #include <string.h>
26 #include "sysemu/kvm.h"
27 #include "qemu/timer.h"
28 #ifdef CONFIG_KVM
29 #include <linux/kvm.h>
30 #endif
31
32 #if !defined(CONFIG_USER_ONLY)
33 #include "exec/softmmu_exec.h"
34 #include "sysemu/sysemu.h"
35 #endif
36
37 /* #define DEBUG_HELPER */
38 #ifdef DEBUG_HELPER
39 #define HELPER_LOG(x...) qemu_log(x)
40 #else
41 #define HELPER_LOG(x...)
42 #endif
43
44 /* Raise an exception dynamically from a helper function.  */
45 void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
46                                      uintptr_t retaddr)
47 {
48     int t;
49
50     env->exception_index = EXCP_PGM;
51     env->int_pgm_code = excp;
52
53     /* Use the (ultimate) callers address to find the insn that trapped.  */
54     cpu_restore_state(env, retaddr);
55
56     /* Advance past the insn.  */
57     t = cpu_ldub_code(env, env->psw.addr);
58     env->int_pgm_ilen = t = get_ilen(t);
59     env->psw.addr += 2 * t;
60
61     cpu_loop_exit(env);
62 }
63
64 /* Raise an exception statically from a TB.  */
65 void HELPER(exception)(CPUS390XState *env, uint32_t excp)
66 {
67     HELPER_LOG("%s: exception %d\n", __func__, excp);
68     env->exception_index = excp;
69     cpu_loop_exit(env);
70 }
71
72 #ifndef CONFIG_USER_ONLY
73 void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
74 {
75     qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
76                   env->psw.addr);
77
78     if (kvm_enabled()) {
79 #ifdef CONFIG_KVM
80         kvm_s390_interrupt(s390_env_get_cpu(env), KVM_S390_PROGRAM_INT, code);
81 #endif
82     } else {
83         env->int_pgm_code = code;
84         env->int_pgm_ilen = ilen;
85         env->exception_index = EXCP_PGM;
86         cpu_loop_exit(env);
87     }
88 }
89
90 /* SCLP service call */
91 uint32_t HELPER(servc)(CPUS390XState *env, uint32_t r1, uint64_t r2)
92 {
93     int r;
94
95     r = sclp_service_call(r1, r2);
96     if (r < 0) {
97         program_interrupt(env, -r, 4);
98         return 0;
99     }
100     return r;
101 }
102
103 /* DIAG */
104 uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
105                       uint64_t code)
106 {
107     uint64_t r;
108
109     switch (num) {
110     case 0x500:
111         /* KVM hypercall */
112         r = s390_virtio_hypercall(env, mem, code);
113         break;
114     case 0x44:
115         /* yield */
116         r = 0;
117         break;
118     case 0x308:
119         /* ipl */
120         r = 0;
121         break;
122     default:
123         r = -1;
124         break;
125     }
126
127     if (r) {
128         program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
129     }
130
131     return r;
132 }
133
134 /* Set Prefix */
135 void HELPER(spx)(CPUS390XState *env, uint64_t a1)
136 {
137     uint32_t prefix;
138
139     prefix = cpu_ldl_data(env, a1);
140     env->psa = prefix & 0xfffff000;
141     qemu_log("prefix: %#x\n", prefix);
142     tlb_flush_page(env, 0);
143     tlb_flush_page(env, TARGET_PAGE_SIZE);
144 }
145
146 static inline uint64_t clock_value(CPUS390XState *env)
147 {
148     uint64_t time;
149
150     time = env->tod_offset +
151         time2tod(qemu_get_clock_ns(vm_clock) - env->tod_basetime);
152
153     return time;
154 }
155
156 /* Store Clock */
157 uint64_t HELPER(stck)(CPUS390XState *env)
158 {
159     return clock_value(env);
160 }
161
162 /* Store Clock Extended */
163 uint32_t HELPER(stcke)(CPUS390XState *env, uint64_t a1)
164 {
165     cpu_stb_data(env, a1, 0);
166     /* basically the same value as stck */
167     cpu_stq_data(env, a1 + 1, clock_value(env) | env->cpu_num);
168     /* more fine grained than stck */
169     cpu_stq_data(env, a1 + 9, 0);
170     /* XXX programmable fields */
171     cpu_stw_data(env, a1 + 17, 0);
172
173     return 0;
174 }
175
176 /* Set Clock Comparator */
177 void HELPER(sckc)(CPUS390XState *env, uint64_t a1)
178 {
179     uint64_t time = cpu_ldq_data(env, a1);
180
181     if (time == -1ULL) {
182         return;
183     }
184
185     /* difference between now and then */
186     time -= clock_value(env);
187     /* nanoseconds */
188     time = (time * 125) >> 9;
189
190     qemu_mod_timer(env->tod_timer, qemu_get_clock_ns(vm_clock) + time);
191 }
192
193 /* Store Clock Comparator */
194 void HELPER(stckc)(CPUS390XState *env, uint64_t a1)
195 {
196     /* XXX implement */
197     cpu_stq_data(env, a1, 0);
198 }
199
200 /* Set CPU Timer */
201 void HELPER(spt)(CPUS390XState *env, uint64_t a1)
202 {
203     uint64_t time = cpu_ldq_data(env, a1);
204
205     if (time == -1ULL) {
206         return;
207     }
208
209     /* nanoseconds */
210     time = (time * 125) >> 9;
211
212     qemu_mod_timer(env->cpu_timer, qemu_get_clock_ns(vm_clock) + time);
213 }
214
215 /* Store CPU Timer */
216 void HELPER(stpt)(CPUS390XState *env, uint64_t a1)
217 {
218     /* XXX implement */
219     cpu_stq_data(env, a1, 0);
220 }
221
222 /* Store System Information */
223 uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t r0,
224                       uint32_t r1)
225 {
226     int cc = 0;
227     int sel1, sel2;
228
229     if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 &&
230         ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) {
231         /* valid function code, invalid reserved bits */
232         program_interrupt(env, PGM_SPECIFICATION, 2);
233     }
234
235     sel1 = r0 & STSI_R0_SEL1_MASK;
236     sel2 = r1 & STSI_R1_SEL2_MASK;
237
238     /* XXX: spec exception if sysib is not 4k-aligned */
239
240     switch (r0 & STSI_LEVEL_MASK) {
241     case STSI_LEVEL_1:
242         if ((sel1 == 1) && (sel2 == 1)) {
243             /* Basic Machine Configuration */
244             struct sysib_111 sysib;
245
246             memset(&sysib, 0, sizeof(sysib));
247             ebcdic_put(sysib.manuf, "QEMU            ", 16);
248             /* same as machine type number in STORE CPU ID */
249             ebcdic_put(sysib.type, "QEMU", 4);
250             /* same as model number in STORE CPU ID */
251             ebcdic_put(sysib.model, "QEMU            ", 16);
252             ebcdic_put(sysib.sequence, "QEMU            ", 16);
253             ebcdic_put(sysib.plant, "QEMU", 4);
254             cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
255         } else if ((sel1 == 2) && (sel2 == 1)) {
256             /* Basic Machine CPU */
257             struct sysib_121 sysib;
258
259             memset(&sysib, 0, sizeof(sysib));
260             /* XXX make different for different CPUs? */
261             ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
262             ebcdic_put(sysib.plant, "QEMU", 4);
263             stw_p(&sysib.cpu_addr, env->cpu_num);
264             cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
265         } else if ((sel1 == 2) && (sel2 == 2)) {
266             /* Basic Machine CPUs */
267             struct sysib_122 sysib;
268
269             memset(&sysib, 0, sizeof(sysib));
270             stl_p(&sysib.capability, 0x443afc29);
271             /* XXX change when SMP comes */
272             stw_p(&sysib.total_cpus, 1);
273             stw_p(&sysib.active_cpus, 1);
274             stw_p(&sysib.standby_cpus, 0);
275             stw_p(&sysib.reserved_cpus, 0);
276             cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
277         } else {
278             cc = 3;
279         }
280         break;
281     case STSI_LEVEL_2:
282         {
283             if ((sel1 == 2) && (sel2 == 1)) {
284                 /* LPAR CPU */
285                 struct sysib_221 sysib;
286
287                 memset(&sysib, 0, sizeof(sysib));
288                 /* XXX make different for different CPUs? */
289                 ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
290                 ebcdic_put(sysib.plant, "QEMU", 4);
291                 stw_p(&sysib.cpu_addr, env->cpu_num);
292                 stw_p(&sysib.cpu_id, 0);
293                 cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
294             } else if ((sel1 == 2) && (sel2 == 2)) {
295                 /* LPAR CPUs */
296                 struct sysib_222 sysib;
297
298                 memset(&sysib, 0, sizeof(sysib));
299                 stw_p(&sysib.lpar_num, 0);
300                 sysib.lcpuc = 0;
301                 /* XXX change when SMP comes */
302                 stw_p(&sysib.total_cpus, 1);
303                 stw_p(&sysib.conf_cpus, 1);
304                 stw_p(&sysib.standby_cpus, 0);
305                 stw_p(&sysib.reserved_cpus, 0);
306                 ebcdic_put(sysib.name, "QEMU    ", 8);
307                 stl_p(&sysib.caf, 1000);
308                 stw_p(&sysib.dedicated_cpus, 0);
309                 stw_p(&sysib.shared_cpus, 0);
310                 cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
311             } else {
312                 cc = 3;
313             }
314             break;
315         }
316     case STSI_LEVEL_3:
317         {
318             if ((sel1 == 2) && (sel2 == 2)) {
319                 /* VM CPUs */
320                 struct sysib_322 sysib;
321
322                 memset(&sysib, 0, sizeof(sysib));
323                 sysib.count = 1;
324                 /* XXX change when SMP comes */
325                 stw_p(&sysib.vm[0].total_cpus, 1);
326                 stw_p(&sysib.vm[0].conf_cpus, 1);
327                 stw_p(&sysib.vm[0].standby_cpus, 0);
328                 stw_p(&sysib.vm[0].reserved_cpus, 0);
329                 ebcdic_put(sysib.vm[0].name, "KVMguest", 8);
330                 stl_p(&sysib.vm[0].caf, 1000);
331                 ebcdic_put(sysib.vm[0].cpi, "KVM/Linux       ", 16);
332                 cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
333             } else {
334                 cc = 3;
335             }
336             break;
337         }
338     case STSI_LEVEL_CURRENT:
339         env->regs[0] = STSI_LEVEL_3;
340         break;
341     default:
342         cc = 3;
343         break;
344     }
345
346     return cc;
347 }
348
349 uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1,
350                       uint64_t cpu_addr)
351 {
352     int cc = 0;
353
354     HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n",
355                __func__, order_code, r1, cpu_addr);
356
357     /* Remember: Use "R1 or R1 + 1, whichever is the odd-numbered register"
358        as parameter (input). Status (output) is always R1. */
359
360     switch (order_code) {
361     case SIGP_SET_ARCH:
362         /* switch arch */
363         break;
364     case SIGP_SENSE:
365         /* enumerate CPU status */
366         if (cpu_addr) {
367             /* XXX implement when SMP comes */
368             return 3;
369         }
370         env->regs[r1] &= 0xffffffff00000000ULL;
371         cc = 1;
372         break;
373 #if !defined(CONFIG_USER_ONLY)
374     case SIGP_RESTART:
375         qemu_system_reset_request();
376         cpu_loop_exit(env);
377         break;
378     case SIGP_STOP:
379         qemu_system_shutdown_request();
380         cpu_loop_exit(env);
381         break;
382 #endif
383     default:
384         /* unknown sigp */
385         fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code);
386         cc = 3;
387     }
388
389     return cc;
390 }
391 #endif