target-s390: Convert CLST, MVST
[sdk/emulator/qemu.git] / target-s390x / mem_helper.c
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"
22 #include "helper.h"
23
24 /*****************************************************************************/
25 /* Softmmu support */
26 #if !defined(CONFIG_USER_ONLY)
27 #include "exec/softmmu_exec.h"
28
29 #define MMUSUFFIX _mmu
30
31 #define SHIFT 0
32 #include "exec/softmmu_template.h"
33
34 #define SHIFT 1
35 #include "exec/softmmu_template.h"
36
37 #define SHIFT 2
38 #include "exec/softmmu_template.h"
39
40 #define SHIFT 3
41 #include "exec/softmmu_template.h"
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 */
47 void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx,
48               uintptr_t retaddr)
49 {
50     int ret;
51
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);
57         }
58         cpu_loop_exit(env);
59     }
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
72 static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
73                             uint8_t byte)
74 {
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;
80
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");
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
94 static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
95                              uint64_t src)
96 {
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;
104
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");
108     }
109     dest_phys |= dest & ~TARGET_PAGE_MASK;
110
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");
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 */
128 uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
129                     uint64_t src)
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++) {
138         x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
139         if (x) {
140             cc = 1;
141         }
142         cpu_stb_data(env, dest + i, x);
143     }
144     return cc;
145 }
146
147 /* xor on array */
148 uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
149                     uint64_t src)
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++) {
173         x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
174         if (x) {
175             cc = 1;
176         }
177         cpu_stb_data(env, dest + i, x);
178     }
179     return cc;
180 }
181
182 /* or on array */
183 uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
184                     uint64_t src)
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++) {
193         x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
194         if (x) {
195             cc = 1;
196         }
197         cpu_stb_data(env, dest + i, x);
198     }
199     return cc;
200 }
201
202 /* memmove */
203 void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
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)) {
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;
222         }
223     }
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;
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++) {
237             cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
238             x += 8;
239         }
240     }
241
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));
245     }
246 }
247
248 /* compare unsigned byte arrays */
249 uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
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++) {
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;
267         }
268     }
269     cc = 0;
270  done:
271     HELPER_LOG("\n");
272     return cc;
273 }
274
275 /* compare logical under mask */
276 uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
277                      uint64_t addr)
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) {
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;
297             }
298             addr++;
299         }
300         mask = (mask << 1) & 0xf;
301         r1 <<= 8;
302     }
303     HELPER_LOG("\n");
304     return cc;
305 }
306
307 static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
308 {
309     /* 31-Bit mode */
310     if (!(env->psw.mask & PSW_MASK_64)) {
311         a &= 0x7fffffff;
312     }
313     return a;
314 }
315
316 static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
317 {
318     uint64_t r = d2;
319     if (x2) {
320         r += env->regs[x2];
321     }
322     if (b2) {
323         r += env->regs[b2];
324     }
325     return fix_address(env, r);
326 }
327
328 static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
329 {
330     return fix_address(env, env->regs[reg]);
331 }
332
333 /* search string (c is byte to search, r2 is string, r1 end of string) */
334 uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
335 {
336     uint64_t i;
337     uint32_t cc = 2;
338     uint64_t str = get_address_31fix(env, r2);
339     uint64_t end = get_address_31fix(env, r1);
340
341     HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__,
342                c, env->regs[r1], env->regs[r2]);
343
344     for (i = str; i != end; i++) {
345         if (cpu_ldub_data(env, i) == c) {
346             env->regs[r1] = i;
347             cc = 1;
348             break;
349         }
350     }
351
352     return cc;
353 }
354
355 /* unsigned string compare (c is string terminator) */
356 uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
357 {
358     uint32_t len;
359
360     c = c & 0xff;
361     s1 = fix_address(env, s1);
362     s2 = fix_address(env, s2);
363
364     /* Lest we fail to service interrupts in a timely manner, limit the
365        amount of work we're willing to do.  For now, lets cap at 8k.  */
366     for (len = 0; len < 0x2000; ++len) {
367         uint8_t v1 = cpu_ldub_data(env, s1 + len);
368         uint8_t v2 = cpu_ldub_data(env, s2 + len);
369         if (v1 == v2) {
370             if (v1 == c) {
371                 /* Equal.  CC=0, and don't advance the registers.  */
372                 env->cc_op = 0;
373                 env->retxl = s2;
374                 return s1;
375             }
376         } else {
377             /* Unequal.  CC={1,2}, and advance the registers.  Note that
378                the terminator need not be zero, but the string that contains
379                the terminator is by definition "low".  */
380             env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
381             env->retxl = s2 + len;
382             return s1 + len;
383         }
384     }
385
386     /* CPU-determined bytes equal; advance the registers.  */
387     env->cc_op = 3;
388     env->retxl = s2 + len;
389     return s1 + len;
390 }
391
392 /* move page */
393 void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
394 {
395     /* XXX missing r0 handling */
396     env->cc_op = 0;
397 #ifdef CONFIG_USER_ONLY
398     memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
399 #else
400     mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
401 #endif
402 }
403
404 /* string copy (c is string terminator) */
405 uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
406 {
407     uint32_t len;
408
409     c = c & 0xff;
410     d = fix_address(env, d);
411     s = fix_address(env, s);
412
413     /* Lest we fail to service interrupts in a timely manner, limit the
414        amount of work we're willing to do.  For now, lets cap at 8k.  */
415     for (len = 0; len < 0x2000; ++len) {
416         uint8_t v = cpu_ldub_data(env, s + len);
417         cpu_stb_data(env, d + len, v);
418         if (v == c) {
419             /* Complete.  Set CC=1 and advance R1.  */
420             env->cc_op = 1;
421             env->retxl = s;
422             return d + len;
423         }
424     }
425
426     /* Incomplete.  Set CC=3 and signal to advance R1 and R2.  */
427     env->cc_op = 3;
428     env->retxl = s + len;
429     return d + len;
430 }
431
432 /* compare and swap 64-bit */
433 uint64_t HELPER(csg)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
434 {
435     /* FIXME: locking? */
436     uint64_t v2 = cpu_ldq_data(env, a2);
437     if (r1 == v2) {
438         cpu_stq_data(env, a2, r3);
439         env->cc_op = 0;
440         return r1;
441     } else {
442         env->cc_op = 1;
443         return v2;
444     }
445 }
446
447 /* compare double and swap 64-bit */
448 uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
449 {
450     /* FIXME: locking? */
451     uint32_t cc;
452     uint64_t v2_hi = cpu_ldq_data(env, a2);
453     uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
454     uint64_t v1_hi = env->regs[r1];
455     uint64_t v1_lo = env->regs[r1 + 1];
456
457     if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
458         cc = 0;
459         cpu_stq_data(env, a2, env->regs[r3]);
460         cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
461     } else {
462         cc = 1;
463         env->regs[r1] = v2_hi;
464         env->regs[r1 + 1] = v2_lo;
465     }
466
467     return cc;
468 }
469
470 /* compare and swap 32-bit */
471 uint64_t HELPER(cs)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
472 {
473     /* FIXME: locking? */
474     uint32_t v2 = cpu_ldl_data(env, a2);
475     if ((uint32_t)r1 == v2) {
476         cpu_stl_data(env, a2, (uint32_t)r3);
477         env->cc_op = 0;
478         return r1;
479     } else {
480         env->cc_op = 1;
481         return v2;
482     }
483 }
484
485 static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
486                            uint32_t mask)
487 {
488     int pos = 24; /* top of the lower half of r1 */
489     uint64_t rmask = 0xff000000ULL;
490     uint8_t val = 0;
491     int ccd = 0;
492     uint32_t cc = 0;
493
494     while (mask) {
495         if (mask & 8) {
496             env->regs[r1] &= ~rmask;
497             val = cpu_ldub_data(env, address);
498             if ((val & 0x80) && !ccd) {
499                 cc = 1;
500             }
501             ccd = 1;
502             if (val && cc == 0) {
503                 cc = 2;
504             }
505             env->regs[r1] |= (uint64_t)val << pos;
506             address++;
507         }
508         mask = (mask << 1) & 0xf;
509         pos -= 8;
510         rmask >>= 8;
511     }
512
513     return cc;
514 }
515
516 /* execute instruction
517    this instruction executes an insn modified with the contents of r1
518    it does not change the executed instruction in memory
519    it does not change the program counter
520    in other words: tricky...
521    currently implemented by interpreting the cases it is most commonly used in
522 */
523 uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
524                     uint64_t addr, uint64_t ret)
525 {
526     uint16_t insn = cpu_lduw_code(env, addr);
527
528     HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
529                insn);
530     if ((insn & 0xf0ff) == 0xd000) {
531         uint32_t l, insn2, b1, b2, d1, d2;
532
533         l = v1 & 0xff;
534         insn2 = cpu_ldl_code(env, addr + 2);
535         b1 = (insn2 >> 28) & 0xf;
536         b2 = (insn2 >> 12) & 0xf;
537         d1 = (insn2 >> 16) & 0xfff;
538         d2 = insn2 & 0xfff;
539         switch (insn & 0xf00) {
540         case 0x200:
541             helper_mvc(env, l, get_address(env, 0, b1, d1),
542                        get_address(env, 0, b2, d2));
543             break;
544         case 0x500:
545             cc = helper_clc(env, l, get_address(env, 0, b1, d1),
546                             get_address(env, 0, b2, d2));
547             break;
548         case 0x700:
549             cc = helper_xc(env, l, get_address(env, 0, b1, d1),
550                            get_address(env, 0, b2, d2));
551             break;
552         case 0xc00:
553             helper_tr(env, l, get_address(env, 0, b1, d1),
554                       get_address(env, 0, b2, d2));
555             break;
556         default:
557             goto abort;
558             break;
559         }
560     } else if ((insn & 0xff00) == 0x0a00) {
561         /* supervisor call */
562         HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
563         env->psw.addr = ret - 4;
564         env->int_svc_code = (insn | v1) & 0xff;
565         env->int_svc_ilen = 4;
566         helper_exception(env, EXCP_SVC);
567     } else if ((insn & 0xff00) == 0xbf00) {
568         uint32_t insn2, r1, r3, b2, d2;
569
570         insn2 = cpu_ldl_code(env, addr + 2);
571         r1 = (insn2 >> 20) & 0xf;
572         r3 = (insn2 >> 16) & 0xf;
573         b2 = (insn2 >> 12) & 0xf;
574         d2 = insn2 & 0xfff;
575         cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
576     } else {
577     abort:
578         cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
579                   insn);
580     }
581     return cc;
582 }
583
584 /* load access registers r1 to r3 from memory at a2 */
585 void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
586 {
587     int i;
588
589     for (i = r1;; i = (i + 1) % 16) {
590         env->aregs[i] = cpu_ldl_data(env, a2);
591         a2 += 4;
592
593         if (i == r3) {
594             break;
595         }
596     }
597 }
598
599 /* store access registers r1 to r3 in memory at a2 */
600 void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
601 {
602     int i;
603
604     for (i = r1;; i = (i + 1) % 16) {
605         cpu_stl_data(env, a2, env->aregs[i]);
606         a2 += 4;
607
608         if (i == r3) {
609             break;
610         }
611     }
612 }
613
614 /* move long */
615 uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
616 {
617     uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
618     uint64_t dest = get_address_31fix(env, r1);
619     uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
620     uint64_t src = get_address_31fix(env, r2);
621     uint8_t pad = src >> 24;
622     uint8_t v;
623     uint32_t cc;
624
625     if (destlen == srclen) {
626         cc = 0;
627     } else if (destlen < srclen) {
628         cc = 1;
629     } else {
630         cc = 2;
631     }
632
633     if (srclen > destlen) {
634         srclen = destlen;
635     }
636
637     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
638         v = cpu_ldub_data(env, src);
639         cpu_stb_data(env, dest, v);
640     }
641
642     for (; destlen; dest++, destlen--) {
643         cpu_stb_data(env, dest, pad);
644     }
645
646     env->regs[r1 + 1] = destlen;
647     /* can't use srclen here, we trunc'ed it */
648     env->regs[r2 + 1] -= src - env->regs[r2];
649     env->regs[r1] = dest;
650     env->regs[r2] = src;
651
652     return cc;
653 }
654
655 /* move long extended another memcopy insn with more bells and whistles */
656 uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
657                        uint32_t r3)
658 {
659     uint64_t destlen = env->regs[r1 + 1];
660     uint64_t dest = env->regs[r1];
661     uint64_t srclen = env->regs[r3 + 1];
662     uint64_t src = env->regs[r3];
663     uint8_t pad = a2 & 0xff;
664     uint8_t v;
665     uint32_t cc;
666
667     if (!(env->psw.mask & PSW_MASK_64)) {
668         destlen = (uint32_t)destlen;
669         srclen = (uint32_t)srclen;
670         dest &= 0x7fffffff;
671         src &= 0x7fffffff;
672     }
673
674     if (destlen == srclen) {
675         cc = 0;
676     } else if (destlen < srclen) {
677         cc = 1;
678     } else {
679         cc = 2;
680     }
681
682     if (srclen > destlen) {
683         srclen = destlen;
684     }
685
686     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
687         v = cpu_ldub_data(env, src);
688         cpu_stb_data(env, dest, v);
689     }
690
691     for (; destlen; dest++, destlen--) {
692         cpu_stb_data(env, dest, pad);
693     }
694
695     env->regs[r1 + 1] = destlen;
696     /* can't use srclen here, we trunc'ed it */
697     /* FIXME: 31-bit mode! */
698     env->regs[r3 + 1] -= src - env->regs[r3];
699     env->regs[r1] = dest;
700     env->regs[r3] = src;
701
702     return cc;
703 }
704
705 /* compare logical long extended memcompare insn with padding */
706 uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
707                        uint32_t r3)
708 {
709     uint64_t destlen = env->regs[r1 + 1];
710     uint64_t dest = get_address_31fix(env, r1);
711     uint64_t srclen = env->regs[r3 + 1];
712     uint64_t src = get_address_31fix(env, r3);
713     uint8_t pad = a2 & 0xff;
714     uint8_t v1 = 0, v2 = 0;
715     uint32_t cc = 0;
716
717     if (!(destlen || srclen)) {
718         return cc;
719     }
720
721     if (srclen > destlen) {
722         srclen = destlen;
723     }
724
725     for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
726         v1 = srclen ? cpu_ldub_data(env, src) : pad;
727         v2 = destlen ? cpu_ldub_data(env, dest) : pad;
728         if (v1 != v2) {
729             cc = (v1 < v2) ? 1 : 2;
730             break;
731         }
732     }
733
734     env->regs[r1 + 1] = destlen;
735     /* can't use srclen here, we trunc'ed it */
736     env->regs[r3 + 1] -= src - env->regs[r3];
737     env->regs[r1] = dest;
738     env->regs[r3] = src;
739
740     return cc;
741 }
742
743 /* checksum */
744 uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
745                       uint64_t src, uint64_t src_len)
746 {
747     uint64_t max_len, len;
748     uint64_t cksm = (uint32_t)r1;
749
750     /* Lest we fail to service interrupts in a timely manner, limit the
751        amount of work we're willing to do.  For now, lets cap at 8k.  */
752     max_len = (src_len > 0x2000 ? 0x2000 : src_len);
753
754     /* Process full words as available.  */
755     for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
756         cksm += (uint32_t)cpu_ldl_data(env, src);
757     }
758
759     switch (max_len - len) {
760     case 1:
761         cksm += cpu_ldub_data(env, src) << 24;
762         len += 1;
763         break;
764     case 2:
765         cksm += cpu_lduw_data(env, src) << 16;
766         len += 2;
767         break;
768     case 3:
769         cksm += cpu_lduw_data(env, src) << 16;
770         cksm += cpu_ldub_data(env, src + 2) << 8;
771         len += 3;
772         break;
773     }
774
775     /* Fold the carry from the checksum.  Note that we can see carry-out
776        during folding more than once (but probably not more than twice).  */
777     while (cksm > 0xffffffffull) {
778         cksm = (uint32_t)cksm + (cksm >> 32);
779     }
780
781     /* Indicate whether or not we've processed everything.  */
782     env->cc_op = (len == src_len ? 0 : 3);
783
784     /* Return both cksm and processed length.  */
785     env->retxl = cksm;
786     return len;
787 }
788
789 void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
790                   uint64_t src)
791 {
792     int len_dest = len >> 4;
793     int len_src = len & 0xf;
794     uint8_t b;
795     int second_nibble = 0;
796
797     dest += len_dest;
798     src += len_src;
799
800     /* last byte is special, it only flips the nibbles */
801     b = cpu_ldub_data(env, src);
802     cpu_stb_data(env, dest, (b << 4) | (b >> 4));
803     src--;
804     len_src--;
805
806     /* now pad every nibble with 0xf0 */
807
808     while (len_dest > 0) {
809         uint8_t cur_byte = 0;
810
811         if (len_src > 0) {
812             cur_byte = cpu_ldub_data(env, src);
813         }
814
815         len_dest--;
816         dest--;
817
818         /* only advance one nibble at a time */
819         if (second_nibble) {
820             cur_byte >>= 4;
821             len_src--;
822             src--;
823         }
824         second_nibble = !second_nibble;
825
826         /* digit */
827         cur_byte = (cur_byte & 0xf);
828         /* zone bits */
829         cur_byte |= 0xf0;
830
831         cpu_stb_data(env, dest, cur_byte);
832     }
833 }
834
835 void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
836                 uint64_t trans)
837 {
838     int i;
839
840     for (i = 0; i <= len; i++) {
841         uint8_t byte = cpu_ldub_data(env, array + i);
842         uint8_t new_byte = cpu_ldub_data(env, trans + byte);
843
844         cpu_stb_data(env, array + i, new_byte);
845     }
846 }
847
848 #if !defined(CONFIG_USER_ONLY)
849 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
850 {
851     int i;
852     uint64_t src = a2;
853
854     for (i = r1;; i = (i + 1) % 16) {
855         env->cregs[i] = cpu_ldq_data(env, src);
856         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
857                    i, src, env->cregs[i]);
858         src += sizeof(uint64_t);
859
860         if (i == r3) {
861             break;
862         }
863     }
864
865     tlb_flush(env, 1);
866 }
867
868 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
869 {
870     int i;
871     uint64_t src = a2;
872
873     for (i = r1;; i = (i + 1) % 16) {
874         env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
875             cpu_ldl_data(env, src);
876         src += sizeof(uint32_t);
877
878         if (i == r3) {
879             break;
880         }
881     }
882
883     tlb_flush(env, 1);
884 }
885
886 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
887 {
888     int i;
889     uint64_t dest = a2;
890
891     for (i = r1;; i = (i + 1) % 16) {
892         cpu_stq_data(env, dest, env->cregs[i]);
893         dest += sizeof(uint64_t);
894
895         if (i == r3) {
896             break;
897         }
898     }
899 }
900
901 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
902 {
903     int i;
904     uint64_t dest = a2;
905
906     for (i = r1;; i = (i + 1) % 16) {
907         cpu_stl_data(env, dest, env->cregs[i]);
908         dest += sizeof(uint32_t);
909
910         if (i == r3) {
911             break;
912         }
913     }
914 }
915
916 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
917 {
918     /* XXX implement */
919
920     return 0;
921 }
922
923 /* insert storage key extended */
924 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
925 {
926     uint64_t addr = get_address(env, 0, 0, r2);
927
928     if (addr > ram_size) {
929         return 0;
930     }
931
932     return env->storage_keys[addr / TARGET_PAGE_SIZE];
933 }
934
935 /* set storage key extended */
936 void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
937 {
938     uint64_t addr = get_address(env, 0, 0, r2);
939
940     if (addr > ram_size) {
941         return;
942     }
943
944     env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
945 }
946
947 /* reset reference bit extended */
948 uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
949 {
950     uint8_t re;
951     uint8_t key;
952
953     if (r2 > ram_size) {
954         return 0;
955     }
956
957     key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
958     re = key & (SK_R | SK_C);
959     env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
960
961     /*
962      * cc
963      *
964      * 0  Reference bit zero; change bit zero
965      * 1  Reference bit zero; change bit one
966      * 2  Reference bit one; change bit zero
967      * 3  Reference bit one; change bit one
968      */
969
970     return re >> 1;
971 }
972
973 /* compare and swap and purge */
974 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
975 {
976     uint32_t cc;
977     uint32_t o1 = env->regs[r1];
978     uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
979     uint32_t o2 = cpu_ldl_data(env, a2);
980
981     if (o1 == o2) {
982         cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
983         if (env->regs[r2] & 0x3) {
984             /* flush TLB / ALB */
985             tlb_flush(env, 1);
986         }
987         cc = 0;
988     } else {
989         env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
990         cc = 1;
991     }
992
993     return cc;
994 }
995
996 static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
997                         uint64_t mode1, uint64_t a2, uint64_t mode2)
998 {
999     target_ulong src, dest;
1000     int flags, cc = 0, i;
1001
1002     if (!l) {
1003         return 0;
1004     } else if (l > 256) {
1005         /* max 256 */
1006         l = 256;
1007         cc = 3;
1008     }
1009
1010     if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1011         cpu_loop_exit(env);
1012     }
1013     dest |= a1 & ~TARGET_PAGE_MASK;
1014
1015     if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1016         cpu_loop_exit(env);
1017     }
1018     src |= a2 & ~TARGET_PAGE_MASK;
1019
1020     /* XXX replace w/ memcpy */
1021     for (i = 0; i < l; i++) {
1022         /* XXX be more clever */
1023         if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1024             (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
1025             mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1026             break;
1027         }
1028         stb_phys(dest + i, ldub_phys(src + i));
1029     }
1030
1031     return cc;
1032 }
1033
1034 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1035 {
1036     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1037                __func__, l, a1, a2);
1038
1039     return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1040 }
1041
1042 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1043 {
1044     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1045                __func__, l, a1, a2);
1046
1047     return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1048 }
1049
1050 /* invalidate pte */
1051 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1052 {
1053     uint64_t page = vaddr & TARGET_PAGE_MASK;
1054     uint64_t pte = 0;
1055
1056     /* XXX broadcast to other CPUs */
1057
1058     /* XXX Linux is nice enough to give us the exact pte address.
1059        According to spec we'd have to find it out ourselves */
1060     /* XXX Linux is fine with overwriting the pte, the spec requires
1061        us to only set the invalid bit */
1062     stq_phys(pte_addr, pte | _PAGE_INVALID);
1063
1064     /* XXX we exploit the fact that Linux passes the exact virtual
1065        address here - it's not obliged to! */
1066     tlb_flush_page(env, page);
1067
1068     /* XXX 31-bit hack */
1069     if (page & 0x80000000) {
1070         tlb_flush_page(env, page & ~0x80000000);
1071     } else {
1072         tlb_flush_page(env, page | 0x80000000);
1073     }
1074 }
1075
1076 /* flush local tlb */
1077 void HELPER(ptlb)(CPUS390XState *env)
1078 {
1079     tlb_flush(env, 1);
1080 }
1081
1082 /* store using real address */
1083 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1084 {
1085     stw_phys(get_address(env, 0, 0, addr), v1);
1086 }
1087
1088 /* load real address */
1089 uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1090 {
1091     uint32_t cc = 0;
1092     int old_exc = env->exception_index;
1093     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1094     uint64_t ret;
1095     int flags;
1096
1097     /* XXX incomplete - has more corner cases */
1098     if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1099         program_interrupt(env, PGM_SPECIAL_OP, 2);
1100     }
1101
1102     env->exception_index = old_exc;
1103     if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1104         cc = 3;
1105     }
1106     if (env->exception_index == EXCP_PGM) {
1107         ret = env->int_pgm_code | 0x80000000;
1108     } else {
1109         ret |= addr & ~TARGET_PAGE_MASK;
1110     }
1111     env->exception_index = old_exc;
1112
1113     env->cc_op = cc;
1114     return ret;
1115 }
1116 #endif