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/>.
21 #include "qemu/osdep.h"
23 #include "exec/address-spaces.h"
24 #include "exec/helper-proto.h"
25 #include "exec/exec-all.h"
26 #include "exec/cpu_ldst.h"
27 #include "qemu/int128.h"
29 #if !defined(CONFIG_USER_ONLY)
30 #include "hw/s390x/storage-keys.h"
33 /*****************************************************************************/
35 #if !defined(CONFIG_USER_ONLY)
37 /* try to fill the TLB and return an exception if error. If retaddr is
38 NULL, it means that the function was called in C code (i.e. not
39 from generated code or from helper.c) */
40 /* XXX: fix it to restore all registers */
41 void tlb_fill(CPUState
*cs
, target_ulong addr
, MMUAccessType access_type
,
42 int mmu_idx
, uintptr_t retaddr
)
44 int ret
= s390_cpu_handle_mmu_fault(cs
, addr
, access_type
, mmu_idx
);
45 if (unlikely(ret
!= 0)) {
46 cpu_loop_exit_restore(cs
, retaddr
);
52 /* #define DEBUG_HELPER */
54 #define HELPER_LOG(x...) qemu_log(x)
56 #define HELPER_LOG(x...)
59 /* Reduce the length so that addr + len doesn't cross a page boundary. */
60 static inline uint32_t adj_len_to_page(uint32_t len
, uint64_t addr
)
62 #ifndef CONFIG_USER_ONLY
63 if ((addr
& ~TARGET_PAGE_MASK
) + len
- 1 >= TARGET_PAGE_SIZE
) {
64 return -addr
& ~TARGET_PAGE_MASK
;
70 static void fast_memset(CPUS390XState
*env
, uint64_t dest
, uint8_t byte
,
71 uint32_t l
, uintptr_t ra
)
73 int mmu_idx
= cpu_mmu_index(env
, false);
76 void *p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
78 /* Access to the whole page in write mode granted. */
79 uint32_t l_adj
= adj_len_to_page(l
, dest
);
80 memset(p
, byte
, l_adj
);
84 /* We failed to get access to the whole page. The next write
85 access will likely fill the QEMU TLB for the next iteration. */
86 cpu_stb_data_ra(env
, dest
, byte
, ra
);
93 static void fast_memmove(CPUS390XState
*env
, uint64_t dest
, uint64_t src
,
94 uint32_t l
, uintptr_t ra
)
96 int mmu_idx
= cpu_mmu_index(env
, false);
99 void *src_p
= tlb_vaddr_to_host(env
, src
, MMU_DATA_LOAD
, mmu_idx
);
100 void *dest_p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
101 if (src_p
&& dest_p
) {
102 /* Access to both whole pages granted. */
103 uint32_t l_adj
= adj_len_to_page(l
, src
);
104 l_adj
= adj_len_to_page(l_adj
, dest
);
105 memmove(dest_p
, src_p
, l_adj
);
110 /* We failed to get access to one or both whole pages. The next
111 read or write access will likely fill the QEMU TLB for the
113 cpu_stb_data_ra(env
, dest
, cpu_ldub_data_ra(env
, src
, ra
), ra
);
122 static uint32_t do_helper_nc(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
123 uint64_t src
, uintptr_t ra
)
128 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
129 __func__
, l
, dest
, src
);
131 for (i
= 0; i
<= l
; i
++) {
132 uint8_t x
= cpu_ldub_data_ra(env
, src
+ i
, ra
);
133 x
&= cpu_ldub_data_ra(env
, dest
+ i
, ra
);
135 cpu_stb_data_ra(env
, dest
+ i
, x
, ra
);
140 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
143 return do_helper_nc(env
, l
, dest
, src
, GETPC());
147 static uint32_t do_helper_xc(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
148 uint64_t src
, uintptr_t ra
)
153 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
154 __func__
, l
, dest
, src
);
156 /* xor with itself is the same as memset(0) */
158 fast_memset(env
, dest
, 0, l
+ 1, ra
);
162 for (i
= 0; i
<= l
; i
++) {
163 uint8_t x
= cpu_ldub_data_ra(env
, src
+ i
, ra
);
164 x
^= cpu_ldub_data_ra(env
, dest
+ i
, ra
);
166 cpu_stb_data_ra(env
, dest
+ i
, x
, ra
);
171 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
174 return do_helper_xc(env
, l
, dest
, src
, GETPC());
178 static uint32_t do_helper_oc(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
179 uint64_t src
, uintptr_t ra
)
184 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
185 __func__
, l
, dest
, src
);
187 for (i
= 0; i
<= l
; i
++) {
188 uint8_t x
= cpu_ldub_data_ra(env
, src
+ i
, ra
);
189 x
|= cpu_ldub_data_ra(env
, dest
+ i
, ra
);
191 cpu_stb_data_ra(env
, dest
+ i
, x
, ra
);
196 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
199 return do_helper_oc(env
, l
, dest
, src
, GETPC());
203 static void do_helper_mvc(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
204 uint64_t src
, uintptr_t ra
)
208 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
209 __func__
, l
, dest
, src
);
211 /* mvc with source pointing to the byte after the destination is the
212 same as memset with the first source byte */
213 if (dest
== src
+ 1) {
214 fast_memset(env
, dest
, cpu_ldub_data_ra(env
, src
, ra
), l
+ 1, ra
);
218 /* mvc and memmove do not behave the same when areas overlap! */
219 if (dest
< src
|| src
+ l
< dest
) {
220 fast_memmove(env
, dest
, src
, l
+ 1, ra
);
224 /* slow version with byte accesses which always work */
225 for (i
= 0; i
<= l
; i
++) {
226 cpu_stb_data_ra(env
, dest
+ i
, cpu_ldub_data_ra(env
, src
+ i
, ra
), ra
);
230 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
232 do_helper_mvc(env
, l
, dest
, src
, GETPC());
235 /* compare unsigned byte arrays */
236 static uint32_t do_helper_clc(CPUS390XState
*env
, uint32_t l
, uint64_t s1
,
237 uint64_t s2
, uintptr_t ra
)
242 HELPER_LOG("%s l %d s1 %" PRIx64
" s2 %" PRIx64
"\n",
243 __func__
, l
, s1
, s2
);
245 for (i
= 0; i
<= l
; i
++) {
246 uint8_t x
= cpu_ldub_data_ra(env
, s1
+ i
, ra
);
247 uint8_t y
= cpu_ldub_data_ra(env
, s2
+ i
, ra
);
248 HELPER_LOG("%02x (%c)/%02x (%c) ", x
, x
, y
, y
);
262 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
264 return do_helper_clc(env
, l
, s1
, s2
, GETPC());
267 /* compare logical under mask */
268 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
271 uintptr_t ra
= GETPC();
274 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
279 uint8_t d
= cpu_ldub_data_ra(env
, addr
, ra
);
280 uint8_t r
= extract32(r1
, 24, 8);
281 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64
") ", mask
, r
, d
,
292 mask
= (mask
<< 1) & 0xf;
300 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
303 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
309 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
318 return fix_address(env
, r
);
321 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
323 return fix_address(env
, env
->regs
[reg
]);
326 /* search string (c is byte to search, r2 is string, r1 end of string) */
327 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
330 uintptr_t ra
= GETPC();
334 str
= fix_address(env
, str
);
335 end
= fix_address(env
, end
);
337 /* Assume for now that R2 is unmodified. */
340 /* Lest we fail to service interrupts in a timely manner, limit the
341 amount of work we're willing to do. For now, let's cap at 8k. */
342 for (len
= 0; len
< 0x2000; ++len
) {
343 if (str
+ len
== end
) {
344 /* Character not found. R1 & R2 are unmodified. */
348 v
= cpu_ldub_data_ra(env
, str
+ len
, ra
);
350 /* Character found. Set R1 to the location; R2 is unmodified. */
356 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
357 env
->retxl
= str
+ len
;
362 /* unsigned string compare (c is string terminator) */
363 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
365 uintptr_t ra
= GETPC();
369 s1
= fix_address(env
, s1
);
370 s2
= fix_address(env
, s2
);
372 /* Lest we fail to service interrupts in a timely manner, limit the
373 amount of work we're willing to do. For now, let's cap at 8k. */
374 for (len
= 0; len
< 0x2000; ++len
) {
375 uint8_t v1
= cpu_ldub_data_ra(env
, s1
+ len
, ra
);
376 uint8_t v2
= cpu_ldub_data_ra(env
, s2
+ len
, ra
);
379 /* Equal. CC=0, and don't advance the registers. */
385 /* Unequal. CC={1,2}, and advance the registers. Note that
386 the terminator need not be zero, but the string that contains
387 the terminator is by definition "low". */
388 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
389 env
->retxl
= s2
+ len
;
394 /* CPU-determined bytes equal; advance the registers. */
396 env
->retxl
= s2
+ len
;
401 uint32_t HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
403 /* ??? missing r0 handling, which includes access keys, but more
404 importantly optional suppression of the exception! */
405 fast_memmove(env
, r1
, r2
, TARGET_PAGE_SIZE
, GETPC());
406 return 0; /* data moved */
409 /* string copy (c is string terminator) */
410 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
412 uintptr_t ra
= GETPC();
416 d
= fix_address(env
, d
);
417 s
= fix_address(env
, s
);
419 /* Lest we fail to service interrupts in a timely manner, limit the
420 amount of work we're willing to do. For now, let's cap at 8k. */
421 for (len
= 0; len
< 0x2000; ++len
) {
422 uint8_t v
= cpu_ldub_data_ra(env
, s
+ len
, ra
);
423 cpu_stb_data_ra(env
, d
+ len
, v
, ra
);
425 /* Complete. Set CC=1 and advance R1. */
432 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
434 env
->retxl
= s
+ len
;
438 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
441 int pos
= 24; /* top of the lower half of r1 */
442 uint64_t rmask
= 0xff000000ULL
;
449 env
->regs
[r1
] &= ~rmask
;
450 val
= cpu_ldub_data(env
, address
);
451 if ((val
& 0x80) && !ccd
) {
455 if (val
&& cc
== 0) {
458 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
461 mask
= (mask
<< 1) & 0xf;
469 /* load access registers r1 to r3 from memory at a2 */
470 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
472 uintptr_t ra
= GETPC();
475 for (i
= r1
;; i
= (i
+ 1) % 16) {
476 env
->aregs
[i
] = cpu_ldl_data_ra(env
, a2
, ra
);
485 /* store access registers r1 to r3 in memory at a2 */
486 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
488 uintptr_t ra
= GETPC();
491 for (i
= r1
;; i
= (i
+ 1) % 16) {
492 cpu_stl_data_ra(env
, a2
, env
->aregs
[i
], ra
);
502 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
504 uintptr_t ra
= GETPC();
505 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
506 uint64_t dest
= get_address_31fix(env
, r1
);
507 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
508 uint64_t src
= get_address_31fix(env
, r2
);
509 uint8_t pad
= env
->regs
[r2
+ 1] >> 24;
513 if (destlen
== srclen
) {
515 } else if (destlen
< srclen
) {
521 if (srclen
> destlen
) {
525 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
526 v
= cpu_ldub_data_ra(env
, src
, ra
);
527 cpu_stb_data_ra(env
, dest
, v
, ra
);
530 for (; destlen
; dest
++, destlen
--) {
531 cpu_stb_data_ra(env
, dest
, pad
, ra
);
534 env
->regs
[r1
+ 1] = destlen
;
535 /* can't use srclen here, we trunc'ed it */
536 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
537 env
->regs
[r1
] = dest
;
543 /* move long extended another memcopy insn with more bells and whistles */
544 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
547 uintptr_t ra
= GETPC();
548 uint64_t destlen
= env
->regs
[r1
+ 1];
549 uint64_t dest
= env
->regs
[r1
];
550 uint64_t srclen
= env
->regs
[r3
+ 1];
551 uint64_t src
= env
->regs
[r3
];
552 uint8_t pad
= a2
& 0xff;
556 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
557 destlen
= (uint32_t)destlen
;
558 srclen
= (uint32_t)srclen
;
563 if (destlen
== srclen
) {
565 } else if (destlen
< srclen
) {
571 if (srclen
> destlen
) {
575 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
576 v
= cpu_ldub_data_ra(env
, src
, ra
);
577 cpu_stb_data_ra(env
, dest
, v
, ra
);
580 for (; destlen
; dest
++, destlen
--) {
581 cpu_stb_data_ra(env
, dest
, pad
, ra
);
584 env
->regs
[r1
+ 1] = destlen
;
585 /* can't use srclen here, we trunc'ed it */
586 /* FIXME: 31-bit mode! */
587 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
588 env
->regs
[r1
] = dest
;
594 /* compare logical long extended memcompare insn with padding */
595 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
598 uintptr_t ra
= GETPC();
599 uint64_t destlen
= env
->regs
[r1
+ 1];
600 uint64_t dest
= get_address_31fix(env
, r1
);
601 uint64_t srclen
= env
->regs
[r3
+ 1];
602 uint64_t src
= get_address_31fix(env
, r3
);
603 uint8_t pad
= a2
& 0xff;
606 if (!(destlen
|| srclen
)) {
610 if (srclen
> destlen
) {
614 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
615 uint8_t v1
= srclen
? cpu_ldub_data_ra(env
, src
, ra
) : pad
;
616 uint8_t v2
= destlen
? cpu_ldub_data_ra(env
, dest
, ra
) : pad
;
618 cc
= (v1
< v2
) ? 1 : 2;
623 env
->regs
[r1
+ 1] = destlen
;
624 /* can't use srclen here, we trunc'ed it */
625 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
626 env
->regs
[r1
] = dest
;
633 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
634 uint64_t src
, uint64_t src_len
)
636 uintptr_t ra
= GETPC();
637 uint64_t max_len
, len
;
638 uint64_t cksm
= (uint32_t)r1
;
640 /* Lest we fail to service interrupts in a timely manner, limit the
641 amount of work we're willing to do. For now, let's cap at 8k. */
642 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
644 /* Process full words as available. */
645 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
646 cksm
+= (uint32_t)cpu_ldl_data_ra(env
, src
, ra
);
649 switch (max_len
- len
) {
651 cksm
+= cpu_ldub_data_ra(env
, src
, ra
) << 24;
655 cksm
+= cpu_lduw_data_ra(env
, src
, ra
) << 16;
659 cksm
+= cpu_lduw_data_ra(env
, src
, ra
) << 16;
660 cksm
+= cpu_ldub_data_ra(env
, src
+ 2, ra
) << 8;
665 /* Fold the carry from the checksum. Note that we can see carry-out
666 during folding more than once (but probably not more than twice). */
667 while (cksm
> 0xffffffffull
) {
668 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
671 /* Indicate whether or not we've processed everything. */
672 env
->cc_op
= (len
== src_len
? 0 : 3);
674 /* Return both cksm and processed length. */
679 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
682 int len_dest
= len
>> 4;
683 int len_src
= len
& 0xf;
685 int second_nibble
= 0;
690 /* last byte is special, it only flips the nibbles */
691 b
= cpu_ldub_data(env
, src
);
692 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
696 /* now pad every nibble with 0xf0 */
698 while (len_dest
> 0) {
699 uint8_t cur_byte
= 0;
702 cur_byte
= cpu_ldub_data(env
, src
);
708 /* only advance one nibble at a time */
714 second_nibble
= !second_nibble
;
717 cur_byte
= (cur_byte
& 0xf);
721 cpu_stb_data(env
, dest
, cur_byte
);
725 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
730 for (i
= 0; i
<= len
; i
++) {
731 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
732 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
734 cpu_stb_data(env
, array
+ i
, new_byte
);
738 uint64_t HELPER(tre
)(CPUS390XState
*env
, uint64_t array
,
739 uint64_t len
, uint64_t trans
)
741 uint8_t end
= env
->regs
[0] & 0xff;
745 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
750 /* Lest we fail to service interrupts in a timely manner, limit the
751 amount of work we're willing to do. For now, let's cap at 8k. */
759 for (i
= 0; i
< l
; i
++) {
760 uint8_t byte
, new_byte
;
762 byte
= cpu_ldub_data(env
, array
+ i
);
769 new_byte
= cpu_ldub_data(env
, trans
+ byte
);
770 cpu_stb_data(env
, array
+ i
, new_byte
);
773 env
->retxl
= len
- i
;
777 uint32_t HELPER(trt
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
783 for (i
= 0; i
<= len
; i
++) {
784 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
785 uint8_t sbyte
= cpu_ldub_data(env
, trans
+ byte
);
788 env
->regs
[1] = array
+ i
;
789 env
->regs
[2] = (env
->regs
[2] & ~0xff) | sbyte
;
790 cc
= (i
== len
) ? 2 : 1;
798 void HELPER(cdsg
)(CPUS390XState
*env
, uint64_t addr
,
799 uint32_t r1
, uint32_t r3
)
801 uintptr_t ra
= GETPC();
802 Int128 cmpv
= int128_make128(env
->regs
[r1
+ 1], env
->regs
[r1
]);
803 Int128 newv
= int128_make128(env
->regs
[r3
+ 1], env
->regs
[r3
]);
808 #ifndef CONFIG_ATOMIC128
809 cpu_loop_exit_atomic(ENV_GET_CPU(env
), ra
);
811 int mem_idx
= cpu_mmu_index(env
, false);
812 TCGMemOpIdx oi
= make_memop_idx(MO_TEQ
| MO_ALIGN_16
, mem_idx
);
813 oldv
= helper_atomic_cmpxchgo_be_mmu(env
, addr
, cmpv
, newv
, oi
, ra
);
814 fail
= !int128_eq(oldv
, cmpv
);
819 oldh
= cpu_ldq_data_ra(env
, addr
+ 0, ra
);
820 oldl
= cpu_ldq_data_ra(env
, addr
+ 8, ra
);
822 oldv
= int128_make128(oldl
, oldh
);
823 fail
= !int128_eq(oldv
, cmpv
);
828 cpu_stq_data_ra(env
, addr
+ 0, int128_gethi(newv
), ra
);
829 cpu_stq_data_ra(env
, addr
+ 8, int128_getlo(newv
), ra
);
833 env
->regs
[r1
] = int128_gethi(oldv
);
834 env
->regs
[r1
+ 1] = int128_getlo(oldv
);
837 #if !defined(CONFIG_USER_ONLY)
838 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
840 S390CPU
*cpu
= s390_env_get_cpu(env
);
841 bool PERchanged
= false;
846 for (i
= r1
;; i
= (i
+ 1) % 16) {
847 val
= cpu_ldq_data(env
, src
);
848 if (env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
852 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
853 i
, src
, env
->cregs
[i
]);
854 src
+= sizeof(uint64_t);
861 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
862 s390_cpu_recompute_watchpoints(CPU(cpu
));
868 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
870 S390CPU
*cpu
= s390_env_get_cpu(env
);
871 bool PERchanged
= false;
876 for (i
= r1
;; i
= (i
+ 1) % 16) {
877 val
= cpu_ldl_data(env
, src
);
878 if ((uint32_t)env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
881 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) | val
;
882 src
+= sizeof(uint32_t);
889 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
890 s390_cpu_recompute_watchpoints(CPU(cpu
));
896 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
901 for (i
= r1
;; i
= (i
+ 1) % 16) {
902 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
903 dest
+= sizeof(uint64_t);
911 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
916 for (i
= r1
;; i
= (i
+ 1) % 16) {
917 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
918 dest
+= sizeof(uint32_t);
926 uint32_t HELPER(testblock
)(CPUS390XState
*env
, uint64_t real_addr
)
928 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
932 real_addr
= fix_address(env
, real_addr
);
933 abs_addr
= mmu_real2abs(env
, real_addr
) & TARGET_PAGE_MASK
;
934 if (!address_space_access_valid(&address_space_memory
, abs_addr
,
935 TARGET_PAGE_SIZE
, true)) {
936 program_interrupt(env
, PGM_ADDRESSING
, 4);
940 /* Check low-address protection */
941 if ((env
->cregs
[0] & CR0_LOWPROT
) && real_addr
< 0x2000) {
942 program_interrupt(env
, PGM_PROTECTION
, 4);
946 for (i
= 0; i
< TARGET_PAGE_SIZE
; i
+= 8) {
947 stq_phys(cs
->as
, abs_addr
+ i
, 0);
953 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
960 /* insert storage key extended */
961 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
963 static S390SKeysState
*ss
;
964 static S390SKeysClass
*skeyclass
;
965 uint64_t addr
= get_address(env
, 0, 0, r2
);
968 if (addr
> ram_size
) {
973 ss
= s390_get_skeys_device();
974 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
977 if (skeyclass
->get_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
)) {
983 /* set storage key extended */
984 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
986 static S390SKeysState
*ss
;
987 static S390SKeysClass
*skeyclass
;
988 uint64_t addr
= get_address(env
, 0, 0, r2
);
991 if (addr
> ram_size
) {
996 ss
= s390_get_skeys_device();
997 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
1001 skeyclass
->set_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
);
1004 /* reset reference bit extended */
1005 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
1007 static S390SKeysState
*ss
;
1008 static S390SKeysClass
*skeyclass
;
1011 if (r2
> ram_size
) {
1015 if (unlikely(!ss
)) {
1016 ss
= s390_get_skeys_device();
1017 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
1020 if (skeyclass
->get_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1024 re
= key
& (SK_R
| SK_C
);
1027 if (skeyclass
->set_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1034 * 0 Reference bit zero; change bit zero
1035 * 1 Reference bit zero; change bit one
1036 * 2 Reference bit one; change bit zero
1037 * 3 Reference bit one; change bit one
1043 /* compare and swap and purge */
1044 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1046 S390CPU
*cpu
= s390_env_get_cpu(env
);
1048 uint32_t o1
= env
->regs
[r1
];
1049 uint64_t a2
= r2
& ~3ULL;
1050 uint32_t o2
= cpu_ldl_data(env
, a2
);
1053 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1055 /* flush TLB / ALB */
1056 tlb_flush(CPU(cpu
));
1060 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1067 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1071 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1072 __func__
, l
, a1
, a2
);
1080 /* XXX replace w/ memcpy */
1081 for (i
= 0; i
< l
; i
++) {
1082 cpu_stb_secondary(env
, a1
+ i
, cpu_ldub_primary(env
, a2
+ i
));
1088 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1092 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1093 __func__
, l
, a1
, a2
);
1101 /* XXX replace w/ memcpy */
1102 for (i
= 0; i
< l
; i
++) {
1103 cpu_stb_primary(env
, a1
+ i
, cpu_ldub_secondary(env
, a2
+ i
));
1109 /* invalidate pte */
1110 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1112 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1113 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1116 /* XXX broadcast to other CPUs */
1118 /* XXX Linux is nice enough to give us the exact pte address.
1119 According to spec we'd have to find it out ourselves */
1120 /* XXX Linux is fine with overwriting the pte, the spec requires
1121 us to only set the invalid bit */
1122 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1124 /* XXX we exploit the fact that Linux passes the exact virtual
1125 address here - it's not obliged to! */
1126 tlb_flush_page(cs
, page
);
1128 /* XXX 31-bit hack */
1129 if (page
& 0x80000000) {
1130 tlb_flush_page(cs
, page
& ~0x80000000);
1132 tlb_flush_page(cs
, page
| 0x80000000);
1136 /* flush local tlb */
1137 void HELPER(ptlb
)(CPUS390XState
*env
)
1139 S390CPU
*cpu
= s390_env_get_cpu(env
);
1141 tlb_flush(CPU(cpu
));
1144 /* load using real address */
1145 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1147 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1149 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1152 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1154 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1156 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1159 /* store using real address */
1160 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1162 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1164 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1166 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1167 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1168 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1169 /* PSW is saved just before calling the helper. */
1170 env
->per_address
= env
->psw
.addr
;
1171 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1175 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1177 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1179 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1181 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1182 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1183 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1184 /* PSW is saved just before calling the helper. */
1185 env
->per_address
= env
->psw
.addr
;
1186 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1190 /* load real address */
1191 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1193 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1195 int old_exc
= cs
->exception_index
;
1196 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1200 /* XXX incomplete - has more corner cases */
1201 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1202 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1205 cs
->exception_index
= old_exc
;
1206 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1209 if (cs
->exception_index
== EXCP_PGM
) {
1210 ret
= env
->int_pgm_code
| 0x80000000;
1212 ret
|= addr
& ~TARGET_PAGE_MASK
;
1214 cs
->exception_index
= old_exc
;
1221 /* execute instruction
1222 this instruction executes an insn modified with the contents of r1
1223 it does not change the executed instruction in memory
1224 it does not change the program counter
1225 in other words: tricky...
1226 currently implemented by interpreting the cases it is most commonly used.
1228 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
1229 uint64_t addr
, uint64_t ret
)
1231 S390CPU
*cpu
= s390_env_get_cpu(env
);
1232 uint16_t insn
= cpu_lduw_code(env
, addr
);
1234 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
1236 if ((insn
& 0xf0ff) == 0xd000) {
1237 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
1240 insn2
= cpu_ldl_code(env
, addr
+ 2);
1241 b1
= (insn2
>> 28) & 0xf;
1242 b2
= (insn2
>> 12) & 0xf;
1243 d1
= (insn2
>> 16) & 0xfff;
1245 switch (insn
& 0xf00) {
1247 do_helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
1248 get_address(env
, 0, b2
, d2
), 0);
1251 cc
= do_helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
1252 get_address(env
, 0, b2
, d2
), 0);
1255 cc
= do_helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
1256 get_address(env
, 0, b2
, d2
), 0);
1259 cc
= do_helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
1260 get_address(env
, 0, b2
, d2
), 0);
1263 cc
= do_helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
1264 get_address(env
, 0, b2
, d2
), 0);
1267 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
1268 get_address(env
, 0, b2
, d2
));
1271 cc
= helper_trt(env
, l
, get_address(env
, 0, b1
, d1
),
1272 get_address(env
, 0, b2
, d2
));
1277 } else if ((insn
& 0xff00) == 0x0a00) {
1278 /* supervisor call */
1279 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
1280 env
->psw
.addr
= ret
- 4;
1281 env
->int_svc_code
= (insn
| v1
) & 0xff;
1282 env
->int_svc_ilen
= 4;
1283 helper_exception(env
, EXCP_SVC
);
1284 } else if ((insn
& 0xff00) == 0xbf00) {
1285 uint32_t insn2
, r1
, r3
, b2
, d2
;
1287 insn2
= cpu_ldl_code(env
, addr
+ 2);
1288 r1
= (insn2
>> 20) & 0xf;
1289 r3
= (insn2
>> 16) & 0xf;
1290 b2
= (insn2
>> 12) & 0xf;
1292 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
1296 "EXECUTE on instruction prefix 0x%x not implemented\n",