]>
git.proxmox.com Git - mirror_qemu.git/blob - target-s390x/mem_helper.c
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 /* Reduce the length so that addr + len doesn't cross a page boundary. */
58 static inline uint64_t adj_len_to_page(uint64_t len
, uint64_t addr
)
60 #ifndef CONFIG_USER_ONLY
61 if ((addr
& ~TARGET_PAGE_MASK
) + len
- 1 >= TARGET_PAGE_SIZE
) {
62 return -addr
& ~TARGET_PAGE_MASK
;
68 #ifndef CONFIG_USER_ONLY
69 static void mvc_fast_memset(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
72 S390CPU
*cpu
= s390_env_get_cpu(env
);
76 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
79 if (mmu_translate(env
, dest
, 1, asc
, &dest_phys
, &flags
, true)) {
80 cpu_stb_data(env
, dest
, byte
);
81 cpu_abort(CPU(cpu
), "should never reach here");
83 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
85 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
87 memset(dest_p
, byte
, len
);
89 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
92 static void mvc_fast_memmove(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
95 S390CPU
*cpu
= s390_env_get_cpu(env
);
101 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
104 if (mmu_translate(env
, dest
, 1, asc
, &dest_phys
, &flags
, true)) {
105 cpu_stb_data(env
, dest
, 0);
106 cpu_abort(CPU(cpu
), "should never reach here");
108 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
110 if (mmu_translate(env
, src
, 0, asc
, &src_phys
, &flags
, true)) {
111 cpu_ldub_data(env
, src
);
112 cpu_abort(CPU(cpu
), "should never reach here");
114 src_phys
|= src
& ~TARGET_PAGE_MASK
;
116 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
117 src_p
= cpu_physical_memory_map(src_phys
, &len
, 0);
119 memmove(dest_p
, src_p
, len
);
121 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
122 cpu_physical_memory_unmap(src_p
, 0, len
, len
);
127 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
134 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
135 __func__
, l
, dest
, src
);
136 for (i
= 0; i
<= l
; i
++) {
137 x
= cpu_ldub_data(env
, dest
+ i
) & cpu_ldub_data(env
, src
+ i
);
141 cpu_stb_data(env
, dest
+ i
, x
);
147 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
154 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
155 __func__
, l
, dest
, src
);
157 #ifndef CONFIG_USER_ONLY
158 /* xor with itself is the same as memset(0) */
159 if ((l
> 32) && (src
== dest
) &&
160 (src
& TARGET_PAGE_MASK
) == ((src
+ l
) & TARGET_PAGE_MASK
)) {
161 mvc_fast_memset(env
, l
+ 1, dest
, 0);
166 memset(g2h(dest
), 0, l
+ 1);
171 for (i
= 0; i
<= l
; i
++) {
172 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
176 cpu_stb_data(env
, dest
+ i
, x
);
182 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
189 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
190 __func__
, l
, dest
, src
);
191 for (i
= 0; i
<= l
; i
++) {
192 x
= cpu_ldub_data(env
, dest
+ i
) | cpu_ldub_data(env
, src
+ i
);
196 cpu_stb_data(env
, dest
+ i
, x
);
202 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
206 uint32_t l_64
= (l
+ 1) / 8;
208 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
209 __func__
, l
, dest
, src
);
211 #ifndef CONFIG_USER_ONLY
213 (src
& TARGET_PAGE_MASK
) == ((src
+ l
) & TARGET_PAGE_MASK
) &&
214 (dest
& TARGET_PAGE_MASK
) == ((dest
+ l
) & TARGET_PAGE_MASK
)) {
215 if (dest
== (src
+ 1)) {
216 mvc_fast_memset(env
, l
+ 1, dest
, cpu_ldub_data(env
, src
));
218 } else if ((src
& TARGET_PAGE_MASK
) != (dest
& TARGET_PAGE_MASK
)) {
219 mvc_fast_memmove(env
, l
+ 1, dest
, src
);
224 if (dest
== (src
+ 1)) {
225 memset(g2h(dest
), cpu_ldub_data(env
, src
), l
+ 1);
227 /* mvc and memmove do not behave the same when areas overlap! */
228 } else if ((dest
< src
) || (src
+ l
< dest
)) {
229 memmove(g2h(dest
), g2h(src
), l
+ 1);
234 /* handle the parts that fit into 8-byte loads/stores */
235 if ((dest
+ 8 <= src
) || (src
+ 8 <= dest
)) {
236 for (i
= 0; i
< l_64
; i
++) {
237 cpu_stq_data(env
, dest
+ x
, cpu_ldq_data(env
, src
+ x
));
242 /* slow version with byte accesses which always work */
243 for (i
= x
; i
<= l
; i
++) {
244 cpu_stb_data(env
, dest
+ i
, cpu_ldub_data(env
, src
+ i
));
248 /* compare unsigned byte arrays */
249 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
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
);
275 /* compare logical under mask */
276 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
282 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
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
,
300 mask
= (mask
<< 1) & 0xf;
307 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
310 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
316 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
325 return fix_address(env
, r
);
328 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
330 return fix_address(env
, env
->regs
[reg
]);
333 /* search string (c is byte to search, r2 is string, r1 end of string) */
334 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
340 str
= fix_address(env
, str
);
341 end
= fix_address(env
, end
);
343 /* Assume for now that R2 is unmodified. */
346 /* Lest we fail to service interrupts in a timely manner, limit the
347 amount of work we're willing to do. For now, let's cap at 8k. */
348 for (len
= 0; len
< 0x2000; ++len
) {
349 if (str
+ len
== end
) {
350 /* Character not found. R1 & R2 are unmodified. */
354 v
= cpu_ldub_data(env
, str
+ len
);
356 /* Character found. Set R1 to the location; R2 is unmodified. */
362 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
363 env
->retxl
= str
+ len
;
368 /* unsigned string compare (c is string terminator) */
369 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
374 s1
= fix_address(env
, s1
);
375 s2
= fix_address(env
, s2
);
377 /* Lest we fail to service interrupts in a timely manner, limit the
378 amount of work we're willing to do. For now, let's cap at 8k. */
379 for (len
= 0; len
< 0x2000; ++len
) {
380 uint8_t v1
= cpu_ldub_data(env
, s1
+ len
);
381 uint8_t v2
= cpu_ldub_data(env
, s2
+ len
);
384 /* Equal. CC=0, and don't advance the registers. */
390 /* Unequal. CC={1,2}, and advance the registers. Note that
391 the terminator need not be zero, but the string that contains
392 the terminator is by definition "low". */
393 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
394 env
->retxl
= s2
+ len
;
399 /* CPU-determined bytes equal; advance the registers. */
401 env
->retxl
= s2
+ len
;
406 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
408 /* XXX missing r0 handling */
410 #ifdef CONFIG_USER_ONLY
411 memmove(g2h(r1
), g2h(r2
), TARGET_PAGE_SIZE
);
413 mvc_fast_memmove(env
, TARGET_PAGE_SIZE
, r1
, r2
);
417 /* string copy (c is string terminator) */
418 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
423 d
= fix_address(env
, d
);
424 s
= fix_address(env
, s
);
426 /* Lest we fail to service interrupts in a timely manner, limit the
427 amount of work we're willing to do. For now, let's cap at 8k. */
428 for (len
= 0; len
< 0x2000; ++len
) {
429 uint8_t v
= cpu_ldub_data(env
, s
+ len
);
430 cpu_stb_data(env
, d
+ len
, v
);
432 /* Complete. Set CC=1 and advance R1. */
439 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
441 env
->retxl
= s
+ len
;
445 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
448 int pos
= 24; /* top of the lower half of r1 */
449 uint64_t rmask
= 0xff000000ULL
;
456 env
->regs
[r1
] &= ~rmask
;
457 val
= cpu_ldub_data(env
, address
);
458 if ((val
& 0x80) && !ccd
) {
462 if (val
&& cc
== 0) {
465 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
468 mask
= (mask
<< 1) & 0xf;
476 /* execute instruction
477 this instruction executes an insn modified with the contents of r1
478 it does not change the executed instruction in memory
479 it does not change the program counter
480 in other words: tricky...
481 currently implemented by interpreting the cases it is most commonly used in
483 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
484 uint64_t addr
, uint64_t ret
)
486 S390CPU
*cpu
= s390_env_get_cpu(env
);
487 uint16_t insn
= cpu_lduw_code(env
, addr
);
489 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
491 if ((insn
& 0xf0ff) == 0xd000) {
492 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
495 insn2
= cpu_ldl_code(env
, addr
+ 2);
496 b1
= (insn2
>> 28) & 0xf;
497 b2
= (insn2
>> 12) & 0xf;
498 d1
= (insn2
>> 16) & 0xfff;
500 switch (insn
& 0xf00) {
502 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
503 get_address(env
, 0, b2
, d2
));
506 cc
= helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
507 get_address(env
, 0, b2
, d2
));
510 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
511 get_address(env
, 0, b2
, d2
));
514 cc
= helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
515 get_address(env
, 0, b2
, d2
));
518 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
519 get_address(env
, 0, b2
, d2
));
522 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
523 get_address(env
, 0, b2
, d2
));
525 cc
= helper_trt(env
, l
, get_address(env
, 0, b1
, d1
),
526 get_address(env
, 0, b2
, d2
));
531 } else if ((insn
& 0xff00) == 0x0a00) {
532 /* supervisor call */
533 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
534 env
->psw
.addr
= ret
- 4;
535 env
->int_svc_code
= (insn
| v1
) & 0xff;
536 env
->int_svc_ilen
= 4;
537 helper_exception(env
, EXCP_SVC
);
538 } else if ((insn
& 0xff00) == 0xbf00) {
539 uint32_t insn2
, r1
, r3
, b2
, d2
;
541 insn2
= cpu_ldl_code(env
, addr
+ 2);
542 r1
= (insn2
>> 20) & 0xf;
543 r3
= (insn2
>> 16) & 0xf;
544 b2
= (insn2
>> 12) & 0xf;
546 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
549 cpu_abort(CPU(cpu
), "EXECUTE on instruction prefix 0x%x not implemented\n",
555 /* load access registers r1 to r3 from memory at a2 */
556 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
560 for (i
= r1
;; i
= (i
+ 1) % 16) {
561 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
570 /* store access registers r1 to r3 in memory at a2 */
571 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
575 for (i
= r1
;; i
= (i
+ 1) % 16) {
576 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
586 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
588 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
589 uint64_t dest
= get_address_31fix(env
, r1
);
590 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
591 uint64_t src
= get_address_31fix(env
, r2
);
592 uint8_t pad
= src
>> 24;
596 if (destlen
== srclen
) {
598 } else if (destlen
< srclen
) {
604 if (srclen
> destlen
) {
608 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
609 v
= cpu_ldub_data(env
, src
);
610 cpu_stb_data(env
, dest
, v
);
613 for (; destlen
; dest
++, destlen
--) {
614 cpu_stb_data(env
, dest
, pad
);
617 env
->regs
[r1
+ 1] = destlen
;
618 /* can't use srclen here, we trunc'ed it */
619 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
620 env
->regs
[r1
] = dest
;
626 /* move long extended another memcopy insn with more bells and whistles */
627 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
630 uint64_t destlen
= env
->regs
[r1
+ 1];
631 uint64_t dest
= env
->regs
[r1
];
632 uint64_t srclen
= env
->regs
[r3
+ 1];
633 uint64_t src
= env
->regs
[r3
];
634 uint8_t pad
= a2
& 0xff;
638 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
639 destlen
= (uint32_t)destlen
;
640 srclen
= (uint32_t)srclen
;
645 if (destlen
== srclen
) {
647 } else if (destlen
< srclen
) {
653 if (srclen
> destlen
) {
657 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
658 v
= cpu_ldub_data(env
, src
);
659 cpu_stb_data(env
, dest
, v
);
662 for (; destlen
; dest
++, destlen
--) {
663 cpu_stb_data(env
, dest
, pad
);
666 env
->regs
[r1
+ 1] = destlen
;
667 /* can't use srclen here, we trunc'ed it */
668 /* FIXME: 31-bit mode! */
669 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
670 env
->regs
[r1
] = dest
;
676 /* compare logical long extended memcompare insn with padding */
677 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
680 uint64_t destlen
= env
->regs
[r1
+ 1];
681 uint64_t dest
= get_address_31fix(env
, r1
);
682 uint64_t srclen
= env
->regs
[r3
+ 1];
683 uint64_t src
= get_address_31fix(env
, r3
);
684 uint8_t pad
= a2
& 0xff;
685 uint8_t v1
= 0, v2
= 0;
688 if (!(destlen
|| srclen
)) {
692 if (srclen
> destlen
) {
696 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
697 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
698 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
700 cc
= (v1
< v2
) ? 1 : 2;
705 env
->regs
[r1
+ 1] = destlen
;
706 /* can't use srclen here, we trunc'ed it */
707 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
708 env
->regs
[r1
] = dest
;
715 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
716 uint64_t src
, uint64_t src_len
)
718 uint64_t max_len
, len
;
719 uint64_t cksm
= (uint32_t)r1
;
721 /* Lest we fail to service interrupts in a timely manner, limit the
722 amount of work we're willing to do. For now, let's cap at 8k. */
723 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
725 /* Process full words as available. */
726 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
727 cksm
+= (uint32_t)cpu_ldl_data(env
, src
);
730 switch (max_len
- len
) {
732 cksm
+= cpu_ldub_data(env
, src
) << 24;
736 cksm
+= cpu_lduw_data(env
, src
) << 16;
740 cksm
+= cpu_lduw_data(env
, src
) << 16;
741 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
746 /* Fold the carry from the checksum. Note that we can see carry-out
747 during folding more than once (but probably not more than twice). */
748 while (cksm
> 0xffffffffull
) {
749 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
752 /* Indicate whether or not we've processed everything. */
753 env
->cc_op
= (len
== src_len
? 0 : 3);
755 /* Return both cksm and processed length. */
760 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
763 int len_dest
= len
>> 4;
764 int len_src
= len
& 0xf;
766 int second_nibble
= 0;
771 /* last byte is special, it only flips the nibbles */
772 b
= cpu_ldub_data(env
, src
);
773 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
777 /* now pad every nibble with 0xf0 */
779 while (len_dest
> 0) {
780 uint8_t cur_byte
= 0;
783 cur_byte
= cpu_ldub_data(env
, src
);
789 /* only advance one nibble at a time */
795 second_nibble
= !second_nibble
;
798 cur_byte
= (cur_byte
& 0xf);
802 cpu_stb_data(env
, dest
, cur_byte
);
806 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
811 for (i
= 0; i
<= len
; i
++) {
812 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
813 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
815 cpu_stb_data(env
, array
+ i
, new_byte
);
819 uint64_t HELPER(tre
)(CPUS390XState
*env
, uint64_t array
,
820 uint64_t len
, uint64_t trans
)
822 uint8_t end
= env
->regs
[0] & 0xff;
826 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
831 /* Lest we fail to service interrupts in a timely manner, limit the
832 amount of work we're willing to do. For now, let's cap at 8k. */
840 for (i
= 0; i
< l
; i
++) {
841 uint8_t byte
, new_byte
;
843 byte
= cpu_ldub_data(env
, array
+ i
);
850 new_byte
= cpu_ldub_data(env
, trans
+ byte
);
851 cpu_stb_data(env
, array
+ i
, new_byte
);
854 env
->retxl
= len
- i
;
858 uint32_t HELPER(trt
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
864 for (i
= 0; i
<= len
; i
++) {
865 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
866 uint8_t sbyte
= cpu_ldub_data(env
, trans
+ byte
);
869 env
->regs
[1] = array
+ i
;
870 env
->regs
[2] = (env
->regs
[2] & ~0xff) | sbyte
;
871 cc
= (i
== len
) ? 2 : 1;
879 #if !defined(CONFIG_USER_ONLY)
880 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
882 S390CPU
*cpu
= s390_env_get_cpu(env
);
886 for (i
= r1
;; i
= (i
+ 1) % 16) {
887 env
->cregs
[i
] = cpu_ldq_data(env
, src
);
888 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
889 i
, src
, env
->cregs
[i
]);
890 src
+= sizeof(uint64_t);
897 tlb_flush(CPU(cpu
), 1);
900 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
902 S390CPU
*cpu
= s390_env_get_cpu(env
);
906 for (i
= r1
;; i
= (i
+ 1) % 16) {
907 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) |
908 cpu_ldl_data(env
, src
);
909 src
+= sizeof(uint32_t);
916 tlb_flush(CPU(cpu
), 1);
919 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
924 for (i
= r1
;; i
= (i
+ 1) % 16) {
925 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
926 dest
+= sizeof(uint64_t);
934 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
939 for (i
= r1
;; i
= (i
+ 1) % 16) {
940 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
941 dest
+= sizeof(uint32_t);
949 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
956 /* insert storage key extended */
957 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
959 uint64_t addr
= get_address(env
, 0, 0, r2
);
961 if (addr
> ram_size
) {
965 return env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
];
968 /* set storage key extended */
969 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
971 uint64_t addr
= get_address(env
, 0, 0, r2
);
973 if (addr
> ram_size
) {
977 env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
] = r1
;
980 /* reset reference bit extended */
981 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
990 key
= env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
];
991 re
= key
& (SK_R
| SK_C
);
992 env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
] = (key
& ~SK_R
);
997 * 0 Reference bit zero; change bit zero
998 * 1 Reference bit zero; change bit one
999 * 2 Reference bit one; change bit zero
1000 * 3 Reference bit one; change bit one
1006 /* compare and swap and purge */
1007 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1009 S390CPU
*cpu
= s390_env_get_cpu(env
);
1011 uint32_t o1
= env
->regs
[r1
];
1012 uint64_t a2
= r2
& ~3ULL;
1013 uint32_t o2
= cpu_ldl_data(env
, a2
);
1016 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1018 /* flush TLB / ALB */
1019 tlb_flush(CPU(cpu
), 1);
1023 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1030 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1034 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1035 __func__
, l
, a1
, a2
);
1043 /* XXX replace w/ memcpy */
1044 for (i
= 0; i
< l
; i
++) {
1045 cpu_stb_secondary(env
, a1
+ i
, cpu_ldub_primary(env
, a2
+ i
));
1051 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1055 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1056 __func__
, l
, a1
, a2
);
1064 /* XXX replace w/ memcpy */
1065 for (i
= 0; i
< l
; i
++) {
1066 cpu_stb_primary(env
, a1
+ i
, cpu_ldub_secondary(env
, a2
+ i
));
1072 /* invalidate pte */
1073 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1075 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1076 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1079 /* XXX broadcast to other CPUs */
1081 /* XXX Linux is nice enough to give us the exact pte address.
1082 According to spec we'd have to find it out ourselves */
1083 /* XXX Linux is fine with overwriting the pte, the spec requires
1084 us to only set the invalid bit */
1085 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1087 /* XXX we exploit the fact that Linux passes the exact virtual
1088 address here - it's not obliged to! */
1089 tlb_flush_page(cs
, page
);
1091 /* XXX 31-bit hack */
1092 if (page
& 0x80000000) {
1093 tlb_flush_page(cs
, page
& ~0x80000000);
1095 tlb_flush_page(cs
, page
| 0x80000000);
1099 /* flush local tlb */
1100 void HELPER(ptlb
)(CPUS390XState
*env
)
1102 S390CPU
*cpu
= s390_env_get_cpu(env
);
1104 tlb_flush(CPU(cpu
), 1);
1107 /* load using real address */
1108 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1110 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1112 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1115 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1117 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1119 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1122 /* store using real address */
1123 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1125 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1127 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1130 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1132 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1134 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1137 /* load real address */
1138 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1140 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1142 int old_exc
= cs
->exception_index
;
1143 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1147 /* XXX incomplete - has more corner cases */
1148 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1149 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1152 cs
->exception_index
= old_exc
;
1153 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1156 if (cs
->exception_index
== EXCP_PGM
) {
1157 ret
= env
->int_pgm_code
| 0x80000000;
1159 ret
|= addr
& ~TARGET_PAGE_MASK
;
1161 cs
->exception_index
= old_exc
;