Merge remote-tracking branch 'stefanha/trivial-patches' into staging
[qemu/opensuse.git] / target-s390x / mem_helper.c
blobbed21e6e1cae0a6f249935bce419ef3a191f4e10
1 /*
2 * S/390 memory access helper routines
4 * Copyright (c) 2009 Ulrich Hecht
5 * Copyright (c) 2009 Alexander Graf
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.
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.
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/>.
21 #include "cpu.h"
22 #include "helper.h"
24 /*****************************************************************************/
25 /* Softmmu support */
26 #if !defined(CONFIG_USER_ONLY)
27 #include "exec/softmmu_exec.h"
29 #define MMUSUFFIX _mmu
31 #define SHIFT 0
32 #include "exec/softmmu_template.h"
34 #define SHIFT 1
35 #include "exec/softmmu_template.h"
37 #define SHIFT 2
38 #include "exec/softmmu_template.h"
40 #define SHIFT 3
41 #include "exec/softmmu_template.h"
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 */
47 void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx,
48 uintptr_t retaddr)
50 int ret;
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 */
56 cpu_restore_state(env, retaddr);
58 cpu_loop_exit(env);
62 #endif
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
71 #ifndef CONFIG_USER_ONLY
72 static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
73 uint8_t byte)
75 hwaddr dest_phys;
76 hwaddr len = l;
77 void *dest_p;
78 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
79 int flags;
81 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
82 cpu_stb_data(env, dest, byte);
83 cpu_abort(env, "should never reach here");
85 dest_phys |= dest & ~TARGET_PAGE_MASK;
87 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
89 memset(dest_p, byte, len);
91 cpu_physical_memory_unmap(dest_p, 1, len, len);
94 static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
95 uint64_t src)
97 hwaddr dest_phys;
98 hwaddr src_phys;
99 hwaddr len = l;
100 void *dest_p;
101 void *src_p;
102 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
103 int flags;
105 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
106 cpu_stb_data(env, dest, 0);
107 cpu_abort(env, "should never reach here");
109 dest_phys |= dest & ~TARGET_PAGE_MASK;
111 if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
112 cpu_ldub_data(env, src);
113 cpu_abort(env, "should never reach here");
115 src_phys |= src & ~TARGET_PAGE_MASK;
117 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
118 src_p = cpu_physical_memory_map(src_phys, &len, 0);
120 memmove(dest_p, src_p, len);
122 cpu_physical_memory_unmap(dest_p, 1, len, len);
123 cpu_physical_memory_unmap(src_p, 0, len, len);
125 #endif
127 /* and on array */
128 uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
129 uint64_t src)
131 int i;
132 unsigned char x;
133 uint32_t cc = 0;
135 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
136 __func__, l, dest, src);
137 for (i = 0; i <= l; i++) {
138 x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
139 if (x) {
140 cc = 1;
142 cpu_stb_data(env, dest + i, x);
144 return cc;
147 /* xor on array */
148 uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
149 uint64_t src)
151 int i;
152 unsigned char x;
153 uint32_t cc = 0;
155 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
156 __func__, l, dest, src);
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;
165 #else
166 if (src == dest) {
167 memset(g2h(dest), 0, l + 1);
168 return 0;
170 #endif
172 for (i = 0; i <= l; i++) {
173 x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
174 if (x) {
175 cc = 1;
177 cpu_stb_data(env, dest + i, x);
179 return cc;
182 /* or on array */
183 uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
184 uint64_t src)
186 int i;
187 unsigned char x;
188 uint32_t cc = 0;
190 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
191 __func__, l, dest, src);
192 for (i = 0; i <= l; i++) {
193 x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
194 if (x) {
195 cc = 1;
197 cpu_stb_data(env, dest + i, x);
199 return cc;
202 /* memmove */
203 void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
205 int i = 0;
206 int x = 0;
207 uint32_t l_64 = (l + 1) / 8;
209 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
210 __func__, l, dest, src);
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)) {
217 mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
218 return;
219 } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
220 mvc_fast_memmove(env, l + 1, dest, src);
221 return;
224 #else
225 if (dest == (src + 1)) {
226 memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
227 return;
228 } else {
229 memmove(g2h(dest), g2h(src), l + 1);
230 return;
232 #endif
234 /* handle the parts that fit into 8-byte loads/stores */
235 if (dest != (src + 1)) {
236 for (i = 0; i < l_64; i++) {
237 cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
238 x += 8;
242 /* slow version crossing pages with byte accesses */
243 for (i = x; i <= l; i++) {
244 cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
248 /* compare unsigned byte arrays */
249 uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
251 int i;
252 unsigned char x, y;
253 uint32_t cc;
255 HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
256 __func__, l, s1, s2);
257 for (i = 0; i <= l; i++) {
258 x = cpu_ldub_data(env, s1 + i);
259 y = cpu_ldub_data(env, s2 + i);
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;
269 cc = 0;
270 done:
271 HELPER_LOG("\n");
272 return cc;
275 /* compare logical under mask */
276 uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
277 uint64_t addr)
279 uint8_t r, d;
280 uint32_t cc;
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) {
287 d = cpu_ldub_data(env, addr);
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;
298 addr++;
300 mask = (mask << 1) & 0xf;
301 r1 <<= 8;
303 HELPER_LOG("\n");
304 return cc;
307 /* store character under mask */
308 void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
309 uint64_t addr)
311 uint8_t r;
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;
318 cpu_stb_data(env, addr, r);
319 HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
320 addr++;
322 mask = (mask << 1) & 0xf;
323 r1 <<= 8;
325 HELPER_LOG("\n");
328 static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
330 uint64_t r = d2;
332 if (x2) {
333 r += env->regs[x2];
336 if (b2) {
337 r += env->regs[b2];
340 /* 31-Bit mode */
341 if (!(env->psw.mask & PSW_MASK_64)) {
342 r &= 0x7fffffff;
345 return r;
348 static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
350 uint64_t r = env->regs[reg];
352 /* 31-Bit mode */
353 if (!(env->psw.mask & PSW_MASK_64)) {
354 r &= 0x7fffffff;
357 return r;
360 /* search string (c is byte to search, r2 is string, r1 end of string) */
361 uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
363 uint64_t i;
364 uint32_t cc = 2;
365 uint64_t str = get_address_31fix(env, r2);
366 uint64_t end = get_address_31fix(env, r1);
368 HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__,
369 c, env->regs[r1], env->regs[r2]);
371 for (i = str; i != end; i++) {
372 if (cpu_ldub_data(env, i) == c) {
373 env->regs[r1] = i;
374 cc = 1;
375 break;
379 return cc;
382 /* unsigned string compare (c is string terminator) */
383 uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
385 uint64_t s1 = get_address_31fix(env, r1);
386 uint64_t s2 = get_address_31fix(env, r2);
387 uint8_t v1, v2;
388 uint32_t cc;
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));
396 #endif
397 for (;;) {
398 v1 = cpu_ldub_data(env, s1);
399 v2 = cpu_ldub_data(env, s2);
400 if ((v1 == c || v2 == c) || (v1 != v2)) {
401 break;
403 s1++;
404 s2++;
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;
415 return cc;
418 /* move page */
419 void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
421 /* XXX missing r0 handling */
422 #ifdef CONFIG_USER_ONLY
423 int i;
425 for (i = 0; i < TARGET_PAGE_SIZE; i++) {
426 cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i));
428 #else
429 mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
430 #endif
433 /* string copy (c is string terminator) */
434 void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
436 uint64_t dest = get_address_31fix(env, r1);
437 uint64_t src = get_address_31fix(env, r2);
438 uint8_t v;
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);
446 #endif
447 for (;;) {
448 v = cpu_ldub_data(env, src);
449 cpu_stb_data(env, dest, v);
450 if (v == c) {
451 break;
453 src++;
454 dest++;
456 env->regs[r1] = dest; /* FIXME: 31-bit mode! */
459 /* compare and swap 64-bit */
460 uint32_t HELPER(csg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
462 /* FIXME: locking? */
463 uint32_t cc;
464 uint64_t v2 = cpu_ldq_data(env, a2);
466 if (env->regs[r1] == v2) {
467 cc = 0;
468 cpu_stq_data(env, a2, env->regs[r3]);
469 } else {
470 cc = 1;
471 env->regs[r1] = v2;
473 return cc;
476 /* compare double and swap 64-bit */
477 uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
479 /* FIXME: locking? */
480 uint32_t cc;
481 uint64_t v2_hi = cpu_ldq_data(env, a2);
482 uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
483 uint64_t v1_hi = env->regs[r1];
484 uint64_t v1_lo = env->regs[r1 + 1];
486 if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
487 cc = 0;
488 cpu_stq_data(env, a2, env->regs[r3]);
489 cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
490 } else {
491 cc = 1;
492 env->regs[r1] = v2_hi;
493 env->regs[r1 + 1] = v2_lo;
496 return cc;
499 /* compare and swap 32-bit */
500 uint32_t HELPER(cs)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
502 /* FIXME: locking? */
503 uint32_t cc;
504 uint32_t v2 = cpu_ldl_data(env, a2);
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;
509 cpu_stl_data(env, a2, (uint32_t)env->regs[r3]);
510 } else {
511 cc = 1;
512 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2;
514 return cc;
517 static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
518 uint32_t mask)
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;
526 while (mask) {
527 if (mask & 8) {
528 env->regs[r1] &= ~rmask;
529 val = cpu_ldub_data(env, address);
530 if ((val & 0x80) && !ccd) {
531 cc = 1;
533 ccd = 1;
534 if (val && cc == 0) {
535 cc = 2;
537 env->regs[r1] |= (uint64_t)val << pos;
538 address++;
540 mask = (mask << 1) & 0xf;
541 pos -= 8;
542 rmask >>= 8;
545 return cc;
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
555 uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
556 uint64_t addr, uint64_t ret)
558 uint16_t insn = cpu_lduw_code(env, addr);
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;
565 l = v1 & 0xff;
566 insn2 = cpu_ldl_code(env, addr + 2);
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:
573 helper_mvc(env, l, get_address(env, 0, b1, d1),
574 get_address(env, 0, b2, d2));
575 break;
576 case 0x500:
577 cc = helper_clc(env, l, get_address(env, 0, b1, d1),
578 get_address(env, 0, b2, d2));
579 break;
580 case 0x700:
581 cc = helper_xc(env, l, get_address(env, 0, b1, d1),
582 get_address(env, 0, b2, d2));
583 break;
584 case 0xc00:
585 helper_tr(env, l, get_address(env, 0, b1, d1),
586 get_address(env, 0, b2, d2));
587 break;
588 default:
589 goto abort;
590 break;
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;
598 helper_exception(env, EXCP_SVC);
599 } else if ((insn & 0xff00) == 0xbf00) {
600 uint32_t insn2, r1, r3, b2, d2;
602 insn2 = cpu_ldl_code(env, addr + 2);
603 r1 = (insn2 >> 20) & 0xf;
604 r3 = (insn2 >> 16) & 0xf;
605 b2 = (insn2 >> 12) & 0xf;
606 d2 = insn2 & 0xfff;
607 cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
608 } else {
609 abort:
610 cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
611 insn);
613 return cc;
616 /* store character under mask high operates on the upper half of r1 */
617 void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
618 uint32_t mask)
620 int pos = 56; /* top of the upper half of r1 */
622 while (mask) {
623 if (mask & 8) {
624 cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff);
625 address++;
627 mask = (mask << 1) & 0xf;
628 pos -= 8;
632 /* insert character under mask high; same as icm, but operates on the
633 upper half of r1 */
634 uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
635 uint32_t mask)
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;
643 while (mask) {
644 if (mask & 8) {
645 env->regs[r1] &= ~rmask;
646 val = cpu_ldub_data(env, address);
647 if ((val & 0x80) && !ccd) {
648 cc = 1;
650 ccd = 1;
651 if (val && cc == 0) {
652 cc = 2;
654 env->regs[r1] |= (uint64_t)val << pos;
655 address++;
657 mask = (mask << 1) & 0xf;
658 pos -= 8;
659 rmask >>= 8;
662 return cc;
665 /* load access registers r1 to r3 from memory at a2 */
666 void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
668 int i;
670 for (i = r1;; i = (i + 1) % 16) {
671 env->aregs[i] = cpu_ldl_data(env, a2);
672 a2 += 4;
674 if (i == r3) {
675 break;
680 /* store access registers r1 to r3 in memory at a2 */
681 void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
683 int i;
685 for (i = r1;; i = (i + 1) % 16) {
686 cpu_stl_data(env, a2, env->aregs[i]);
687 a2 += 4;
689 if (i == r3) {
690 break;
695 /* move long */
696 uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
698 uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
699 uint64_t dest = get_address_31fix(env, r1);
700 uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
701 uint64_t src = get_address_31fix(env, r2);
702 uint8_t pad = src >> 24;
703 uint8_t v;
704 uint32_t cc;
706 if (destlen == srclen) {
707 cc = 0;
708 } else if (destlen < srclen) {
709 cc = 1;
710 } else {
711 cc = 2;
714 if (srclen > destlen) {
715 srclen = destlen;
718 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
719 v = cpu_ldub_data(env, src);
720 cpu_stb_data(env, dest, v);
723 for (; destlen; dest++, destlen--) {
724 cpu_stb_data(env, dest, pad);
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;
733 return cc;
736 /* move long extended another memcopy insn with more bells and whistles */
737 uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
738 uint32_t r3)
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;
748 if (!(env->psw.mask & PSW_MASK_64)) {
749 destlen = (uint32_t)destlen;
750 srclen = (uint32_t)srclen;
751 dest &= 0x7fffffff;
752 src &= 0x7fffffff;
755 if (destlen == srclen) {
756 cc = 0;
757 } else if (destlen < srclen) {
758 cc = 1;
759 } else {
760 cc = 2;
763 if (srclen > destlen) {
764 srclen = destlen;
767 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
768 v = cpu_ldub_data(env, src);
769 cpu_stb_data(env, dest, v);
772 for (; destlen; dest++, destlen--) {
773 cpu_stb_data(env, dest, pad);
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;
783 return cc;
786 /* compare logical long extended memcompare insn with padding */
787 uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
788 uint32_t r3)
790 uint64_t destlen = env->regs[r1 + 1];
791 uint64_t dest = get_address_31fix(env, r1);
792 uint64_t srclen = env->regs[r3 + 1];
793 uint64_t src = get_address_31fix(env, r3);
794 uint8_t pad = a2 & 0xff;
795 uint8_t v1 = 0, v2 = 0;
796 uint32_t cc = 0;
798 if (!(destlen || srclen)) {
799 return cc;
802 if (srclen > destlen) {
803 srclen = destlen;
806 for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
807 v1 = srclen ? cpu_ldub_data(env, src) : pad;
808 v2 = destlen ? cpu_ldub_data(env, dest) : pad;
809 if (v1 != v2) {
810 cc = (v1 < v2) ? 1 : 2;
811 break;
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;
821 return cc;
824 /* checksum */
825 void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
827 uint64_t src = get_address_31fix(env, r2);
828 uint64_t src_len = env->regs[(r2 + 1) & 15];
829 uint64_t cksm = (uint32_t)env->regs[r1];
831 while (src_len >= 4) {
832 cksm += cpu_ldl_data(env, src);
834 /* move to next word */
835 src_len -= 4;
836 src += 4;
839 switch (src_len) {
840 case 0:
841 break;
842 case 1:
843 cksm += cpu_ldub_data(env, src) << 24;
844 break;
845 case 2:
846 cksm += cpu_lduw_data(env, src) << 16;
847 break;
848 case 3:
849 cksm += cpu_lduw_data(env, src) << 16;
850 cksm += cpu_ldub_data(env, src + 2) << 8;
851 break;
854 /* indicate we've processed everything */
855 env->regs[r2] = src + src_len;
856 env->regs[(r2 + 1) & 15] = 0;
858 /* store result */
859 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
860 ((uint32_t)cksm + (cksm >> 32));
863 void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
864 uint64_t src)
866 int len_dest = len >> 4;
867 int len_src = len & 0xf;
868 uint8_t b;
869 int second_nibble = 0;
871 dest += len_dest;
872 src += len_src;
874 /* last byte is special, it only flips the nibbles */
875 b = cpu_ldub_data(env, src);
876 cpu_stb_data(env, dest, (b << 4) | (b >> 4));
877 src--;
878 len_src--;
880 /* now pad every nibble with 0xf0 */
882 while (len_dest > 0) {
883 uint8_t cur_byte = 0;
885 if (len_src > 0) {
886 cur_byte = cpu_ldub_data(env, src);
889 len_dest--;
890 dest--;
892 /* only advance one nibble at a time */
893 if (second_nibble) {
894 cur_byte >>= 4;
895 len_src--;
896 src--;
898 second_nibble = !second_nibble;
900 /* digit */
901 cur_byte = (cur_byte & 0xf);
902 /* zone bits */
903 cur_byte |= 0xf0;
905 cpu_stb_data(env, dest, cur_byte);
909 void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
910 uint64_t trans)
912 int i;
914 for (i = 0; i <= len; i++) {
915 uint8_t byte = cpu_ldub_data(env, array + i);
916 uint8_t new_byte = cpu_ldub_data(env, trans + byte);
918 cpu_stb_data(env, array + i, new_byte);
922 #if !defined(CONFIG_USER_ONLY)
923 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
925 int i;
926 uint64_t src = a2;
928 for (i = r1;; i = (i + 1) % 16) {
929 env->cregs[i] = cpu_ldq_data(env, src);
930 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
931 i, src, env->cregs[i]);
932 src += sizeof(uint64_t);
934 if (i == r3) {
935 break;
939 tlb_flush(env, 1);
942 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
944 int i;
945 uint64_t src = a2;
947 for (i = r1;; i = (i + 1) % 16) {
948 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
949 cpu_ldl_data(env, src);
950 src += sizeof(uint32_t);
952 if (i == r3) {
953 break;
957 tlb_flush(env, 1);
960 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
962 int i;
963 uint64_t dest = a2;
965 for (i = r1;; i = (i + 1) % 16) {
966 cpu_stq_data(env, dest, env->cregs[i]);
967 dest += sizeof(uint64_t);
969 if (i == r3) {
970 break;
975 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
977 int i;
978 uint64_t dest = a2;
980 for (i = r1;; i = (i + 1) % 16) {
981 cpu_stl_data(env, dest, env->cregs[i]);
982 dest += sizeof(uint32_t);
984 if (i == r3) {
985 break;
990 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
992 /* XXX implement */
994 return 0;
997 /* insert storage key extended */
998 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
1000 uint64_t addr = get_address(env, 0, 0, r2);
1002 if (addr > ram_size) {
1003 return 0;
1006 return env->storage_keys[addr / TARGET_PAGE_SIZE];
1009 /* set storage key extended */
1010 void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1012 uint64_t addr = get_address(env, 0, 0, r2);
1014 if (addr > ram_size) {
1015 return;
1018 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
1021 /* reset reference bit extended */
1022 uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1024 uint8_t re;
1025 uint8_t key;
1027 if (r2 > ram_size) {
1028 return 0;
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);
1036 * cc
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
1044 return re >> 1;
1047 /* compare and swap and purge */
1048 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1050 uint32_t cc;
1051 uint32_t o1 = env->regs[r1];
1052 uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
1053 uint32_t o2 = cpu_ldl_data(env, a2);
1055 if (o1 == o2) {
1056 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1057 if (env->regs[r2] & 0x3) {
1058 /* flush TLB / ALB */
1059 tlb_flush(env, 1);
1061 cc = 0;
1062 } else {
1063 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1064 cc = 1;
1067 return cc;
1070 static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1071 uint64_t mode1, uint64_t a2, uint64_t mode2)
1073 target_ulong src, dest;
1074 int flags, cc = 0, i;
1076 if (!l) {
1077 return 0;
1078 } else if (l > 256) {
1079 /* max 256 */
1080 l = 256;
1081 cc = 3;
1084 if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1085 cpu_loop_exit(env);
1087 dest |= a1 & ~TARGET_PAGE_MASK;
1089 if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1090 cpu_loop_exit(env);
1092 src |= a2 & ~TARGET_PAGE_MASK;
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))) {
1099 mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1100 break;
1102 stb_phys(dest + i, ldub_phys(src + i));
1105 return cc;
1108 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1110 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1111 __func__, l, a1, a2);
1113 return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1116 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1118 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1119 __func__, l, a1, a2);
1121 return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1124 /* invalidate pte */
1125 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1127 uint64_t page = vaddr & TARGET_PAGE_MASK;
1128 uint64_t pte = 0;
1130 /* XXX broadcast to other CPUs */
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);
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);
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);
1150 /* flush local tlb */
1151 void HELPER(ptlb)(CPUS390XState *env)
1153 tlb_flush(env, 1);
1156 /* store using real address */
1157 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1159 stw_phys(get_address(env, 0, 0, addr), v1);
1162 /* load real address */
1163 uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1)
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;
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);
1176 env->exception_index = old_exc;
1177 if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1178 cc = 3;
1180 if (env->exception_index == EXCP_PGM) {
1181 ret = env->int_pgm_code | 0x80000000;
1182 } else {
1183 ret |= addr & ~TARGET_PAGE_MASK;
1185 env->exception_index = old_exc;
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;
1194 return cc;
1197 #endif