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