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