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