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