]> git.proxmox.com Git - mirror_qemu.git/blob - target-s390x/mem_helper.c
exec: Change cpu_abort() argument to CPUState
[mirror_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 int i;
814 uint64_t src = a2;
815
816 for (i = r1;; i = (i + 1) % 16) {
817 env->cregs[i] = cpu_ldq_data(env, src);
818 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
819 i, src, env->cregs[i]);
820 src += sizeof(uint64_t);
821
822 if (i == r3) {
823 break;
824 }
825 }
826
827 tlb_flush(env, 1);
828 }
829
830 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
831 {
832 int i;
833 uint64_t src = a2;
834
835 for (i = r1;; i = (i + 1) % 16) {
836 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
837 cpu_ldl_data(env, src);
838 src += sizeof(uint32_t);
839
840 if (i == r3) {
841 break;
842 }
843 }
844
845 tlb_flush(env, 1);
846 }
847
848 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
849 {
850 int i;
851 uint64_t dest = a2;
852
853 for (i = r1;; i = (i + 1) % 16) {
854 cpu_stq_data(env, dest, env->cregs[i]);
855 dest += sizeof(uint64_t);
856
857 if (i == r3) {
858 break;
859 }
860 }
861 }
862
863 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
864 {
865 int i;
866 uint64_t dest = a2;
867
868 for (i = r1;; i = (i + 1) % 16) {
869 cpu_stl_data(env, dest, env->cregs[i]);
870 dest += sizeof(uint32_t);
871
872 if (i == r3) {
873 break;
874 }
875 }
876 }
877
878 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
879 {
880 /* XXX implement */
881
882 return 0;
883 }
884
885 /* insert storage key extended */
886 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
887 {
888 uint64_t addr = get_address(env, 0, 0, r2);
889
890 if (addr > ram_size) {
891 return 0;
892 }
893
894 return env->storage_keys[addr / TARGET_PAGE_SIZE];
895 }
896
897 /* set storage key extended */
898 void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
899 {
900 uint64_t addr = get_address(env, 0, 0, r2);
901
902 if (addr > ram_size) {
903 return;
904 }
905
906 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
907 }
908
909 /* reset reference bit extended */
910 uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
911 {
912 uint8_t re;
913 uint8_t key;
914
915 if (r2 > ram_size) {
916 return 0;
917 }
918
919 key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
920 re = key & (SK_R | SK_C);
921 env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
922
923 /*
924 * cc
925 *
926 * 0 Reference bit zero; change bit zero
927 * 1 Reference bit zero; change bit one
928 * 2 Reference bit one; change bit zero
929 * 3 Reference bit one; change bit one
930 */
931
932 return re >> 1;
933 }
934
935 /* compare and swap and purge */
936 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
937 {
938 uint32_t cc;
939 uint32_t o1 = env->regs[r1];
940 uint64_t a2 = r2 & ~3ULL;
941 uint32_t o2 = cpu_ldl_data(env, a2);
942
943 if (o1 == o2) {
944 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
945 if (r2 & 0x3) {
946 /* flush TLB / ALB */
947 tlb_flush(env, 1);
948 }
949 cc = 0;
950 } else {
951 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
952 cc = 1;
953 }
954
955 return cc;
956 }
957
958 static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
959 uint64_t mode1, uint64_t a2, uint64_t mode2)
960 {
961 CPUState *cs = CPU(s390_env_get_cpu(env));
962 target_ulong src, dest;
963 int flags, cc = 0, i;
964
965 if (!l) {
966 return 0;
967 } else if (l > 256) {
968 /* max 256 */
969 l = 256;
970 cc = 3;
971 }
972
973 if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
974 cpu_loop_exit(CPU(s390_env_get_cpu(env)));
975 }
976 dest |= a1 & ~TARGET_PAGE_MASK;
977
978 if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
979 cpu_loop_exit(CPU(s390_env_get_cpu(env)));
980 }
981 src |= a2 & ~TARGET_PAGE_MASK;
982
983 /* XXX replace w/ memcpy */
984 for (i = 0; i < l; i++) {
985 /* XXX be more clever */
986 if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
987 (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
988 mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
989 break;
990 }
991 stb_phys(cs->as, dest + i, ldub_phys(cs->as, src + i));
992 }
993
994 return cc;
995 }
996
997 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
998 {
999 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1000 __func__, l, a1, a2);
1001
1002 return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1003 }
1004
1005 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1006 {
1007 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1008 __func__, l, a1, a2);
1009
1010 return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1011 }
1012
1013 /* invalidate pte */
1014 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1015 {
1016 CPUState *cs = CPU(s390_env_get_cpu(env));
1017 uint64_t page = vaddr & TARGET_PAGE_MASK;
1018 uint64_t pte = 0;
1019
1020 /* XXX broadcast to other CPUs */
1021
1022 /* XXX Linux is nice enough to give us the exact pte address.
1023 According to spec we'd have to find it out ourselves */
1024 /* XXX Linux is fine with overwriting the pte, the spec requires
1025 us to only set the invalid bit */
1026 stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
1027
1028 /* XXX we exploit the fact that Linux passes the exact virtual
1029 address here - it's not obliged to! */
1030 tlb_flush_page(env, page);
1031
1032 /* XXX 31-bit hack */
1033 if (page & 0x80000000) {
1034 tlb_flush_page(env, page & ~0x80000000);
1035 } else {
1036 tlb_flush_page(env, page | 0x80000000);
1037 }
1038 }
1039
1040 /* flush local tlb */
1041 void HELPER(ptlb)(CPUS390XState *env)
1042 {
1043 tlb_flush(env, 1);
1044 }
1045
1046 /* store using real address */
1047 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1048 {
1049 CPUState *cs = CPU(s390_env_get_cpu(env));
1050
1051 stw_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
1052 }
1053
1054 /* load real address */
1055 uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1056 {
1057 CPUState *cs = CPU(s390_env_get_cpu(env));
1058 uint32_t cc = 0;
1059 int old_exc = cs->exception_index;
1060 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1061 uint64_t ret;
1062 int flags;
1063
1064 /* XXX incomplete - has more corner cases */
1065 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1066 program_interrupt(env, PGM_SPECIAL_OP, 2);
1067 }
1068
1069 cs->exception_index = old_exc;
1070 if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1071 cc = 3;
1072 }
1073 if (cs->exception_index == EXCP_PGM) {
1074 ret = env->int_pgm_code | 0x80000000;
1075 } else {
1076 ret |= addr & ~TARGET_PAGE_MASK;
1077 }
1078 cs->exception_index = old_exc;
1079
1080 env->cc_op = cc;
1081 return ret;
1082 }
1083 #endif