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/helper-proto.h"
24 #include "exec/cpu_ldst.h"
26 #if !defined(CONFIG_USER_ONLY)
27 #include "hw/s390x/storage-keys.h"
30 /*****************************************************************************/
32 #if !defined(CONFIG_USER_ONLY)
34 /* try to fill the TLB and return an exception if error. If retaddr is
35 NULL, it means that the function was called in C code (i.e. not
36 from generated code or from helper.c) */
37 /* XXX: fix it to restore all registers */
38 void tlb_fill(CPUState
*cs
, target_ulong addr
, int is_write
, int mmu_idx
,
43 ret
= s390_cpu_handle_mmu_fault(cs
, addr
, is_write
, mmu_idx
);
44 if (unlikely(ret
!= 0)) {
45 if (likely(retaddr
)) {
46 /* now we have a real cpu fault */
47 cpu_restore_state(cs
, retaddr
);
55 /* #define DEBUG_HELPER */
57 #define HELPER_LOG(x...) qemu_log(x)
59 #define HELPER_LOG(x...)
62 /* Reduce the length so that addr + len doesn't cross a page boundary. */
63 static inline uint64_t adj_len_to_page(uint64_t len
, uint64_t addr
)
65 #ifndef CONFIG_USER_ONLY
66 if ((addr
& ~TARGET_PAGE_MASK
) + len
- 1 >= TARGET_PAGE_SIZE
) {
67 return -addr
& ~TARGET_PAGE_MASK
;
73 static void fast_memset(CPUS390XState
*env
, uint64_t dest
, uint8_t byte
,
76 int mmu_idx
= cpu_mmu_index(env
, false);
79 void *p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
81 /* Access to the whole page in write mode granted. */
82 int l_adj
= adj_len_to_page(l
, dest
);
83 memset(p
, byte
, l_adj
);
87 /* We failed to get access to the whole page. The next write
88 access will likely fill the QEMU TLB for the next iteration. */
89 cpu_stb_data(env
, dest
, byte
);
96 static void fast_memmove(CPUS390XState
*env
, uint64_t dest
, uint64_t src
,
99 int mmu_idx
= cpu_mmu_index(env
, false);
102 void *src_p
= tlb_vaddr_to_host(env
, src
, MMU_DATA_LOAD
, mmu_idx
);
103 void *dest_p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
104 if (src_p
&& dest_p
) {
105 /* Access to both whole pages granted. */
106 int l_adj
= adj_len_to_page(l
, src
);
107 l_adj
= adj_len_to_page(l_adj
, dest
);
108 memmove(dest_p
, src_p
, l_adj
);
113 /* We failed to get access to one or both whole pages. The next
114 read or write access will likely fill the QEMU TLB for the
116 cpu_stb_data(env
, dest
, cpu_ldub_data(env
, src
));
125 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
132 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
133 __func__
, l
, dest
, src
);
134 for (i
= 0; i
<= l
; i
++) {
135 x
= cpu_ldub_data(env
, dest
+ i
) & cpu_ldub_data(env
, src
+ i
);
139 cpu_stb_data(env
, dest
+ i
, x
);
145 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
152 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
153 __func__
, l
, dest
, src
);
155 /* xor with itself is the same as memset(0) */
157 fast_memset(env
, dest
, 0, l
+ 1);
161 for (i
= 0; i
<= l
; i
++) {
162 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
166 cpu_stb_data(env
, dest
+ i
, x
);
172 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
179 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
180 __func__
, l
, dest
, src
);
181 for (i
= 0; i
<= l
; i
++) {
182 x
= cpu_ldub_data(env
, dest
+ i
) | cpu_ldub_data(env
, src
+ i
);
186 cpu_stb_data(env
, dest
+ i
, x
);
192 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
196 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
197 __func__
, l
, dest
, src
);
199 /* mvc with source pointing to the byte after the destination is the
200 same as memset with the first source byte */
201 if (dest
== (src
+ 1)) {
202 fast_memset(env
, dest
, cpu_ldub_data(env
, src
), l
+ 1);
206 /* mvc and memmove do not behave the same when areas overlap! */
207 if ((dest
< src
) || (src
+ l
< dest
)) {
208 fast_memmove(env
, dest
, src
, l
+ 1);
212 /* slow version with byte accesses which always work */
213 for (i
= 0; i
<= l
; i
++) {
214 cpu_stb_data(env
, dest
+ i
, cpu_ldub_data(env
, src
+ i
));
218 /* compare unsigned byte arrays */
219 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
225 HELPER_LOG("%s l %d s1 %" PRIx64
" s2 %" PRIx64
"\n",
226 __func__
, l
, s1
, s2
);
227 for (i
= 0; i
<= l
; i
++) {
228 x
= cpu_ldub_data(env
, s1
+ i
);
229 y
= cpu_ldub_data(env
, s2
+ i
);
230 HELPER_LOG("%02x (%c)/%02x (%c) ", x
, x
, y
, y
);
245 /* compare logical under mask */
246 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
252 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
257 d
= cpu_ldub_data(env
, addr
);
258 r
= (r1
& 0xff000000UL
) >> 24;
259 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64
") ", mask
, r
, d
,
270 mask
= (mask
<< 1) & 0xf;
277 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
280 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
286 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
295 return fix_address(env
, r
);
298 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
300 return fix_address(env
, env
->regs
[reg
]);
303 /* search string (c is byte to search, r2 is string, r1 end of string) */
304 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
310 str
= fix_address(env
, str
);
311 end
= fix_address(env
, end
);
313 /* Assume for now that R2 is unmodified. */
316 /* Lest we fail to service interrupts in a timely manner, limit the
317 amount of work we're willing to do. For now, let's cap at 8k. */
318 for (len
= 0; len
< 0x2000; ++len
) {
319 if (str
+ len
== end
) {
320 /* Character not found. R1 & R2 are unmodified. */
324 v
= cpu_ldub_data(env
, str
+ len
);
326 /* Character found. Set R1 to the location; R2 is unmodified. */
332 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
333 env
->retxl
= str
+ len
;
338 /* unsigned string compare (c is string terminator) */
339 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
344 s1
= fix_address(env
, s1
);
345 s2
= fix_address(env
, s2
);
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 uint8_t v1
= cpu_ldub_data(env
, s1
+ len
);
351 uint8_t v2
= cpu_ldub_data(env
, s2
+ len
);
354 /* Equal. CC=0, and don't advance the registers. */
360 /* Unequal. CC={1,2}, and advance the registers. Note that
361 the terminator need not be zero, but the string that contains
362 the terminator is by definition "low". */
363 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
364 env
->retxl
= s2
+ len
;
369 /* CPU-determined bytes equal; advance the registers. */
371 env
->retxl
= s2
+ len
;
376 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
378 /* XXX missing r0 handling */
380 fast_memmove(env
, r1
, r2
, TARGET_PAGE_SIZE
);
383 /* string copy (c is string terminator) */
384 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
389 d
= fix_address(env
, d
);
390 s
= fix_address(env
, s
);
392 /* Lest we fail to service interrupts in a timely manner, limit the
393 amount of work we're willing to do. For now, let's cap at 8k. */
394 for (len
= 0; len
< 0x2000; ++len
) {
395 uint8_t v
= cpu_ldub_data(env
, s
+ len
);
396 cpu_stb_data(env
, d
+ len
, v
);
398 /* Complete. Set CC=1 and advance R1. */
405 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
407 env
->retxl
= s
+ len
;
411 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
414 int pos
= 24; /* top of the lower half of r1 */
415 uint64_t rmask
= 0xff000000ULL
;
422 env
->regs
[r1
] &= ~rmask
;
423 val
= cpu_ldub_data(env
, address
);
424 if ((val
& 0x80) && !ccd
) {
428 if (val
&& cc
== 0) {
431 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
434 mask
= (mask
<< 1) & 0xf;
442 /* execute instruction
443 this instruction executes an insn modified with the contents of r1
444 it does not change the executed instruction in memory
445 it does not change the program counter
446 in other words: tricky...
447 currently implemented by interpreting the cases it is most commonly used in
449 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
450 uint64_t addr
, uint64_t ret
)
452 S390CPU
*cpu
= s390_env_get_cpu(env
);
453 uint16_t insn
= cpu_lduw_code(env
, addr
);
455 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
457 if ((insn
& 0xf0ff) == 0xd000) {
458 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
461 insn2
= cpu_ldl_code(env
, addr
+ 2);
462 b1
= (insn2
>> 28) & 0xf;
463 b2
= (insn2
>> 12) & 0xf;
464 d1
= (insn2
>> 16) & 0xfff;
466 switch (insn
& 0xf00) {
468 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
469 get_address(env
, 0, b2
, d2
));
472 cc
= helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
473 get_address(env
, 0, b2
, d2
));
476 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
477 get_address(env
, 0, b2
, d2
));
480 cc
= helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
481 get_address(env
, 0, b2
, d2
));
484 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
485 get_address(env
, 0, b2
, d2
));
488 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
489 get_address(env
, 0, b2
, d2
));
492 cc
= helper_trt(env
, l
, get_address(env
, 0, b1
, d1
),
493 get_address(env
, 0, b2
, d2
));
498 } else if ((insn
& 0xff00) == 0x0a00) {
499 /* supervisor call */
500 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
501 env
->psw
.addr
= ret
- 4;
502 env
->int_svc_code
= (insn
| v1
) & 0xff;
503 env
->int_svc_ilen
= 4;
504 helper_exception(env
, EXCP_SVC
);
505 } else if ((insn
& 0xff00) == 0xbf00) {
506 uint32_t insn2
, r1
, r3
, b2
, d2
;
508 insn2
= cpu_ldl_code(env
, addr
+ 2);
509 r1
= (insn2
>> 20) & 0xf;
510 r3
= (insn2
>> 16) & 0xf;
511 b2
= (insn2
>> 12) & 0xf;
513 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
516 cpu_abort(CPU(cpu
), "EXECUTE on instruction prefix 0x%x not implemented\n",
522 /* load access registers r1 to r3 from memory at a2 */
523 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
527 for (i
= r1
;; i
= (i
+ 1) % 16) {
528 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
537 /* store access registers r1 to r3 in memory at a2 */
538 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
542 for (i
= r1
;; i
= (i
+ 1) % 16) {
543 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
553 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
555 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
556 uint64_t dest
= get_address_31fix(env
, r1
);
557 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
558 uint64_t src
= get_address_31fix(env
, r2
);
559 uint8_t pad
= env
->regs
[r2
+ 1] >> 24;
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(env
, src
);
577 cpu_stb_data(env
, dest
, v
);
580 for (; destlen
; dest
++, destlen
--) {
581 cpu_stb_data(env
, dest
, pad
);
584 env
->regs
[r1
+ 1] = destlen
;
585 /* can't use srclen here, we trunc'ed it */
586 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
587 env
->regs
[r1
] = dest
;
593 /* move long extended another memcopy insn with more bells and whistles */
594 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
597 uint64_t destlen
= env
->regs
[r1
+ 1];
598 uint64_t dest
= env
->regs
[r1
];
599 uint64_t srclen
= env
->regs
[r3
+ 1];
600 uint64_t src
= env
->regs
[r3
];
601 uint8_t pad
= a2
& 0xff;
605 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
606 destlen
= (uint32_t)destlen
;
607 srclen
= (uint32_t)srclen
;
612 if (destlen
== srclen
) {
614 } else if (destlen
< srclen
) {
620 if (srclen
> destlen
) {
624 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
625 v
= cpu_ldub_data(env
, src
);
626 cpu_stb_data(env
, dest
, v
);
629 for (; destlen
; dest
++, destlen
--) {
630 cpu_stb_data(env
, dest
, pad
);
633 env
->regs
[r1
+ 1] = destlen
;
634 /* can't use srclen here, we trunc'ed it */
635 /* FIXME: 31-bit mode! */
636 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
637 env
->regs
[r1
] = dest
;
643 /* compare logical long extended memcompare insn with padding */
644 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
647 uint64_t destlen
= env
->regs
[r1
+ 1];
648 uint64_t dest
= get_address_31fix(env
, r1
);
649 uint64_t srclen
= env
->regs
[r3
+ 1];
650 uint64_t src
= get_address_31fix(env
, r3
);
651 uint8_t pad
= a2
& 0xff;
652 uint8_t v1
= 0, v2
= 0;
655 if (!(destlen
|| srclen
)) {
659 if (srclen
> destlen
) {
663 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
664 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
665 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
667 cc
= (v1
< v2
) ? 1 : 2;
672 env
->regs
[r1
+ 1] = destlen
;
673 /* can't use srclen here, we trunc'ed it */
674 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
675 env
->regs
[r1
] = dest
;
682 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
683 uint64_t src
, uint64_t src_len
)
685 uint64_t max_len
, len
;
686 uint64_t cksm
= (uint32_t)r1
;
688 /* Lest we fail to service interrupts in a timely manner, limit the
689 amount of work we're willing to do. For now, let's cap at 8k. */
690 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
692 /* Process full words as available. */
693 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
694 cksm
+= (uint32_t)cpu_ldl_data(env
, src
);
697 switch (max_len
- len
) {
699 cksm
+= cpu_ldub_data(env
, src
) << 24;
703 cksm
+= cpu_lduw_data(env
, src
) << 16;
707 cksm
+= cpu_lduw_data(env
, src
) << 16;
708 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
713 /* Fold the carry from the checksum. Note that we can see carry-out
714 during folding more than once (but probably not more than twice). */
715 while (cksm
> 0xffffffffull
) {
716 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
719 /* Indicate whether or not we've processed everything. */
720 env
->cc_op
= (len
== src_len
? 0 : 3);
722 /* Return both cksm and processed length. */
727 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
730 int len_dest
= len
>> 4;
731 int len_src
= len
& 0xf;
733 int second_nibble
= 0;
738 /* last byte is special, it only flips the nibbles */
739 b
= cpu_ldub_data(env
, src
);
740 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
744 /* now pad every nibble with 0xf0 */
746 while (len_dest
> 0) {
747 uint8_t cur_byte
= 0;
750 cur_byte
= cpu_ldub_data(env
, src
);
756 /* only advance one nibble at a time */
762 second_nibble
= !second_nibble
;
765 cur_byte
= (cur_byte
& 0xf);
769 cpu_stb_data(env
, dest
, cur_byte
);
773 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
778 for (i
= 0; i
<= len
; i
++) {
779 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
780 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
782 cpu_stb_data(env
, array
+ i
, new_byte
);
786 uint64_t HELPER(tre
)(CPUS390XState
*env
, uint64_t array
,
787 uint64_t len
, uint64_t trans
)
789 uint8_t end
= env
->regs
[0] & 0xff;
793 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
798 /* Lest we fail to service interrupts in a timely manner, limit the
799 amount of work we're willing to do. For now, let's cap at 8k. */
807 for (i
= 0; i
< l
; i
++) {
808 uint8_t byte
, new_byte
;
810 byte
= cpu_ldub_data(env
, array
+ i
);
817 new_byte
= cpu_ldub_data(env
, trans
+ byte
);
818 cpu_stb_data(env
, array
+ i
, new_byte
);
821 env
->retxl
= len
- i
;
825 uint32_t HELPER(trt
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
831 for (i
= 0; i
<= len
; i
++) {
832 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
833 uint8_t sbyte
= cpu_ldub_data(env
, trans
+ byte
);
836 env
->regs
[1] = array
+ i
;
837 env
->regs
[2] = (env
->regs
[2] & ~0xff) | sbyte
;
838 cc
= (i
== len
) ? 2 : 1;
846 #if !defined(CONFIG_USER_ONLY)
847 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
849 S390CPU
*cpu
= s390_env_get_cpu(env
);
850 bool PERchanged
= false;
855 for (i
= r1
;; i
= (i
+ 1) % 16) {
856 val
= cpu_ldq_data(env
, src
);
857 if (env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
861 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
862 i
, src
, env
->cregs
[i
]);
863 src
+= sizeof(uint64_t);
870 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
871 s390_cpu_recompute_watchpoints(CPU(cpu
));
874 tlb_flush(CPU(cpu
), 1);
877 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
879 S390CPU
*cpu
= s390_env_get_cpu(env
);
880 bool PERchanged
= false;
885 for (i
= r1
;; i
= (i
+ 1) % 16) {
886 val
= cpu_ldl_data(env
, src
);
887 if ((uint32_t)env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
890 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) | val
;
891 src
+= sizeof(uint32_t);
898 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
899 s390_cpu_recompute_watchpoints(CPU(cpu
));
902 tlb_flush(CPU(cpu
), 1);
905 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
910 for (i
= r1
;; i
= (i
+ 1) % 16) {
911 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
912 dest
+= sizeof(uint64_t);
920 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
925 for (i
= r1
;; i
= (i
+ 1) % 16) {
926 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
927 dest
+= sizeof(uint32_t);
935 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
942 /* insert storage key extended */
943 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
945 static S390SKeysState
*ss
;
946 static S390SKeysClass
*skeyclass
;
947 uint64_t addr
= get_address(env
, 0, 0, r2
);
950 if (addr
> ram_size
) {
955 ss
= s390_get_skeys_device();
956 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
959 if (skeyclass
->get_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
)) {
965 /* set storage key extended */
966 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
968 static S390SKeysState
*ss
;
969 static S390SKeysClass
*skeyclass
;
970 uint64_t addr
= get_address(env
, 0, 0, r2
);
973 if (addr
> ram_size
) {
978 ss
= s390_get_skeys_device();
979 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
983 skeyclass
->set_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
);
986 /* reset reference bit extended */
987 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
989 static S390SKeysState
*ss
;
990 static S390SKeysClass
*skeyclass
;
998 ss
= s390_get_skeys_device();
999 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
1002 if (skeyclass
->get_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1006 re
= key
& (SK_R
| SK_C
);
1009 if (skeyclass
->set_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1016 * 0 Reference bit zero; change bit zero
1017 * 1 Reference bit zero; change bit one
1018 * 2 Reference bit one; change bit zero
1019 * 3 Reference bit one; change bit one
1025 /* compare and swap and purge */
1026 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1028 S390CPU
*cpu
= s390_env_get_cpu(env
);
1030 uint32_t o1
= env
->regs
[r1
];
1031 uint64_t a2
= r2
& ~3ULL;
1032 uint32_t o2
= cpu_ldl_data(env
, a2
);
1035 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1037 /* flush TLB / ALB */
1038 tlb_flush(CPU(cpu
), 1);
1042 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1049 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1053 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1054 __func__
, l
, a1
, a2
);
1062 /* XXX replace w/ memcpy */
1063 for (i
= 0; i
< l
; i
++) {
1064 cpu_stb_secondary(env
, a1
+ i
, cpu_ldub_primary(env
, a2
+ i
));
1070 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1074 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1075 __func__
, l
, a1
, a2
);
1083 /* XXX replace w/ memcpy */
1084 for (i
= 0; i
< l
; i
++) {
1085 cpu_stb_primary(env
, a1
+ i
, cpu_ldub_secondary(env
, a2
+ i
));
1091 /* invalidate pte */
1092 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1094 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1095 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1098 /* XXX broadcast to other CPUs */
1100 /* XXX Linux is nice enough to give us the exact pte address.
1101 According to spec we'd have to find it out ourselves */
1102 /* XXX Linux is fine with overwriting the pte, the spec requires
1103 us to only set the invalid bit */
1104 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1106 /* XXX we exploit the fact that Linux passes the exact virtual
1107 address here - it's not obliged to! */
1108 tlb_flush_page(cs
, page
);
1110 /* XXX 31-bit hack */
1111 if (page
& 0x80000000) {
1112 tlb_flush_page(cs
, page
& ~0x80000000);
1114 tlb_flush_page(cs
, page
| 0x80000000);
1118 /* flush local tlb */
1119 void HELPER(ptlb
)(CPUS390XState
*env
)
1121 S390CPU
*cpu
= s390_env_get_cpu(env
);
1123 tlb_flush(CPU(cpu
), 1);
1126 /* load using real address */
1127 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1129 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1131 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1134 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1136 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1138 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1141 /* store using real address */
1142 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1144 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1146 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1148 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1149 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1150 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1151 /* PSW is saved just before calling the helper. */
1152 env
->per_address
= env
->psw
.addr
;
1153 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1157 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1159 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1161 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1163 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1164 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1165 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1166 /* PSW is saved just before calling the helper. */
1167 env
->per_address
= env
->psw
.addr
;
1168 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1172 /* load real address */
1173 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1175 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1177 int old_exc
= cs
->exception_index
;
1178 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1182 /* XXX incomplete - has more corner cases */
1183 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1184 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1187 cs
->exception_index
= old_exc
;
1188 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1191 if (cs
->exception_index
== EXCP_PGM
) {
1192 ret
= env
->int_pgm_code
| 0x80000000;
1194 ret
|= addr
& ~TARGET_PAGE_MASK
;
1196 cs
->exception_index
= old_exc
;