]> Git Repo - qemu.git/blob - target-s390x/mem_helper.c
Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging
[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, true)) {
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, true)) {
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, true)) {
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     /* mvc and memmove do not behave the same when areas overlap! */
217     } else if ((dest < src) || (src + l < dest)) {
218         memmove(g2h(dest), g2h(src), l + 1);
219         return;
220     }
221 #endif
222
223     /* handle the parts that fit into 8-byte loads/stores */
224     if ((dest + 8 <= src) || (src + 8 <= dest)) {
225         for (i = 0; i < l_64; i++) {
226             cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
227             x += 8;
228         }
229     }
230
231     /* slow version with byte accesses which always work */
232     for (i = x; i <= l; i++) {
233         cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
234     }
235 }
236
237 /* compare unsigned byte arrays */
238 uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
239 {
240     int i;
241     unsigned char x, y;
242     uint32_t cc;
243
244     HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
245                __func__, l, s1, s2);
246     for (i = 0; i <= l; i++) {
247         x = cpu_ldub_data(env, s1 + i);
248         y = cpu_ldub_data(env, s2 + i);
249         HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
250         if (x < y) {
251             cc = 1;
252             goto done;
253         } else if (x > y) {
254             cc = 2;
255             goto done;
256         }
257     }
258     cc = 0;
259  done:
260     HELPER_LOG("\n");
261     return cc;
262 }
263
264 /* compare logical under mask */
265 uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
266                      uint64_t addr)
267 {
268     uint8_t r, d;
269     uint32_t cc;
270
271     HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
272                mask, addr);
273     cc = 0;
274     while (mask) {
275         if (mask & 8) {
276             d = cpu_ldub_data(env, addr);
277             r = (r1 & 0xff000000UL) >> 24;
278             HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
279                        addr);
280             if (r < d) {
281                 cc = 1;
282                 break;
283             } else if (r > d) {
284                 cc = 2;
285                 break;
286             }
287             addr++;
288         }
289         mask = (mask << 1) & 0xf;
290         r1 <<= 8;
291     }
292     HELPER_LOG("\n");
293     return cc;
294 }
295
296 static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
297 {
298     /* 31-Bit mode */
299     if (!(env->psw.mask & PSW_MASK_64)) {
300         a &= 0x7fffffff;
301     }
302     return a;
303 }
304
305 static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
306 {
307     uint64_t r = d2;
308     if (x2) {
309         r += env->regs[x2];
310     }
311     if (b2) {
312         r += env->regs[b2];
313     }
314     return fix_address(env, r);
315 }
316
317 static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
318 {
319     return fix_address(env, env->regs[reg]);
320 }
321
322 /* search string (c is byte to search, r2 is string, r1 end of string) */
323 uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
324                       uint64_t str)
325 {
326     uint32_t len;
327     uint8_t v, c = r0;
328
329     str = fix_address(env, str);
330     end = fix_address(env, end);
331
332     /* Assume for now that R2 is unmodified.  */
333     env->retxl = str;
334
335     /* Lest we fail to service interrupts in a timely manner, limit the
336        amount of work we're willing to do.  For now, let's cap at 8k.  */
337     for (len = 0; len < 0x2000; ++len) {
338         if (str + len == end) {
339             /* Character not found.  R1 & R2 are unmodified.  */
340             env->cc_op = 2;
341             return end;
342         }
343         v = cpu_ldub_data(env, str + len);
344         if (v == c) {
345             /* Character found.  Set R1 to the location; R2 is unmodified.  */
346             env->cc_op = 1;
347             return str + len;
348         }
349     }
350
351     /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
352     env->retxl = str + len;
353     env->cc_op = 3;
354     return end;
355 }
356
357 /* unsigned string compare (c is string terminator) */
358 uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
359 {
360     uint32_t len;
361
362     c = c & 0xff;
363     s1 = fix_address(env, s1);
364     s2 = fix_address(env, s2);
365
366     /* Lest we fail to service interrupts in a timely manner, limit the
367        amount of work we're willing to do.  For now, let's cap at 8k.  */
368     for (len = 0; len < 0x2000; ++len) {
369         uint8_t v1 = cpu_ldub_data(env, s1 + len);
370         uint8_t v2 = cpu_ldub_data(env, s2 + len);
371         if (v1 == v2) {
372             if (v1 == c) {
373                 /* Equal.  CC=0, and don't advance the registers.  */
374                 env->cc_op = 0;
375                 env->retxl = s2;
376                 return s1;
377             }
378         } else {
379             /* Unequal.  CC={1,2}, and advance the registers.  Note that
380                the terminator need not be zero, but the string that contains
381                the terminator is by definition "low".  */
382             env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
383             env->retxl = s2 + len;
384             return s1 + len;
385         }
386     }
387
388     /* CPU-determined bytes equal; advance the registers.  */
389     env->cc_op = 3;
390     env->retxl = s2 + len;
391     return s1 + len;
392 }
393
394 /* move page */
395 void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
396 {
397     /* XXX missing r0 handling */
398     env->cc_op = 0;
399 #ifdef CONFIG_USER_ONLY
400     memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
401 #else
402     mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
403 #endif
404 }
405
406 /* string copy (c is string terminator) */
407 uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
408 {
409     uint32_t len;
410
411     c = c & 0xff;
412     d = fix_address(env, d);
413     s = fix_address(env, s);
414
415     /* Lest we fail to service interrupts in a timely manner, limit the
416        amount of work we're willing to do.  For now, let's cap at 8k.  */
417     for (len = 0; len < 0x2000; ++len) {
418         uint8_t v = cpu_ldub_data(env, s + len);
419         cpu_stb_data(env, d + len, v);
420         if (v == c) {
421             /* Complete.  Set CC=1 and advance R1.  */
422             env->cc_op = 1;
423             env->retxl = s;
424             return d + len;
425         }
426     }
427
428     /* Incomplete.  Set CC=3 and signal to advance R1 and R2.  */
429     env->cc_op = 3;
430     env->retxl = s + len;
431     return d + len;
432 }
433
434 static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
435                            uint32_t mask)
436 {
437     int pos = 24; /* top of the lower half of r1 */
438     uint64_t rmask = 0xff000000ULL;
439     uint8_t val = 0;
440     int ccd = 0;
441     uint32_t cc = 0;
442
443     while (mask) {
444         if (mask & 8) {
445             env->regs[r1] &= ~rmask;
446             val = cpu_ldub_data(env, address);
447             if ((val & 0x80) && !ccd) {
448                 cc = 1;
449             }
450             ccd = 1;
451             if (val && cc == 0) {
452                 cc = 2;
453             }
454             env->regs[r1] |= (uint64_t)val << pos;
455             address++;
456         }
457         mask = (mask << 1) & 0xf;
458         pos -= 8;
459         rmask >>= 8;
460     }
461
462     return cc;
463 }
464
465 /* execute instruction
466    this instruction executes an insn modified with the contents of r1
467    it does not change the executed instruction in memory
468    it does not change the program counter
469    in other words: tricky...
470    currently implemented by interpreting the cases it is most commonly used in
471 */
472 uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
473                     uint64_t addr, uint64_t ret)
474 {
475     S390CPU *cpu = s390_env_get_cpu(env);
476     uint16_t insn = cpu_lduw_code(env, addr);
477
478     HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
479                insn);
480     if ((insn & 0xf0ff) == 0xd000) {
481         uint32_t l, insn2, b1, b2, d1, d2;
482
483         l = v1 & 0xff;
484         insn2 = cpu_ldl_code(env, addr + 2);
485         b1 = (insn2 >> 28) & 0xf;
486         b2 = (insn2 >> 12) & 0xf;
487         d1 = (insn2 >> 16) & 0xfff;
488         d2 = insn2 & 0xfff;
489         switch (insn & 0xf00) {
490         case 0x200:
491             helper_mvc(env, l, get_address(env, 0, b1, d1),
492                        get_address(env, 0, b2, d2));
493             break;
494         case 0x400:
495             cc = helper_nc(env, l, get_address(env, 0, b1, d1),
496                             get_address(env, 0, b2, d2));
497             break;
498         case 0x500:
499             cc = helper_clc(env, l, get_address(env, 0, b1, d1),
500                             get_address(env, 0, b2, d2));
501             break;
502         case 0x600:
503             cc = helper_oc(env, l, get_address(env, 0, b1, d1),
504                             get_address(env, 0, b2, d2));
505             break;
506         case 0x700:
507             cc = helper_xc(env, l, get_address(env, 0, b1, d1),
508                            get_address(env, 0, b2, d2));
509             break;
510         case 0xc00:
511             helper_tr(env, l, get_address(env, 0, b1, d1),
512                       get_address(env, 0, b2, d2));
513         case 0xd00:
514             cc = helper_trt(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(CPU(cpu), "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 uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
809                      uint64_t len, uint64_t trans)
810 {
811     uint8_t end = env->regs[0] & 0xff;
812     uint64_t l = len;
813     uint64_t i;
814
815     if (!(env->psw.mask & PSW_MASK_64)) {
816         array &= 0x7fffffff;
817         l = (uint32_t)l;
818     }
819
820     /* Lest we fail to service interrupts in a timely manner, limit the
821        amount of work we're willing to do.  For now, let's cap at 8k.  */
822     if (l > 0x2000) {
823         l = 0x2000;
824         env->cc_op = 3;
825     } else {
826         env->cc_op = 0;
827     }
828
829     for (i = 0; i < l; i++) {
830         uint8_t byte, new_byte;
831
832         byte = cpu_ldub_data(env, array + i);
833
834         if (byte == end) {
835             env->cc_op = 1;
836             break;
837         }
838
839         new_byte = cpu_ldub_data(env, trans + byte);
840         cpu_stb_data(env, array + i, new_byte);
841     }
842
843     env->retxl = len - i;
844     return array + i;
845 }
846
847 uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
848                      uint64_t trans)
849 {
850     uint32_t cc = 0;
851     int i;
852
853     for (i = 0; i <= len; i++) {
854         uint8_t byte = cpu_ldub_data(env, array + i);
855         uint8_t sbyte = cpu_ldub_data(env, trans + byte);
856
857         if (sbyte != 0) {
858             env->regs[1] = array + i;
859             env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
860             cc = (i == len) ? 2 : 1;
861             break;
862         }
863     }
864
865     return cc;
866 }
867
868 #if !defined(CONFIG_USER_ONLY)
869 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
870 {
871     S390CPU *cpu = s390_env_get_cpu(env);
872     int i;
873     uint64_t src = a2;
874
875     for (i = r1;; i = (i + 1) % 16) {
876         env->cregs[i] = cpu_ldq_data(env, src);
877         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
878                    i, src, env->cregs[i]);
879         src += sizeof(uint64_t);
880
881         if (i == r3) {
882             break;
883         }
884     }
885
886     tlb_flush(CPU(cpu), 1);
887 }
888
889 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
890 {
891     S390CPU *cpu = s390_env_get_cpu(env);
892     int i;
893     uint64_t src = a2;
894
895     for (i = r1;; i = (i + 1) % 16) {
896         env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
897             cpu_ldl_data(env, src);
898         src += sizeof(uint32_t);
899
900         if (i == r3) {
901             break;
902         }
903     }
904
905     tlb_flush(CPU(cpu), 1);
906 }
907
908 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
909 {
910     int i;
911     uint64_t dest = a2;
912
913     for (i = r1;; i = (i + 1) % 16) {
914         cpu_stq_data(env, dest, env->cregs[i]);
915         dest += sizeof(uint64_t);
916
917         if (i == r3) {
918             break;
919         }
920     }
921 }
922
923 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
924 {
925     int i;
926     uint64_t dest = a2;
927
928     for (i = r1;; i = (i + 1) % 16) {
929         cpu_stl_data(env, dest, env->cregs[i]);
930         dest += sizeof(uint32_t);
931
932         if (i == r3) {
933             break;
934         }
935     }
936 }
937
938 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
939 {
940     /* XXX implement */
941
942     return 0;
943 }
944
945 /* insert storage key extended */
946 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
947 {
948     uint64_t addr = get_address(env, 0, 0, r2);
949
950     if (addr > ram_size) {
951         return 0;
952     }
953
954     return env->storage_keys[addr / TARGET_PAGE_SIZE];
955 }
956
957 /* set storage key extended */
958 void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
959 {
960     uint64_t addr = get_address(env, 0, 0, r2);
961
962     if (addr > ram_size) {
963         return;
964     }
965
966     env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
967 }
968
969 /* reset reference bit extended */
970 uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
971 {
972     uint8_t re;
973     uint8_t key;
974
975     if (r2 > ram_size) {
976         return 0;
977     }
978
979     key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
980     re = key & (SK_R | SK_C);
981     env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
982
983     /*
984      * cc
985      *
986      * 0  Reference bit zero; change bit zero
987      * 1  Reference bit zero; change bit one
988      * 2  Reference bit one; change bit zero
989      * 3  Reference bit one; change bit one
990      */
991
992     return re >> 1;
993 }
994
995 /* compare and swap and purge */
996 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
997 {
998     S390CPU *cpu = s390_env_get_cpu(env);
999     uint32_t cc;
1000     uint32_t o1 = env->regs[r1];
1001     uint64_t a2 = r2 & ~3ULL;
1002     uint32_t o2 = cpu_ldl_data(env, a2);
1003
1004     if (o1 == o2) {
1005         cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1006         if (r2 & 0x3) {
1007             /* flush TLB / ALB */
1008             tlb_flush(CPU(cpu), 1);
1009         }
1010         cc = 0;
1011     } else {
1012         env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1013         cc = 1;
1014     }
1015
1016     return cc;
1017 }
1018
1019 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1020 {
1021     int cc = 0, i;
1022
1023     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1024                __func__, l, a1, a2);
1025
1026     if (l > 256) {
1027         /* max 256 */
1028         l = 256;
1029         cc = 3;
1030     }
1031
1032     /* XXX replace w/ memcpy */
1033     for (i = 0; i < l; i++) {
1034         cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
1035     }
1036
1037     return cc;
1038 }
1039
1040 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1041 {
1042     int cc = 0, i;
1043
1044     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1045                __func__, l, a1, a2);
1046
1047     if (l > 256) {
1048         /* max 256 */
1049         l = 256;
1050         cc = 3;
1051     }
1052
1053     /* XXX replace w/ memcpy */
1054     for (i = 0; i < l; i++) {
1055         cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
1056     }
1057
1058     return cc;
1059 }
1060
1061 /* invalidate pte */
1062 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1063 {
1064     CPUState *cs = CPU(s390_env_get_cpu(env));
1065     uint64_t page = vaddr & TARGET_PAGE_MASK;
1066     uint64_t pte = 0;
1067
1068     /* XXX broadcast to other CPUs */
1069
1070     /* XXX Linux is nice enough to give us the exact pte address.
1071        According to spec we'd have to find it out ourselves */
1072     /* XXX Linux is fine with overwriting the pte, the spec requires
1073        us to only set the invalid bit */
1074     stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
1075
1076     /* XXX we exploit the fact that Linux passes the exact virtual
1077        address here - it's not obliged to! */
1078     tlb_flush_page(cs, page);
1079
1080     /* XXX 31-bit hack */
1081     if (page & 0x80000000) {
1082         tlb_flush_page(cs, page & ~0x80000000);
1083     } else {
1084         tlb_flush_page(cs, page | 0x80000000);
1085     }
1086 }
1087
1088 /* flush local tlb */
1089 void HELPER(ptlb)(CPUS390XState *env)
1090 {
1091     S390CPU *cpu = s390_env_get_cpu(env);
1092
1093     tlb_flush(CPU(cpu), 1);
1094 }
1095
1096 /* load using real address */
1097 uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
1098 {
1099     CPUState *cs = CPU(s390_env_get_cpu(env));
1100
1101     return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr));
1102 }
1103
1104 uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr)
1105 {
1106     CPUState *cs = CPU(s390_env_get_cpu(env));
1107
1108     return ldq_phys(cs->as, get_address(env, 0, 0, addr));
1109 }
1110
1111 /* store using real address */
1112 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1113 {
1114     CPUState *cs = CPU(s390_env_get_cpu(env));
1115
1116     stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
1117 }
1118
1119 void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1120 {
1121     CPUState *cs = CPU(s390_env_get_cpu(env));
1122
1123     stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
1124 }
1125
1126 /* load real address */
1127 uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1128 {
1129     CPUState *cs = CPU(s390_env_get_cpu(env));
1130     uint32_t cc = 0;
1131     int old_exc = cs->exception_index;
1132     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1133     uint64_t ret;
1134     int flags;
1135
1136     /* XXX incomplete - has more corner cases */
1137     if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1138         program_interrupt(env, PGM_SPECIAL_OP, 2);
1139     }
1140
1141     cs->exception_index = old_exc;
1142     if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
1143         cc = 3;
1144     }
1145     if (cs->exception_index == EXCP_PGM) {
1146         ret = env->int_pgm_code | 0x80000000;
1147     } else {
1148         ret |= addr & ~TARGET_PAGE_MASK;
1149     }
1150     cs->exception_index = old_exc;
1151
1152     env->cc_op = cc;
1153     return ret;
1154 }
1155 #endif
This page took 0.087594 seconds and 4 git commands to generate.