2 * S/390 memory access helper routines
4 * Copyright (c) 2009 Ulrich Hecht
5 * Copyright (c) 2009 Alexander Graf
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.
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.
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/>.
22 #include "exec/helper-proto.h"
23 #include "exec/cpu_ldst.h"
25 /*****************************************************************************/
27 #if !defined(CONFIG_USER_ONLY)
29 /* try to fill the TLB and return an exception if error. If retaddr is
30 NULL, it means that the function was called in C code (i.e. not
31 from generated code or from helper.c) */
32 /* XXX: fix it to restore all registers */
33 void tlb_fill(CPUState
*cs
, target_ulong addr
, int is_write
, int mmu_idx
,
38 ret
= s390_cpu_handle_mmu_fault(cs
, addr
, is_write
, mmu_idx
);
39 if (unlikely(ret
!= 0)) {
40 if (likely(retaddr
)) {
41 /* now we have a real cpu fault */
42 cpu_restore_state(cs
, retaddr
);
50 /* #define DEBUG_HELPER */
52 #define HELPER_LOG(x...) qemu_log(x)
54 #define HELPER_LOG(x...)
57 #ifndef CONFIG_USER_ONLY
58 static void mvc_fast_memset(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
61 S390CPU
*cpu
= s390_env_get_cpu(env
);
65 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
68 if (mmu_translate(env
, dest
, 1, asc
, &dest_phys
, &flags
, true)) {
69 cpu_stb_data(env
, dest
, byte
);
70 cpu_abort(CPU(cpu
), "should never reach here");
72 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
74 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
76 memset(dest_p
, byte
, len
);
78 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
81 static void mvc_fast_memmove(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
84 S390CPU
*cpu
= s390_env_get_cpu(env
);
90 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
93 if (mmu_translate(env
, dest
, 1, asc
, &dest_phys
, &flags
, true)) {
94 cpu_stb_data(env
, dest
, 0);
95 cpu_abort(CPU(cpu
), "should never reach here");
97 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
99 if (mmu_translate(env
, src
, 0, asc
, &src_phys
, &flags
, true)) {
100 cpu_ldub_data(env
, src
);
101 cpu_abort(CPU(cpu
), "should never reach here");
103 src_phys
|= src
& ~TARGET_PAGE_MASK
;
105 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
106 src_p
= cpu_physical_memory_map(src_phys
, &len
, 0);
108 memmove(dest_p
, src_p
, len
);
110 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
111 cpu_physical_memory_unmap(src_p
, 0, len
, len
);
116 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
123 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
124 __func__
, l
, dest
, src
);
125 for (i
= 0; i
<= l
; i
++) {
126 x
= cpu_ldub_data(env
, dest
+ i
) & cpu_ldub_data(env
, src
+ i
);
130 cpu_stb_data(env
, dest
+ i
, x
);
136 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
143 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
144 __func__
, l
, dest
, src
);
146 #ifndef CONFIG_USER_ONLY
147 /* xor with itself is the same as memset(0) */
148 if ((l
> 32) && (src
== dest
) &&
149 (src
& TARGET_PAGE_MASK
) == ((src
+ l
) & TARGET_PAGE_MASK
)) {
150 mvc_fast_memset(env
, l
+ 1, dest
, 0);
155 memset(g2h(dest
), 0, l
+ 1);
160 for (i
= 0; i
<= l
; i
++) {
161 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
165 cpu_stb_data(env
, dest
+ i
, x
);
171 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
178 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
179 __func__
, l
, dest
, src
);
180 for (i
= 0; i
<= l
; i
++) {
181 x
= cpu_ldub_data(env
, dest
+ i
) | cpu_ldub_data(env
, src
+ i
);
185 cpu_stb_data(env
, dest
+ i
, x
);
191 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
195 uint32_t l_64
= (l
+ 1) / 8;
197 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
198 __func__
, l
, dest
, src
);
200 #ifndef CONFIG_USER_ONLY
202 (src
& TARGET_PAGE_MASK
) == ((src
+ l
) & TARGET_PAGE_MASK
) &&
203 (dest
& TARGET_PAGE_MASK
) == ((dest
+ l
) & TARGET_PAGE_MASK
)) {
204 if (dest
== (src
+ 1)) {
205 mvc_fast_memset(env
, l
+ 1, dest
, cpu_ldub_data(env
, src
));
207 } else if ((src
& TARGET_PAGE_MASK
) != (dest
& TARGET_PAGE_MASK
)) {
208 mvc_fast_memmove(env
, l
+ 1, dest
, src
);
213 if (dest
== (src
+ 1)) {
214 memset(g2h(dest
), cpu_ldub_data(env
, src
), l
+ 1);
217 memmove(g2h(dest
), g2h(src
), l
+ 1);
222 /* handle the parts that fit into 8-byte loads/stores */
223 if (dest
!= (src
+ 1)) {
224 for (i
= 0; i
< l_64
; i
++) {
225 cpu_stq_data(env
, dest
+ x
, cpu_ldq_data(env
, src
+ x
));
230 /* slow version crossing pages with byte accesses */
231 for (i
= x
; i
<= l
; i
++) {
232 cpu_stb_data(env
, dest
+ i
, cpu_ldub_data(env
, src
+ i
));
236 /* compare unsigned byte arrays */
237 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
243 HELPER_LOG("%s l %d s1 %" PRIx64
" s2 %" PRIx64
"\n",
244 __func__
, l
, s1
, s2
);
245 for (i
= 0; i
<= l
; i
++) {
246 x
= cpu_ldub_data(env
, s1
+ i
);
247 y
= cpu_ldub_data(env
, s2
+ i
);
248 HELPER_LOG("%02x (%c)/%02x (%c) ", x
, x
, y
, y
);
263 /* compare logical under mask */
264 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
270 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
275 d
= cpu_ldub_data(env
, addr
);
276 r
= (r1
& 0xff000000UL
) >> 24;
277 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64
") ", mask
, r
, d
,
288 mask
= (mask
<< 1) & 0xf;
295 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
298 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
304 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
313 return fix_address(env
, r
);
316 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
318 return fix_address(env
, env
->regs
[reg
]);
321 /* search string (c is byte to search, r2 is string, r1 end of string) */
322 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
328 str
= fix_address(env
, str
);
329 end
= fix_address(env
, end
);
331 /* Assume for now that R2 is unmodified. */
334 /* Lest we fail to service interrupts in a timely manner, limit the
335 amount of work we're willing to do. For now, let's cap at 8k. */
336 for (len
= 0; len
< 0x2000; ++len
) {
337 if (str
+ len
== end
) {
338 /* Character not found. R1 & R2 are unmodified. */
342 v
= cpu_ldub_data(env
, str
+ len
);
344 /* Character found. Set R1 to the location; R2 is unmodified. */
350 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
351 env
->retxl
= str
+ len
;
356 /* unsigned string compare (c is string terminator) */
357 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
362 s1
= fix_address(env
, s1
);
363 s2
= fix_address(env
, s2
);
365 /* Lest we fail to service interrupts in a timely manner, limit the
366 amount of work we're willing to do. For now, let's cap at 8k. */
367 for (len
= 0; len
< 0x2000; ++len
) {
368 uint8_t v1
= cpu_ldub_data(env
, s1
+ len
);
369 uint8_t v2
= cpu_ldub_data(env
, s2
+ len
);
372 /* Equal. CC=0, and don't advance the registers. */
378 /* Unequal. CC={1,2}, and advance the registers. Note that
379 the terminator need not be zero, but the string that contains
380 the terminator is by definition "low". */
381 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
382 env
->retxl
= s2
+ len
;
387 /* CPU-determined bytes equal; advance the registers. */
389 env
->retxl
= s2
+ len
;
394 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
396 /* XXX missing r0 handling */
398 #ifdef CONFIG_USER_ONLY
399 memmove(g2h(r1
), g2h(r2
), TARGET_PAGE_SIZE
);
401 mvc_fast_memmove(env
, TARGET_PAGE_SIZE
, r1
, r2
);
405 /* string copy (c is string terminator) */
406 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
411 d
= fix_address(env
, d
);
412 s
= fix_address(env
, s
);
414 /* Lest we fail to service interrupts in a timely manner, limit the
415 amount of work we're willing to do. For now, let's cap at 8k. */
416 for (len
= 0; len
< 0x2000; ++len
) {
417 uint8_t v
= cpu_ldub_data(env
, s
+ len
);
418 cpu_stb_data(env
, d
+ len
, v
);
420 /* Complete. Set CC=1 and advance R1. */
427 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
429 env
->retxl
= s
+ len
;
433 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
436 int pos
= 24; /* top of the lower half of r1 */
437 uint64_t rmask
= 0xff000000ULL
;
444 env
->regs
[r1
] &= ~rmask
;
445 val
= cpu_ldub_data(env
, address
);
446 if ((val
& 0x80) && !ccd
) {
450 if (val
&& cc
== 0) {
453 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
456 mask
= (mask
<< 1) & 0xf;
464 /* execute instruction
465 this instruction executes an insn modified with the contents of r1
466 it does not change the executed instruction in memory
467 it does not change the program counter
468 in other words: tricky...
469 currently implemented by interpreting the cases it is most commonly used in
471 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
472 uint64_t addr
, uint64_t ret
)
474 S390CPU
*cpu
= s390_env_get_cpu(env
);
475 uint16_t insn
= cpu_lduw_code(env
, addr
);
477 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
479 if ((insn
& 0xf0ff) == 0xd000) {
480 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
483 insn2
= cpu_ldl_code(env
, addr
+ 2);
484 b1
= (insn2
>> 28) & 0xf;
485 b2
= (insn2
>> 12) & 0xf;
486 d1
= (insn2
>> 16) & 0xfff;
488 switch (insn
& 0xf00) {
490 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
491 get_address(env
, 0, b2
, d2
));
494 cc
= helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
495 get_address(env
, 0, b2
, d2
));
498 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
499 get_address(env
, 0, b2
, d2
));
502 cc
= helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
503 get_address(env
, 0, b2
, d2
));
506 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
507 get_address(env
, 0, b2
, d2
));
510 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
511 get_address(env
, 0, b2
, d2
));
516 } else if ((insn
& 0xff00) == 0x0a00) {
517 /* supervisor call */
518 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
519 env
->psw
.addr
= ret
- 4;
520 env
->int_svc_code
= (insn
| v1
) & 0xff;
521 env
->int_svc_ilen
= 4;
522 helper_exception(env
, EXCP_SVC
);
523 } else if ((insn
& 0xff00) == 0xbf00) {
524 uint32_t insn2
, r1
, r3
, b2
, d2
;
526 insn2
= cpu_ldl_code(env
, addr
+ 2);
527 r1
= (insn2
>> 20) & 0xf;
528 r3
= (insn2
>> 16) & 0xf;
529 b2
= (insn2
>> 12) & 0xf;
531 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
534 cpu_abort(CPU(cpu
), "EXECUTE on instruction prefix 0x%x not implemented\n",
540 /* load access registers r1 to r3 from memory at a2 */
541 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
545 for (i
= r1
;; i
= (i
+ 1) % 16) {
546 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
555 /* store access registers r1 to r3 in memory at a2 */
556 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
560 for (i
= r1
;; i
= (i
+ 1) % 16) {
561 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
571 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
573 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
574 uint64_t dest
= get_address_31fix(env
, r1
);
575 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
576 uint64_t src
= get_address_31fix(env
, r2
);
577 uint8_t pad
= src
>> 24;
581 if (destlen
== srclen
) {
583 } else if (destlen
< srclen
) {
589 if (srclen
> destlen
) {
593 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
594 v
= cpu_ldub_data(env
, src
);
595 cpu_stb_data(env
, dest
, v
);
598 for (; destlen
; dest
++, destlen
--) {
599 cpu_stb_data(env
, dest
, pad
);
602 env
->regs
[r1
+ 1] = destlen
;
603 /* can't use srclen here, we trunc'ed it */
604 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
605 env
->regs
[r1
] = dest
;
611 /* move long extended another memcopy insn with more bells and whistles */
612 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
615 uint64_t destlen
= env
->regs
[r1
+ 1];
616 uint64_t dest
= env
->regs
[r1
];
617 uint64_t srclen
= env
->regs
[r3
+ 1];
618 uint64_t src
= env
->regs
[r3
];
619 uint8_t pad
= a2
& 0xff;
623 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
624 destlen
= (uint32_t)destlen
;
625 srclen
= (uint32_t)srclen
;
630 if (destlen
== srclen
) {
632 } else if (destlen
< srclen
) {
638 if (srclen
> destlen
) {
642 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
643 v
= cpu_ldub_data(env
, src
);
644 cpu_stb_data(env
, dest
, v
);
647 for (; destlen
; dest
++, destlen
--) {
648 cpu_stb_data(env
, dest
, pad
);
651 env
->regs
[r1
+ 1] = destlen
;
652 /* can't use srclen here, we trunc'ed it */
653 /* FIXME: 31-bit mode! */
654 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
655 env
->regs
[r1
] = dest
;
661 /* compare logical long extended memcompare insn with padding */
662 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
665 uint64_t destlen
= env
->regs
[r1
+ 1];
666 uint64_t dest
= get_address_31fix(env
, r1
);
667 uint64_t srclen
= env
->regs
[r3
+ 1];
668 uint64_t src
= get_address_31fix(env
, r3
);
669 uint8_t pad
= a2
& 0xff;
670 uint8_t v1
= 0, v2
= 0;
673 if (!(destlen
|| srclen
)) {
677 if (srclen
> destlen
) {
681 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
682 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
683 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
685 cc
= (v1
< v2
) ? 1 : 2;
690 env
->regs
[r1
+ 1] = destlen
;
691 /* can't use srclen here, we trunc'ed it */
692 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
693 env
->regs
[r1
] = dest
;
700 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
701 uint64_t src
, uint64_t src_len
)
703 uint64_t max_len
, len
;
704 uint64_t cksm
= (uint32_t)r1
;
706 /* Lest we fail to service interrupts in a timely manner, limit the
707 amount of work we're willing to do. For now, let's cap at 8k. */
708 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
710 /* Process full words as available. */
711 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
712 cksm
+= (uint32_t)cpu_ldl_data(env
, src
);
715 switch (max_len
- len
) {
717 cksm
+= cpu_ldub_data(env
, src
) << 24;
721 cksm
+= cpu_lduw_data(env
, src
) << 16;
725 cksm
+= cpu_lduw_data(env
, src
) << 16;
726 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
731 /* Fold the carry from the checksum. Note that we can see carry-out
732 during folding more than once (but probably not more than twice). */
733 while (cksm
> 0xffffffffull
) {
734 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
737 /* Indicate whether or not we've processed everything. */
738 env
->cc_op
= (len
== src_len
? 0 : 3);
740 /* Return both cksm and processed length. */
745 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
748 int len_dest
= len
>> 4;
749 int len_src
= len
& 0xf;
751 int second_nibble
= 0;
756 /* last byte is special, it only flips the nibbles */
757 b
= cpu_ldub_data(env
, src
);
758 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
762 /* now pad every nibble with 0xf0 */
764 while (len_dest
> 0) {
765 uint8_t cur_byte
= 0;
768 cur_byte
= cpu_ldub_data(env
, src
);
774 /* only advance one nibble at a time */
780 second_nibble
= !second_nibble
;
783 cur_byte
= (cur_byte
& 0xf);
787 cpu_stb_data(env
, dest
, cur_byte
);
791 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
796 for (i
= 0; i
<= len
; i
++) {
797 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
798 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
800 cpu_stb_data(env
, array
+ i
, new_byte
);
804 #if !defined(CONFIG_USER_ONLY)
805 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
807 S390CPU
*cpu
= s390_env_get_cpu(env
);
811 for (i
= r1
;; i
= (i
+ 1) % 16) {
812 env
->cregs
[i
] = cpu_ldq_data(env
, src
);
813 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
814 i
, src
, env
->cregs
[i
]);
815 src
+= sizeof(uint64_t);
822 tlb_flush(CPU(cpu
), 1);
825 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
827 S390CPU
*cpu
= s390_env_get_cpu(env
);
831 for (i
= r1
;; i
= (i
+ 1) % 16) {
832 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) |
833 cpu_ldl_data(env
, src
);
834 src
+= sizeof(uint32_t);
841 tlb_flush(CPU(cpu
), 1);
844 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
849 for (i
= r1
;; i
= (i
+ 1) % 16) {
850 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
851 dest
+= sizeof(uint64_t);
859 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
864 for (i
= r1
;; i
= (i
+ 1) % 16) {
865 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
866 dest
+= sizeof(uint32_t);
874 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
881 /* insert storage key extended */
882 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
884 uint64_t addr
= get_address(env
, 0, 0, r2
);
886 if (addr
> ram_size
) {
890 return env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
];
893 /* set storage key extended */
894 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
896 uint64_t addr
= get_address(env
, 0, 0, r2
);
898 if (addr
> ram_size
) {
902 env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
] = r1
;
905 /* reset reference bit extended */
906 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
915 key
= env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
];
916 re
= key
& (SK_R
| SK_C
);
917 env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
] = (key
& ~SK_R
);
922 * 0 Reference bit zero; change bit zero
923 * 1 Reference bit zero; change bit one
924 * 2 Reference bit one; change bit zero
925 * 3 Reference bit one; change bit one
931 /* compare and swap and purge */
932 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
934 S390CPU
*cpu
= s390_env_get_cpu(env
);
936 uint32_t o1
= env
->regs
[r1
];
937 uint64_t a2
= r2
& ~3ULL;
938 uint32_t o2
= cpu_ldl_data(env
, a2
);
941 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
943 /* flush TLB / ALB */
944 tlb_flush(CPU(cpu
), 1);
948 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
955 static uint32_t mvc_asc(CPUS390XState
*env
, int64_t l
, uint64_t a1
,
956 uint64_t mode1
, uint64_t a2
, uint64_t mode2
)
958 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
959 target_ulong src
, dest
;
960 int flags
, cc
= 0, i
;
964 } else if (l
> 256) {
970 if (mmu_translate(env
, a1
, 1, mode1
, &dest
, &flags
, true)) {
971 cpu_loop_exit(CPU(s390_env_get_cpu(env
)));
973 dest
|= a1
& ~TARGET_PAGE_MASK
;
975 if (mmu_translate(env
, a2
, 0, mode2
, &src
, &flags
, true)) {
976 cpu_loop_exit(CPU(s390_env_get_cpu(env
)));
978 src
|= a2
& ~TARGET_PAGE_MASK
;
980 /* XXX replace w/ memcpy */
981 for (i
= 0; i
< l
; i
++) {
982 /* XXX be more clever */
983 if ((((dest
+ i
) & TARGET_PAGE_MASK
) != (dest
& TARGET_PAGE_MASK
)) ||
984 (((src
+ i
) & TARGET_PAGE_MASK
) != (src
& TARGET_PAGE_MASK
))) {
985 mvc_asc(env
, l
- i
, a1
+ i
, mode1
, a2
+ i
, mode2
);
988 stb_phys(cs
->as
, dest
+ i
, ldub_phys(cs
->as
, src
+ i
));
994 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
996 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
997 __func__
, l
, a1
, a2
);
999 return mvc_asc(env
, l
, a1
, PSW_ASC_SECONDARY
, a2
, PSW_ASC_PRIMARY
);
1002 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1004 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1005 __func__
, l
, a1
, a2
);
1007 return mvc_asc(env
, l
, a1
, PSW_ASC_PRIMARY
, a2
, PSW_ASC_SECONDARY
);
1010 /* invalidate pte */
1011 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1013 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1014 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1017 /* XXX broadcast to other CPUs */
1019 /* XXX Linux is nice enough to give us the exact pte address.
1020 According to spec we'd have to find it out ourselves */
1021 /* XXX Linux is fine with overwriting the pte, the spec requires
1022 us to only set the invalid bit */
1023 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1025 /* XXX we exploit the fact that Linux passes the exact virtual
1026 address here - it's not obliged to! */
1027 tlb_flush_page(cs
, page
);
1029 /* XXX 31-bit hack */
1030 if (page
& 0x80000000) {
1031 tlb_flush_page(cs
, page
& ~0x80000000);
1033 tlb_flush_page(cs
, page
| 0x80000000);
1037 /* flush local tlb */
1038 void HELPER(ptlb
)(CPUS390XState
*env
)
1040 S390CPU
*cpu
= s390_env_get_cpu(env
);
1042 tlb_flush(CPU(cpu
), 1);
1045 /* load using real address */
1046 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1048 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1050 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1053 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1055 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1057 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1060 /* store using real address */
1061 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1063 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1065 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1068 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1070 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1072 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1075 /* load real address */
1076 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1078 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1080 int old_exc
= cs
->exception_index
;
1081 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1085 /* XXX incomplete - has more corner cases */
1086 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1087 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1090 cs
->exception_index
= old_exc
;
1091 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1094 if (cs
->exception_index
== EXCP_PGM
) {
1095 ret
= env
->int_pgm_code
| 0x80000000;
1097 ret
|= addr
& ~TARGET_PAGE_MASK
;
1099 cs
->exception_index
= old_exc
;