]> git.proxmox.com Git - mirror_qemu.git/blame - target-s390x/mem_helper.c
target-s390: Split out disas_jcc
[mirror_qemu.git] / target-s390x / mem_helper.c
CommitLineData
8ef7f78e
BS
1/*
2 * S/390 memory access 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"
8ef7f78e
BS
22#include "helper.h"
23
24/*****************************************************************************/
25/* Softmmu support */
26#if !defined(CONFIG_USER_ONLY)
022c62cb 27#include "exec/softmmu_exec.h"
8ef7f78e
BS
28
29#define MMUSUFFIX _mmu
30
31#define SHIFT 0
022c62cb 32#include "exec/softmmu_template.h"
8ef7f78e
BS
33
34#define SHIFT 1
022c62cb 35#include "exec/softmmu_template.h"
8ef7f78e
BS
36
37#define SHIFT 2
022c62cb 38#include "exec/softmmu_template.h"
8ef7f78e
BS
39
40#define SHIFT 3
022c62cb 41#include "exec/softmmu_template.h"
8ef7f78e
BS
42
43/* try to fill the TLB and return an exception if error. If retaddr is
44 NULL, it means that the function was called in C code (i.e. not
45 from generated code or from helper.c) */
46/* XXX: fix it to restore all registers */
19b0516f 47void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx,
8ef7f78e
BS
48 uintptr_t retaddr)
49{
8ef7f78e
BS
50 int ret;
51
8ef7f78e
BS
52 ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx);
53 if (unlikely(ret != 0)) {
54 if (likely(retaddr)) {
55 /* now we have a real cpu fault */
a8a826a3 56 cpu_restore_state(env, retaddr);
8ef7f78e
BS
57 }
58 cpu_loop_exit(env);
59 }
8ef7f78e
BS
60}
61
62#endif
63
64/* #define DEBUG_HELPER */
65#ifdef DEBUG_HELPER
66#define HELPER_LOG(x...) qemu_log(x)
67#else
68#define HELPER_LOG(x...)
69#endif
70
71#ifndef CONFIG_USER_ONLY
72static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
73 uint8_t byte)
74{
a8170e5e
AK
75 hwaddr dest_phys;
76 hwaddr len = l;
8ef7f78e
BS
77 void *dest_p;
78 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
79 int flags;
80
81 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
19b0516f 82 cpu_stb_data(env, dest, byte);
8ef7f78e
BS
83 cpu_abort(env, "should never reach here");
84 }
85 dest_phys |= dest & ~TARGET_PAGE_MASK;
86
87 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
88
89 memset(dest_p, byte, len);
90
91 cpu_physical_memory_unmap(dest_p, 1, len, len);
92}
93
94static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
95 uint64_t src)
96{
a8170e5e
AK
97 hwaddr dest_phys;
98 hwaddr src_phys;
99 hwaddr len = l;
8ef7f78e
BS
100 void *dest_p;
101 void *src_p;
102 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
103 int flags;
104
105 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
19b0516f 106 cpu_stb_data(env, dest, 0);
8ef7f78e
BS
107 cpu_abort(env, "should never reach here");
108 }
109 dest_phys |= dest & ~TARGET_PAGE_MASK;
110
111 if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
19b0516f 112 cpu_ldub_data(env, src);
8ef7f78e
BS
113 cpu_abort(env, "should never reach here");
114 }
115 src_phys |= src & ~TARGET_PAGE_MASK;
116
117 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
118 src_p = cpu_physical_memory_map(src_phys, &len, 0);
119
120 memmove(dest_p, src_p, len);
121
122 cpu_physical_memory_unmap(dest_p, 1, len, len);
123 cpu_physical_memory_unmap(src_p, 0, len, len);
124}
125#endif
126
127/* and on array */
19b0516f
BS
128uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
129 uint64_t src)
8ef7f78e
BS
130{
131 int i;
132 unsigned char x;
133 uint32_t cc = 0;
134
135 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
136 __func__, l, dest, src);
137 for (i = 0; i <= l; i++) {
19b0516f 138 x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
8ef7f78e
BS
139 if (x) {
140 cc = 1;
141 }
19b0516f 142 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
143 }
144 return cc;
145}
146
147/* xor on array */
19b0516f
BS
148uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
149 uint64_t src)
8ef7f78e
BS
150{
151 int i;
152 unsigned char x;
153 uint32_t cc = 0;
154
155 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
156 __func__, l, dest, src);
157
158#ifndef CONFIG_USER_ONLY
159 /* xor with itself is the same as memset(0) */
160 if ((l > 32) && (src == dest) &&
161 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
162 mvc_fast_memset(env, l + 1, dest, 0);
163 return 0;
164 }
165#else
166 if (src == dest) {
167 memset(g2h(dest), 0, l + 1);
168 return 0;
169 }
170#endif
171
172 for (i = 0; i <= l; i++) {
19b0516f 173 x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
8ef7f78e
BS
174 if (x) {
175 cc = 1;
176 }
19b0516f 177 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
178 }
179 return cc;
180}
181
182/* or on array */
19b0516f
BS
183uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
184 uint64_t src)
8ef7f78e
BS
185{
186 int i;
187 unsigned char x;
188 uint32_t cc = 0;
189
190 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
191 __func__, l, dest, src);
192 for (i = 0; i <= l; i++) {
19b0516f 193 x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
8ef7f78e
BS
194 if (x) {
195 cc = 1;
196 }
19b0516f 197 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
198 }
199 return cc;
200}
201
202/* memmove */
19b0516f 203void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
8ef7f78e
BS
204{
205 int i = 0;
206 int x = 0;
207 uint32_t l_64 = (l + 1) / 8;
208
209 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
210 __func__, l, dest, src);
211
212#ifndef CONFIG_USER_ONLY
213 if ((l > 32) &&
214 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
215 (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
216 if (dest == (src + 1)) {
19b0516f 217 mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
8ef7f78e
BS
218 return;
219 } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
220 mvc_fast_memmove(env, l + 1, dest, src);
221 return;
222 }
223 }
224#else
225 if (dest == (src + 1)) {
19b0516f 226 memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
8ef7f78e
BS
227 return;
228 } else {
229 memmove(g2h(dest), g2h(src), l + 1);
230 return;
231 }
232#endif
233
234 /* handle the parts that fit into 8-byte loads/stores */
235 if (dest != (src + 1)) {
236 for (i = 0; i < l_64; i++) {
19b0516f 237 cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
8ef7f78e
BS
238 x += 8;
239 }
240 }
241
242 /* slow version crossing pages with byte accesses */
243 for (i = x; i <= l; i++) {
19b0516f 244 cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
8ef7f78e
BS
245 }
246}
247
248/* compare unsigned byte arrays */
19b0516f 249uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
8ef7f78e
BS
250{
251 int i;
252 unsigned char x, y;
253 uint32_t cc;
254
255 HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
256 __func__, l, s1, s2);
257 for (i = 0; i <= l; i++) {
19b0516f
BS
258 x = cpu_ldub_data(env, s1 + i);
259 y = cpu_ldub_data(env, s2 + i);
8ef7f78e
BS
260 HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
261 if (x < y) {
262 cc = 1;
263 goto done;
264 } else if (x > y) {
265 cc = 2;
266 goto done;
267 }
268 }
269 cc = 0;
270 done:
271 HELPER_LOG("\n");
272 return cc;
273}
274
275/* compare logical under mask */
19b0516f
BS
276uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
277 uint64_t addr)
8ef7f78e
BS
278{
279 uint8_t r, d;
280 uint32_t cc;
281
282 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
283 mask, addr);
284 cc = 0;
285 while (mask) {
286 if (mask & 8) {
19b0516f 287 d = cpu_ldub_data(env, addr);
8ef7f78e
BS
288 r = (r1 & 0xff000000UL) >> 24;
289 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
290 addr);
291 if (r < d) {
292 cc = 1;
293 break;
294 } else if (r > d) {
295 cc = 2;
296 break;
297 }
298 addr++;
299 }
300 mask = (mask << 1) & 0xf;
301 r1 <<= 8;
302 }
303 HELPER_LOG("\n");
304 return cc;
305}
306
307/* store character under mask */
19b0516f
BS
308void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
309 uint64_t addr)
8ef7f78e
BS
310{
311 uint8_t r;
312
313 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__, r1, mask,
314 addr);
315 while (mask) {
316 if (mask & 8) {
317 r = (r1 & 0xff000000UL) >> 24;
19b0516f 318 cpu_stb_data(env, addr, r);
8ef7f78e
BS
319 HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
320 addr++;
321 }
322 mask = (mask << 1) & 0xf;
323 r1 <<= 8;
324 }
325 HELPER_LOG("\n");
326}
327
19b0516f 328static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
8ef7f78e
BS
329{
330 uint64_t r = d2;
331
332 if (x2) {
333 r += env->regs[x2];
334 }
335
336 if (b2) {
337 r += env->regs[b2];
338 }
339
340 /* 31-Bit mode */
341 if (!(env->psw.mask & PSW_MASK_64)) {
342 r &= 0x7fffffff;
343 }
344
345 return r;
346}
347
19b0516f 348static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
8ef7f78e
BS
349{
350 uint64_t r = env->regs[reg];
351
352 /* 31-Bit mode */
353 if (!(env->psw.mask & PSW_MASK_64)) {
354 r &= 0x7fffffff;
355 }
356
357 return r;
358}
359
360/* search string (c is byte to search, r2 is string, r1 end of string) */
19b0516f 361uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
8ef7f78e
BS
362{
363 uint64_t i;
364 uint32_t cc = 2;
19b0516f
BS
365 uint64_t str = get_address_31fix(env, r2);
366 uint64_t end = get_address_31fix(env, r1);
8ef7f78e
BS
367
368 HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__,
369 c, env->regs[r1], env->regs[r2]);
370
371 for (i = str; i != end; i++) {
19b0516f 372 if (cpu_ldub_data(env, i) == c) {
8ef7f78e
BS
373 env->regs[r1] = i;
374 cc = 1;
375 break;
376 }
377 }
378
379 return cc;
380}
381
382/* unsigned string compare (c is string terminator) */
19b0516f 383uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
8ef7f78e 384{
19b0516f
BS
385 uint64_t s1 = get_address_31fix(env, r1);
386 uint64_t s2 = get_address_31fix(env, r2);
8ef7f78e
BS
387 uint8_t v1, v2;
388 uint32_t cc;
389
390 c = c & 0xff;
391#ifdef CONFIG_USER_ONLY
392 if (!c) {
393 HELPER_LOG("%s: comparing '%s' and '%s'\n",
394 __func__, (char *)g2h(s1), (char *)g2h(s2));
395 }
396#endif
397 for (;;) {
19b0516f
BS
398 v1 = cpu_ldub_data(env, s1);
399 v2 = cpu_ldub_data(env, s2);
8ef7f78e
BS
400 if ((v1 == c || v2 == c) || (v1 != v2)) {
401 break;
402 }
403 s1++;
404 s2++;
405 }
406
407 if (v1 == v2) {
408 cc = 0;
409 } else {
410 cc = (v1 < v2) ? 1 : 2;
411 /* FIXME: 31-bit mode! */
412 env->regs[r1] = s1;
413 env->regs[r2] = s2;
414 }
415 return cc;
416}
417
418/* move page */
19b0516f 419void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
8ef7f78e
BS
420{
421 /* XXX missing r0 handling */
422#ifdef CONFIG_USER_ONLY
423 int i;
424
425 for (i = 0; i < TARGET_PAGE_SIZE; i++) {
19b0516f 426 cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i));
8ef7f78e
BS
427 }
428#else
429 mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
430#endif
431}
432
433/* string copy (c is string terminator) */
19b0516f 434void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
8ef7f78e 435{
19b0516f
BS
436 uint64_t dest = get_address_31fix(env, r1);
437 uint64_t src = get_address_31fix(env, r2);
8ef7f78e
BS
438 uint8_t v;
439
440 c = c & 0xff;
441#ifdef CONFIG_USER_ONLY
442 if (!c) {
443 HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src),
444 dest);
445 }
446#endif
447 for (;;) {
19b0516f
BS
448 v = cpu_ldub_data(env, src);
449 cpu_stb_data(env, dest, v);
8ef7f78e
BS
450 if (v == c) {
451 break;
452 }
453 src++;
454 dest++;
455 }
456 env->regs[r1] = dest; /* FIXME: 31-bit mode! */
457}
458
459/* compare and swap 64-bit */
19b0516f 460uint32_t HELPER(csg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
461{
462 /* FIXME: locking? */
463 uint32_t cc;
19b0516f 464 uint64_t v2 = cpu_ldq_data(env, a2);
8ef7f78e
BS
465
466 if (env->regs[r1] == v2) {
467 cc = 0;
19b0516f 468 cpu_stq_data(env, a2, env->regs[r3]);
8ef7f78e
BS
469 } else {
470 cc = 1;
471 env->regs[r1] = v2;
472 }
473 return cc;
474}
475
476/* compare double and swap 64-bit */
19b0516f 477uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
478{
479 /* FIXME: locking? */
480 uint32_t cc;
19b0516f
BS
481 uint64_t v2_hi = cpu_ldq_data(env, a2);
482 uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
8ef7f78e
BS
483 uint64_t v1_hi = env->regs[r1];
484 uint64_t v1_lo = env->regs[r1 + 1];
485
486 if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
487 cc = 0;
19b0516f
BS
488 cpu_stq_data(env, a2, env->regs[r3]);
489 cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
8ef7f78e
BS
490 } else {
491 cc = 1;
492 env->regs[r1] = v2_hi;
493 env->regs[r1 + 1] = v2_lo;
494 }
495
496 return cc;
497}
498
499/* compare and swap 32-bit */
19b0516f 500uint32_t HELPER(cs)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
501{
502 /* FIXME: locking? */
503 uint32_t cc;
19b0516f 504 uint32_t v2 = cpu_ldl_data(env, a2);
8ef7f78e
BS
505
506 HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __func__, r1, a2, r3);
507 if (((uint32_t)env->regs[r1]) == v2) {
508 cc = 0;
19b0516f 509 cpu_stl_data(env, a2, (uint32_t)env->regs[r3]);
8ef7f78e
BS
510 } else {
511 cc = 1;
512 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2;
513 }
514 return cc;
515}
516
19b0516f
BS
517static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
518 uint32_t mask)
8ef7f78e
BS
519{
520 int pos = 24; /* top of the lower half of r1 */
521 uint64_t rmask = 0xff000000ULL;
522 uint8_t val = 0;
523 int ccd = 0;
524 uint32_t cc = 0;
525
526 while (mask) {
527 if (mask & 8) {
528 env->regs[r1] &= ~rmask;
19b0516f 529 val = cpu_ldub_data(env, address);
8ef7f78e
BS
530 if ((val & 0x80) && !ccd) {
531 cc = 1;
532 }
533 ccd = 1;
534 if (val && cc == 0) {
535 cc = 2;
536 }
537 env->regs[r1] |= (uint64_t)val << pos;
538 address++;
539 }
540 mask = (mask << 1) & 0xf;
541 pos -= 8;
542 rmask >>= 8;
543 }
544
545 return cc;
546}
547
548/* execute instruction
549 this instruction executes an insn modified with the contents of r1
550 it does not change the executed instruction in memory
551 it does not change the program counter
552 in other words: tricky...
553 currently implemented by interpreting the cases it is most commonly used in
554*/
19b0516f
BS
555uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
556 uint64_t addr, uint64_t ret)
8ef7f78e 557{
19b0516f 558 uint16_t insn = cpu_lduw_code(env, addr);
8ef7f78e
BS
559
560 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
561 insn);
562 if ((insn & 0xf0ff) == 0xd000) {
563 uint32_t l, insn2, b1, b2, d1, d2;
564
565 l = v1 & 0xff;
19b0516f 566 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
567 b1 = (insn2 >> 28) & 0xf;
568 b2 = (insn2 >> 12) & 0xf;
569 d1 = (insn2 >> 16) & 0xfff;
570 d2 = insn2 & 0xfff;
571 switch (insn & 0xf00) {
572 case 0x200:
19b0516f
BS
573 helper_mvc(env, l, get_address(env, 0, b1, d1),
574 get_address(env, 0, b2, d2));
8ef7f78e
BS
575 break;
576 case 0x500:
19b0516f
BS
577 cc = helper_clc(env, l, get_address(env, 0, b1, d1),
578 get_address(env, 0, b2, d2));
8ef7f78e
BS
579 break;
580 case 0x700:
19b0516f
BS
581 cc = helper_xc(env, l, get_address(env, 0, b1, d1),
582 get_address(env, 0, b2, d2));
8ef7f78e
BS
583 break;
584 case 0xc00:
19b0516f
BS
585 helper_tr(env, l, get_address(env, 0, b1, d1),
586 get_address(env, 0, b2, d2));
8ef7f78e
BS
587 break;
588 default:
589 goto abort;
590 break;
591 }
592 } else if ((insn & 0xff00) == 0x0a00) {
593 /* supervisor call */
594 HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
595 env->psw.addr = ret - 4;
596 env->int_svc_code = (insn | v1) & 0xff;
597 env->int_svc_ilc = 4;
089f5c06 598 helper_exception(env, EXCP_SVC);
8ef7f78e
BS
599 } else if ((insn & 0xff00) == 0xbf00) {
600 uint32_t insn2, r1, r3, b2, d2;
601
19b0516f 602 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
603 r1 = (insn2 >> 20) & 0xf;
604 r3 = (insn2 >> 16) & 0xf;
605 b2 = (insn2 >> 12) & 0xf;
606 d2 = insn2 & 0xfff;
19b0516f 607 cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
8ef7f78e
BS
608 } else {
609 abort:
610 cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
611 insn);
612 }
613 return cc;
614}
615
616/* store character under mask high operates on the upper half of r1 */
19b0516f
BS
617void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
618 uint32_t mask)
8ef7f78e
BS
619{
620 int pos = 56; /* top of the upper half of r1 */
621
622 while (mask) {
623 if (mask & 8) {
19b0516f 624 cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff);
8ef7f78e
BS
625 address++;
626 }
627 mask = (mask << 1) & 0xf;
628 pos -= 8;
629 }
630}
631
632/* insert character under mask high; same as icm, but operates on the
633 upper half of r1 */
19b0516f
BS
634uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
635 uint32_t mask)
8ef7f78e
BS
636{
637 int pos = 56; /* top of the upper half of r1 */
638 uint64_t rmask = 0xff00000000000000ULL;
639 uint8_t val = 0;
640 int ccd = 0;
641 uint32_t cc = 0;
642
643 while (mask) {
644 if (mask & 8) {
645 env->regs[r1] &= ~rmask;
19b0516f 646 val = cpu_ldub_data(env, address);
8ef7f78e
BS
647 if ((val & 0x80) && !ccd) {
648 cc = 1;
649 }
650 ccd = 1;
651 if (val && cc == 0) {
652 cc = 2;
653 }
654 env->regs[r1] |= (uint64_t)val << pos;
655 address++;
656 }
657 mask = (mask << 1) & 0xf;
658 pos -= 8;
659 rmask >>= 8;
660 }
661
662 return cc;
663}
664
665/* load access registers r1 to r3 from memory at a2 */
19b0516f 666void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
667{
668 int i;
669
670 for (i = r1;; i = (i + 1) % 16) {
19b0516f 671 env->aregs[i] = cpu_ldl_data(env, a2);
8ef7f78e
BS
672 a2 += 4;
673
674 if (i == r3) {
675 break;
676 }
677 }
678}
679
680/* store access registers r1 to r3 in memory at a2 */
19b0516f 681void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
682{
683 int i;
684
685 for (i = r1;; i = (i + 1) % 16) {
19b0516f 686 cpu_stl_data(env, a2, env->aregs[i]);
8ef7f78e
BS
687 a2 += 4;
688
689 if (i == r3) {
690 break;
691 }
692 }
693}
694
695/* move long */
19b0516f 696uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
8ef7f78e
BS
697{
698 uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
19b0516f 699 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 700 uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
19b0516f 701 uint64_t src = get_address_31fix(env, r2);
8ef7f78e
BS
702 uint8_t pad = src >> 24;
703 uint8_t v;
704 uint32_t cc;
705
706 if (destlen == srclen) {
707 cc = 0;
708 } else if (destlen < srclen) {
709 cc = 1;
710 } else {
711 cc = 2;
712 }
713
714 if (srclen > destlen) {
715 srclen = destlen;
716 }
717
718 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
719 v = cpu_ldub_data(env, src);
720 cpu_stb_data(env, dest, v);
8ef7f78e
BS
721 }
722
723 for (; destlen; dest++, destlen--) {
19b0516f 724 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
725 }
726
727 env->regs[r1 + 1] = destlen;
728 /* can't use srclen here, we trunc'ed it */
729 env->regs[r2 + 1] -= src - env->regs[r2];
730 env->regs[r1] = dest;
731 env->regs[r2] = src;
732
733 return cc;
734}
735
736/* move long extended another memcopy insn with more bells and whistles */
19b0516f
BS
737uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
738 uint32_t r3)
8ef7f78e
BS
739{
740 uint64_t destlen = env->regs[r1 + 1];
741 uint64_t dest = env->regs[r1];
742 uint64_t srclen = env->regs[r3 + 1];
743 uint64_t src = env->regs[r3];
744 uint8_t pad = a2 & 0xff;
745 uint8_t v;
746 uint32_t cc;
747
748 if (!(env->psw.mask & PSW_MASK_64)) {
749 destlen = (uint32_t)destlen;
750 srclen = (uint32_t)srclen;
751 dest &= 0x7fffffff;
752 src &= 0x7fffffff;
753 }
754
755 if (destlen == srclen) {
756 cc = 0;
757 } else if (destlen < srclen) {
758 cc = 1;
759 } else {
760 cc = 2;
761 }
762
763 if (srclen > destlen) {
764 srclen = destlen;
765 }
766
767 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
768 v = cpu_ldub_data(env, src);
769 cpu_stb_data(env, dest, v);
8ef7f78e
BS
770 }
771
772 for (; destlen; dest++, destlen--) {
19b0516f 773 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
774 }
775
776 env->regs[r1 + 1] = destlen;
777 /* can't use srclen here, we trunc'ed it */
778 /* FIXME: 31-bit mode! */
779 env->regs[r3 + 1] -= src - env->regs[r3];
780 env->regs[r1] = dest;
781 env->regs[r3] = src;
782
783 return cc;
784}
785
786/* compare logical long extended memcompare insn with padding */
19b0516f
BS
787uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
788 uint32_t r3)
8ef7f78e
BS
789{
790 uint64_t destlen = env->regs[r1 + 1];
19b0516f 791 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 792 uint64_t srclen = env->regs[r3 + 1];
19b0516f 793 uint64_t src = get_address_31fix(env, r3);
8ef7f78e
BS
794 uint8_t pad = a2 & 0xff;
795 uint8_t v1 = 0, v2 = 0;
796 uint32_t cc = 0;
797
798 if (!(destlen || srclen)) {
799 return cc;
800 }
801
802 if (srclen > destlen) {
803 srclen = destlen;
804 }
805
806 for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
807 v1 = srclen ? cpu_ldub_data(env, src) : pad;
808 v2 = destlen ? cpu_ldub_data(env, dest) : pad;
8ef7f78e
BS
809 if (v1 != v2) {
810 cc = (v1 < v2) ? 1 : 2;
811 break;
812 }
813 }
814
815 env->regs[r1 + 1] = destlen;
816 /* can't use srclen here, we trunc'ed it */
817 env->regs[r3 + 1] -= src - env->regs[r3];
818 env->regs[r1] = dest;
819 env->regs[r3] = src;
820
821 return cc;
822}
823
824/* checksum */
19b0516f 825void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
8ef7f78e 826{
19b0516f 827 uint64_t src = get_address_31fix(env, r2);
8ef7f78e
BS
828 uint64_t src_len = env->regs[(r2 + 1) & 15];
829 uint64_t cksm = (uint32_t)env->regs[r1];
830
831 while (src_len >= 4) {
19b0516f 832 cksm += cpu_ldl_data(env, src);
8ef7f78e
BS
833
834 /* move to next word */
835 src_len -= 4;
836 src += 4;
837 }
838
839 switch (src_len) {
840 case 0:
841 break;
842 case 1:
19b0516f 843 cksm += cpu_ldub_data(env, src) << 24;
8ef7f78e
BS
844 break;
845 case 2:
19b0516f 846 cksm += cpu_lduw_data(env, src) << 16;
8ef7f78e
BS
847 break;
848 case 3:
19b0516f
BS
849 cksm += cpu_lduw_data(env, src) << 16;
850 cksm += cpu_ldub_data(env, src + 2) << 8;
8ef7f78e
BS
851 break;
852 }
853
854 /* indicate we've processed everything */
855 env->regs[r2] = src + src_len;
856 env->regs[(r2 + 1) & 15] = 0;
857
858 /* store result */
859 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
860 ((uint32_t)cksm + (cksm >> 32));
861}
862
19b0516f
BS
863void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
864 uint64_t src)
8ef7f78e
BS
865{
866 int len_dest = len >> 4;
867 int len_src = len & 0xf;
868 uint8_t b;
869 int second_nibble = 0;
870
871 dest += len_dest;
872 src += len_src;
873
874 /* last byte is special, it only flips the nibbles */
19b0516f
BS
875 b = cpu_ldub_data(env, src);
876 cpu_stb_data(env, dest, (b << 4) | (b >> 4));
8ef7f78e
BS
877 src--;
878 len_src--;
879
880 /* now pad every nibble with 0xf0 */
881
882 while (len_dest > 0) {
883 uint8_t cur_byte = 0;
884
885 if (len_src > 0) {
19b0516f 886 cur_byte = cpu_ldub_data(env, src);
8ef7f78e
BS
887 }
888
889 len_dest--;
890 dest--;
891
892 /* only advance one nibble at a time */
893 if (second_nibble) {
894 cur_byte >>= 4;
895 len_src--;
896 src--;
897 }
898 second_nibble = !second_nibble;
899
900 /* digit */
901 cur_byte = (cur_byte & 0xf);
902 /* zone bits */
903 cur_byte |= 0xf0;
904
19b0516f 905 cpu_stb_data(env, dest, cur_byte);
8ef7f78e
BS
906 }
907}
908
19b0516f
BS
909void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
910 uint64_t trans)
8ef7f78e
BS
911{
912 int i;
913
914 for (i = 0; i <= len; i++) {
19b0516f
BS
915 uint8_t byte = cpu_ldub_data(env, array + i);
916 uint8_t new_byte = cpu_ldub_data(env, trans + byte);
8ef7f78e 917
19b0516f 918 cpu_stb_data(env, array + i, new_byte);
8ef7f78e
BS
919 }
920}
921
922#if !defined(CONFIG_USER_ONLY)
19b0516f 923void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
924{
925 int i;
926 uint64_t src = a2;
927
928 for (i = r1;; i = (i + 1) % 16) {
19b0516f 929 env->cregs[i] = cpu_ldq_data(env, src);
8ef7f78e
BS
930 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
931 i, src, env->cregs[i]);
932 src += sizeof(uint64_t);
933
934 if (i == r3) {
935 break;
936 }
937 }
938
939 tlb_flush(env, 1);
940}
941
19b0516f 942void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
943{
944 int i;
945 uint64_t src = a2;
946
947 for (i = r1;; i = (i + 1) % 16) {
19b0516f
BS
948 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
949 cpu_ldl_data(env, src);
8ef7f78e
BS
950 src += sizeof(uint32_t);
951
952 if (i == r3) {
953 break;
954 }
955 }
956
957 tlb_flush(env, 1);
958}
959
19b0516f 960void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
961{
962 int i;
963 uint64_t dest = a2;
964
965 for (i = r1;; i = (i + 1) % 16) {
19b0516f 966 cpu_stq_data(env, dest, env->cregs[i]);
8ef7f78e
BS
967 dest += sizeof(uint64_t);
968
969 if (i == r3) {
970 break;
971 }
972 }
973}
974
19b0516f 975void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
976{
977 int i;
978 uint64_t dest = a2;
979
980 for (i = r1;; i = (i + 1) % 16) {
19b0516f 981 cpu_stl_data(env, dest, env->cregs[i]);
8ef7f78e
BS
982 dest += sizeof(uint32_t);
983
984 if (i == r3) {
985 break;
986 }
987 }
988}
989
990uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
991{
992 /* XXX implement */
993
994 return 0;
995}
996
997/* insert storage key extended */
19b0516f 998uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
8ef7f78e 999{
19b0516f 1000 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
1001
1002 if (addr > ram_size) {
1003 return 0;
1004 }
1005
1006 return env->storage_keys[addr / TARGET_PAGE_SIZE];
1007}
1008
1009/* set storage key extended */
19b0516f 1010void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
8ef7f78e 1011{
19b0516f 1012 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
1013
1014 if (addr > ram_size) {
1015 return;
1016 }
1017
1018 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
1019}
1020
1021/* reset reference bit extended */
19b0516f 1022uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
8ef7f78e
BS
1023{
1024 uint8_t re;
1025 uint8_t key;
1026
1027 if (r2 > ram_size) {
1028 return 0;
1029 }
1030
1031 key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
1032 re = key & (SK_R | SK_C);
1033 env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
1034
1035 /*
1036 * cc
1037 *
1038 * 0 Reference bit zero; change bit zero
1039 * 1 Reference bit zero; change bit one
1040 * 2 Reference bit one; change bit zero
1041 * 3 Reference bit one; change bit one
1042 */
1043
1044 return re >> 1;
1045}
1046
1047/* compare and swap and purge */
19b0516f 1048uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
8ef7f78e
BS
1049{
1050 uint32_t cc;
1051 uint32_t o1 = env->regs[r1];
19b0516f
BS
1052 uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
1053 uint32_t o2 = cpu_ldl_data(env, a2);
8ef7f78e
BS
1054
1055 if (o1 == o2) {
19b0516f 1056 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
8ef7f78e
BS
1057 if (env->regs[r2] & 0x3) {
1058 /* flush TLB / ALB */
1059 tlb_flush(env, 1);
1060 }
1061 cc = 0;
1062 } else {
1063 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1064 cc = 1;
1065 }
1066
1067 return cc;
1068}
1069
19b0516f
BS
1070static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1071 uint64_t mode1, uint64_t a2, uint64_t mode2)
8ef7f78e
BS
1072{
1073 target_ulong src, dest;
1074 int flags, cc = 0, i;
1075
1076 if (!l) {
1077 return 0;
1078 } else if (l > 256) {
1079 /* max 256 */
1080 l = 256;
1081 cc = 3;
1082 }
1083
1084 if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1085 cpu_loop_exit(env);
1086 }
1087 dest |= a1 & ~TARGET_PAGE_MASK;
1088
1089 if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1090 cpu_loop_exit(env);
1091 }
1092 src |= a2 & ~TARGET_PAGE_MASK;
1093
1094 /* XXX replace w/ memcpy */
1095 for (i = 0; i < l; i++) {
1096 /* XXX be more clever */
1097 if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1098 (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
19b0516f 1099 mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
8ef7f78e
BS
1100 break;
1101 }
1102 stb_phys(dest + i, ldub_phys(src + i));
1103 }
1104
1105 return cc;
1106}
1107
19b0516f 1108uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
1109{
1110 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1111 __func__, l, a1, a2);
1112
19b0516f 1113 return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
8ef7f78e
BS
1114}
1115
19b0516f 1116uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
1117{
1118 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1119 __func__, l, a1, a2);
1120
19b0516f 1121 return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
8ef7f78e
BS
1122}
1123
1124/* invalidate pte */
19b0516f 1125void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
8ef7f78e
BS
1126{
1127 uint64_t page = vaddr & TARGET_PAGE_MASK;
1128 uint64_t pte = 0;
1129
1130 /* XXX broadcast to other CPUs */
1131
1132 /* XXX Linux is nice enough to give us the exact pte address.
1133 According to spec we'd have to find it out ourselves */
1134 /* XXX Linux is fine with overwriting the pte, the spec requires
1135 us to only set the invalid bit */
1136 stq_phys(pte_addr, pte | _PAGE_INVALID);
1137
1138 /* XXX we exploit the fact that Linux passes the exact virtual
1139 address here - it's not obliged to! */
1140 tlb_flush_page(env, page);
1141
1142 /* XXX 31-bit hack */
1143 if (page & 0x80000000) {
1144 tlb_flush_page(env, page & ~0x80000000);
1145 } else {
1146 tlb_flush_page(env, page | 0x80000000);
1147 }
1148}
1149
1150/* flush local tlb */
19b0516f 1151void HELPER(ptlb)(CPUS390XState *env)
8ef7f78e
BS
1152{
1153 tlb_flush(env, 1);
1154}
1155
1156/* store using real address */
19b0516f 1157void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
8ef7f78e 1158{
19b0516f 1159 stw_phys(get_address(env, 0, 0, addr), v1);
8ef7f78e
BS
1160}
1161
1162/* load real address */
19b0516f 1163uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1)
8ef7f78e
BS
1164{
1165 uint32_t cc = 0;
1166 int old_exc = env->exception_index;
1167 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1168 uint64_t ret;
1169 int flags;
1170
1171 /* XXX incomplete - has more corner cases */
1172 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1173 program_interrupt(env, PGM_SPECIAL_OP, 2);
1174 }
1175
1176 env->exception_index = old_exc;
1177 if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1178 cc = 3;
1179 }
1180 if (env->exception_index == EXCP_PGM) {
1181 ret = env->int_pgm_code | 0x80000000;
1182 } else {
1183 ret |= addr & ~TARGET_PAGE_MASK;
1184 }
1185 env->exception_index = old_exc;
1186
1187 if (!(env->psw.mask & PSW_MASK_64)) {
1188 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
1189 (ret & 0xffffffffULL);
1190 } else {
1191 env->regs[r1] = ret;
1192 }
1193
1194 return cc;
1195}
1196
1197#endif