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