diff options
author | Cho, Yu-Chen <acho@suse.com> | 2021-07-07 18:53:17 +0800 |
---|---|---|
committer | Cornelia Huck <cohuck@redhat.com> | 2021-07-07 14:01:56 +0200 |
commit | c9274b6bf0571ecbaaed3e9c3b229e17607a0ea2 (patch) | |
tree | ed9756bc3e44ba8db0e02bc9975347f6b898f1c1 /target/s390x/mem_helper.c | |
parent | b6b4722307f31491ee553c674ded2a8bba6173e1 (diff) |
target/s390x: start moving TCG-only code to tcg/
move everything related to translate, as well as HELPER code in tcg/
mmu_helper.c stays put for now, as it contains both TCG and KVM code.
After the reshuffling, update MAINTAINERS accordingly.
Make use of the new directory:
target/s390x/tcg/
Signed-off-by: Claudio Fontana <cfontana@suse.de>
Signed-off-by: Cho, Yu-Chen <acho@suse.com>
Acked-by: David Hildenbrand <david@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Message-Id: <20210707105324.23400-8-acho@suse.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'target/s390x/mem_helper.c')
-rw-r--r-- | target/s390x/mem_helper.c | 3008 |
1 files changed, 0 insertions, 3008 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c deleted file mode 100644 index 9bae13ecf0..0000000000 --- a/target/s390x/mem_helper.c +++ /dev/null @@ -1,3008 +0,0 @@ -/* - * S/390 memory access helper routines - * - * Copyright (c) 2009 Ulrich Hecht - * Copyright (c) 2009 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "s390x-internal.h" -#include "tcg_s390x.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "qemu/int128.h" -#include "qemu/atomic128.h" -#include "tcg/tcg.h" - -#if !defined(CONFIG_USER_ONLY) -#include "hw/s390x/storage-keys.h" -#include "hw/boards.h" -#endif - -/*****************************************************************************/ -/* Softmmu support */ - -/* #define DEBUG_HELPER */ -#ifdef DEBUG_HELPER -#define HELPER_LOG(x...) qemu_log(x) -#else -#define HELPER_LOG(x...) -#endif - -static inline bool psw_key_valid(CPUS390XState *env, uint8_t psw_key) -{ - uint16_t pkm = env->cregs[3] >> 16; - - if (env->psw.mask & PSW_MASK_PSTATE) { - /* PSW key has range 0..15, it is valid if the bit is 1 in the PKM */ - return pkm & (0x80 >> psw_key); - } - return true; -} - -static bool is_destructive_overlap(CPUS390XState *env, uint64_t dest, - uint64_t src, uint32_t len) -{ - if (!len || src == dest) { - return false; - } - /* Take care of wrapping at the end of address space. */ - if (unlikely(wrap_address(env, src + len - 1) < src)) { - return dest > src || dest <= wrap_address(env, src + len - 1); - } - return dest > src && dest <= src + len - 1; -} - -/* Trigger a SPECIFICATION exception if an address or a length is not - naturally aligned. */ -static inline void check_alignment(CPUS390XState *env, uint64_t v, - int wordsize, uintptr_t ra) -{ - if (v % wordsize) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } -} - -/* Load a value from memory according to its size. */ -static inline uint64_t cpu_ldusize_data_ra(CPUS390XState *env, uint64_t addr, - int wordsize, uintptr_t ra) -{ - switch (wordsize) { - case 1: - return cpu_ldub_data_ra(env, addr, ra); - case 2: - return cpu_lduw_data_ra(env, addr, ra); - default: - abort(); - } -} - -/* Store a to memory according to its size. */ -static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr, - uint64_t value, int wordsize, - uintptr_t ra) -{ - switch (wordsize) { - case 1: - cpu_stb_data_ra(env, addr, value, ra); - break; - case 2: - cpu_stw_data_ra(env, addr, value, ra); - break; - default: - abort(); - } -} - -/* An access covers at most 4096 bytes and therefore at most two pages. */ -typedef struct S390Access { - target_ulong vaddr1; - target_ulong vaddr2; - char *haddr1; - char *haddr2; - uint16_t size1; - uint16_t size2; - /* - * If we can't access the host page directly, we'll have to do I/O access - * via ld/st helpers. These are internal details, so we store the - * mmu idx to do the access here instead of passing it around in the - * helpers. Maybe, one day we can get rid of ld/st access - once we can - * handle TLB_NOTDIRTY differently. We don't expect these special accesses - * to trigger exceptions - only if we would have TLB_NOTDIRTY on LAP - * pages, we might trigger a new MMU translation - very unlikely that - * the mapping changes in between and we would trigger a fault. - */ - int mmu_idx; -} S390Access; - -/* - * With nonfault=1, return the PGM_ exception that would have been injected - * into the guest; return 0 if no exception was detected. - * - * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec. - * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr. - */ -static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t ra) -{ - int flags; - -#if defined(CONFIG_USER_ONLY) - flags = page_get_flags(addr); - if (!(flags & (access_type == MMU_DATA_LOAD ? PAGE_READ : PAGE_WRITE_ORG))) { - env->__excp_addr = addr; - flags = (flags & PAGE_VALID) ? PGM_PROTECTION : PGM_ADDRESSING; - if (nonfault) { - return flags; - } - tcg_s390_program_interrupt(env, flags, ra); - } - *phost = g2h(env_cpu(env), addr); -#else - /* - * For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL - * to detect if there was an exception during tlb_fill(). - */ - env->tlb_fill_exc = 0; - flags = probe_access_flags(env, addr, access_type, mmu_idx, nonfault, phost, - ra); - if (env->tlb_fill_exc) { - return env->tlb_fill_exc; - } - - if (unlikely(flags & TLB_WATCHPOINT)) { - /* S390 does not presently use transaction attributes. */ - cpu_check_watchpoint(env_cpu(env), addr, size, - MEMTXATTRS_UNSPECIFIED, - (access_type == MMU_DATA_STORE - ? BP_MEM_WRITE : BP_MEM_READ), ra); - } -#endif - return 0; -} - -static int access_prepare_nf(S390Access *access, CPUS390XState *env, - bool nonfault, vaddr vaddr1, int size, - MMUAccessType access_type, - int mmu_idx, uintptr_t ra) -{ - void *haddr1, *haddr2 = NULL; - int size1, size2, exc; - vaddr vaddr2 = 0; - - assert(size > 0 && size <= 4096); - - size1 = MIN(size, -(vaddr1 | TARGET_PAGE_MASK)), - size2 = size - size1; - - exc = s390_probe_access(env, vaddr1, size1, access_type, mmu_idx, nonfault, - &haddr1, ra); - if (exc) { - return exc; - } - if (unlikely(size2)) { - /* The access crosses page boundaries. */ - vaddr2 = wrap_address(env, vaddr1 + size1); - exc = s390_probe_access(env, vaddr2, size2, access_type, mmu_idx, - nonfault, &haddr2, ra); - if (exc) { - return exc; - } - } - - *access = (S390Access) { - .vaddr1 = vaddr1, - .vaddr2 = vaddr2, - .haddr1 = haddr1, - .haddr2 = haddr2, - .size1 = size1, - .size2 = size2, - .mmu_idx = mmu_idx - }; - return 0; -} - -static S390Access access_prepare(CPUS390XState *env, vaddr vaddr, int size, - MMUAccessType access_type, int mmu_idx, - uintptr_t ra) -{ - S390Access ret; - int exc = access_prepare_nf(&ret, env, false, vaddr, size, - access_type, mmu_idx, ra); - assert(!exc); - return ret; -} - -/* Helper to handle memset on a single page. */ -static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, - uint8_t byte, uint16_t size, int mmu_idx, - uintptr_t ra) -{ -#ifdef CONFIG_USER_ONLY - g_assert(haddr); - memset(haddr, byte, size); -#else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - int i; - - if (likely(haddr)) { - memset(haddr, byte, size); - } else { - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - g_assert(size > 0); - helper_ret_stb_mmu(env, vaddr, byte, oi, ra); - haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); - if (likely(haddr)) { - memset(haddr + 1, byte, size - 1); - } else { - for (i = 1; i < size; i++) { - helper_ret_stb_mmu(env, vaddr + i, byte, oi, ra); - } - } - } -#endif -} - -static void access_memset(CPUS390XState *env, S390Access *desta, - uint8_t byte, uintptr_t ra) -{ - - do_access_memset(env, desta->vaddr1, desta->haddr1, byte, desta->size1, - desta->mmu_idx, ra); - if (likely(!desta->size2)) { - return; - } - do_access_memset(env, desta->vaddr2, desta->haddr2, byte, desta->size2, - desta->mmu_idx, ra); -} - -static uint8_t do_access_get_byte(CPUS390XState *env, vaddr vaddr, char **haddr, - int offset, int mmu_idx, uintptr_t ra) -{ -#ifdef CONFIG_USER_ONLY - return ldub_p(*haddr + offset); -#else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - uint8_t byte; - - if (likely(*haddr)) { - return ldub_p(*haddr + offset); - } - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - byte = helper_ret_ldub_mmu(env, vaddr + offset, oi, ra); - *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_LOAD, mmu_idx); - return byte; -#endif -} - -static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, - int offset, uintptr_t ra) -{ - if (offset < access->size1) { - return do_access_get_byte(env, access->vaddr1, &access->haddr1, - offset, access->mmu_idx, ra); - } - return do_access_get_byte(env, access->vaddr2, &access->haddr2, - offset - access->size1, access->mmu_idx, ra); -} - -static void do_access_set_byte(CPUS390XState *env, vaddr vaddr, char **haddr, - int offset, uint8_t byte, int mmu_idx, - uintptr_t ra) -{ -#ifdef CONFIG_USER_ONLY - stb_p(*haddr + offset, byte); -#else - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - - if (likely(*haddr)) { - stb_p(*haddr + offset, byte); - return; - } - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - helper_ret_stb_mmu(env, vaddr + offset, byte, oi, ra); - *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); -#endif -} - -static void access_set_byte(CPUS390XState *env, S390Access *access, - int offset, uint8_t byte, uintptr_t ra) -{ - if (offset < access->size1) { - do_access_set_byte(env, access->vaddr1, &access->haddr1, offset, byte, - access->mmu_idx, ra); - } else { - do_access_set_byte(env, access->vaddr2, &access->haddr2, - offset - access->size1, byte, access->mmu_idx, ra); - } -} - -/* - * Move data with the same semantics as memmove() in case ranges don't overlap - * or src > dest. Undefined behavior on destructive overlaps. - */ -static void access_memmove(CPUS390XState *env, S390Access *desta, - S390Access *srca, uintptr_t ra) -{ - int diff; - - g_assert(desta->size1 + desta->size2 == srca->size1 + srca->size2); - - /* Fallback to slow access in case we don't have access to all host pages */ - if (unlikely(!desta->haddr1 || (desta->size2 && !desta->haddr2) || - !srca->haddr1 || (srca->size2 && !srca->haddr2))) { - int i; - - for (i = 0; i < desta->size1 + desta->size2; i++) { - uint8_t byte = access_get_byte(env, srca, i, ra); - - access_set_byte(env, desta, i, byte, ra); - } - return; - } - - if (srca->size1 == desta->size1) { - memmove(desta->haddr1, srca->haddr1, srca->size1); - if (unlikely(srca->size2)) { - memmove(desta->haddr2, srca->haddr2, srca->size2); - } - } else if (srca->size1 < desta->size1) { - diff = desta->size1 - srca->size1; - memmove(desta->haddr1, srca->haddr1, srca->size1); - memmove(desta->haddr1 + srca->size1, srca->haddr2, diff); - if (likely(desta->size2)) { - memmove(desta->haddr2, srca->haddr2 + diff, desta->size2); - } - } else { - diff = srca->size1 - desta->size1; - memmove(desta->haddr1, srca->haddr1, desta->size1); - memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); - if (likely(srca->size2)) { - memmove(desta->haddr2 + diff, srca->haddr2, srca->size2); - } - } -} - -static int mmu_idx_from_as(uint8_t as) -{ - switch (as) { - case AS_PRIMARY: - return MMU_PRIMARY_IDX; - case AS_SECONDARY: - return MMU_SECONDARY_IDX; - case AS_HOME: - return MMU_HOME_IDX; - default: - /* FIXME AS_ACCREG */ - g_assert_not_reached(); - } -} - -/* and on array */ -static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src, uintptr_t ra) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca1, srca2, desta; - uint32_t i; - uint8_t c = 0; - - HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", - __func__, l, dest, src); - - /* NC always processes one more byte than specified - maximum is 256 */ - l++; - - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < l; i++) { - const uint8_t x = access_get_byte(env, &srca1, i, ra) & - access_get_byte(env, &srca2, i, ra); - - c |= x; - access_set_byte(env, &desta, i, x, ra); - } - return c != 0; -} - -uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src) -{ - return do_helper_nc(env, l, dest, src, GETPC()); -} - -/* xor on array */ -static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src, uintptr_t ra) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca1, srca2, desta; - uint32_t i; - uint8_t c = 0; - - HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", - __func__, l, dest, src); - - /* XC always processes one more byte than specified - maximum is 256 */ - l++; - - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - - /* xor with itself is the same as memset(0) */ - if (src == dest) { - access_memset(env, &desta, 0, ra); - return 0; - } - - for (i = 0; i < l; i++) { - const uint8_t x = access_get_byte(env, &srca1, i, ra) ^ - access_get_byte(env, &srca2, i, ra); - - c |= x; - access_set_byte(env, &desta, i, x, ra); - } - return c != 0; -} - -uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src) -{ - return do_helper_xc(env, l, dest, src, GETPC()); -} - -/* or on array */ -static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src, uintptr_t ra) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca1, srca2, desta; - uint32_t i; - uint8_t c = 0; - - HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", - __func__, l, dest, src); - - /* OC always processes one more byte than specified - maximum is 256 */ - l++; - - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < l; i++) { - const uint8_t x = access_get_byte(env, &srca1, i, ra) | - access_get_byte(env, &srca2, i, ra); - - c |= x; - access_set_byte(env, &desta, i, x, ra); - } - return c != 0; -} - -uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src) -{ - return do_helper_oc(env, l, dest, src, GETPC()); -} - -/* memmove */ -static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest, - uint64_t src, uintptr_t ra) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca, desta; - uint32_t i; - - HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", - __func__, l, dest, src); - - /* MVC always copies one more byte than specified - maximum is 256 */ - l++; - - srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - - /* - * "When the operands overlap, the result is obtained as if the operands - * were processed one byte at a time". Only non-destructive overlaps - * behave like memmove(). - */ - if (dest == src + 1) { - access_memset(env, &desta, access_get_byte(env, &srca, 0, ra), ra); - } else if (!is_destructive_overlap(env, dest, src, l)) { - access_memmove(env, &desta, &srca, ra); - } else { - for (i = 0; i < l; i++) { - uint8_t byte = access_get_byte(env, &srca, i, ra); - - access_set_byte(env, &desta, i, byte, ra); - } - } - - return env->cc_op; -} - -void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) -{ - do_helper_mvc(env, l, dest, src, GETPC()); -} - -/* move inverse */ -void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca, desta; - uintptr_t ra = GETPC(); - int i; - - /* MVCIN always copies one more byte than specified - maximum is 256 */ - l++; - - src = wrap_address(env, src - l + 1); - srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < l; i++) { - const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra); - - access_set_byte(env, &desta, i, x, ra); - } -} - -/* move numerics */ -void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca1, srca2, desta; - uintptr_t ra = GETPC(); - int i; - - /* MVN always copies one more byte than specified - maximum is 256 */ - l++; - - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < l; i++) { - const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0x0f) | - (access_get_byte(env, &srca2, i, ra) & 0xf0); - - access_set_byte(env, &desta, i, x, ra); - } -} - -/* move with offset */ -void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) -{ - const int mmu_idx = cpu_mmu_index(env, false); - /* MVO always processes one more byte than specified - maximum is 16 */ - const int len_dest = (l >> 4) + 1; - const int len_src = (l & 0xf) + 1; - uintptr_t ra = GETPC(); - uint8_t byte_dest, byte_src; - S390Access srca, desta; - int i, j; - - srca = access_prepare(env, src, len_src, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, len_dest, MMU_DATA_STORE, mmu_idx, ra); - - /* Handle rightmost byte */ - byte_dest = cpu_ldub_data_ra(env, dest + len_dest - 1, ra); - byte_src = access_get_byte(env, &srca, len_src - 1, ra); - byte_dest = (byte_dest & 0x0f) | (byte_src << 4); - access_set_byte(env, &desta, len_dest - 1, byte_dest, ra); - - /* Process remaining bytes from right to left */ - for (i = len_dest - 2, j = len_src - 2; i >= 0; i--, j--) { - byte_dest = byte_src >> 4; - if (j >= 0) { - byte_src = access_get_byte(env, &srca, j, ra); - } else { - byte_src = 0; - } - byte_dest |= byte_src << 4; - access_set_byte(env, &desta, i, byte_dest, ra); - } -} - -/* move zones */ -void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) -{ - const int mmu_idx = cpu_mmu_index(env, false); - S390Access srca1, srca2, desta; - uintptr_t ra = GETPC(); - int i; - - /* MVZ always copies one more byte than specified - maximum is 256 */ - l++; - - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < l; i++) { - const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0xf0) | - (access_get_byte(env, &srca2, i, ra) & 0x0f); - - access_set_byte(env, &desta, i, x, ra); - } -} - -/* compare unsigned byte arrays */ -static uint32_t do_helper_clc(CPUS390XState *env, uint32_t l, uint64_t s1, - uint64_t s2, uintptr_t ra) -{ - uint32_t i; - uint32_t cc = 0; - - HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", - __func__, l, s1, s2); - - for (i = 0; i <= l; i++) { - uint8_t x = cpu_ldub_data_ra(env, s1 + i, ra); - uint8_t y = cpu_ldub_data_ra(env, s2 + i, ra); - HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y); - if (x < y) { - cc = 1; - break; - } else if (x > y) { - cc = 2; - break; - } - } - - HELPER_LOG("\n"); - return cc; -} - -uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2) -{ - return do_helper_clc(env, l, s1, s2, GETPC()); -} - -/* compare logical under mask */ -uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, - uint64_t addr) -{ - uintptr_t ra = GETPC(); - uint32_t cc = 0; - - HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, - mask, addr); - - while (mask) { - if (mask & 8) { - uint8_t d = cpu_ldub_data_ra(env, addr, ra); - uint8_t r = extract32(r1, 24, 8); - HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d, - addr); - if (r < d) { - cc = 1; - break; - } else if (r > d) { - cc = 2; - break; - } - addr++; - } - mask = (mask << 1) & 0xf; - r1 <<= 8; - } - - HELPER_LOG("\n"); - return cc; -} - -static inline uint64_t get_address(CPUS390XState *env, int reg) -{ - return wrap_address(env, env->regs[reg]); -} - -/* - * Store the address to the given register, zeroing out unused leftmost - * bits in bit positions 32-63 (24-bit and 31-bit mode only). - */ -static inline void set_address_zero(CPUS390XState *env, int reg, - uint64_t address) -{ - if (env->psw.mask & PSW_MASK_64) { - env->regs[reg] = address; - } else { - if (!(env->psw.mask & PSW_MASK_32)) { - address &= 0x00ffffff; - } else { - address &= 0x7fffffff; - } - env->regs[reg] = deposit64(env->regs[reg], 0, 32, address); - } -} - -static inline void set_address(CPUS390XState *env, int reg, uint64_t address) -{ - if (env->psw.mask & PSW_MASK_64) { - /* 64-Bit mode */ - env->regs[reg] = address; - } else { - if (!(env->psw.mask & PSW_MASK_32)) { - /* 24-Bit mode. According to the PoO it is implementation - dependent if bits 32-39 remain unchanged or are set to - zeros. Choose the former so that the function can also be - used for TRT. */ - env->regs[reg] = deposit64(env->regs[reg], 0, 24, address); - } else { - /* 31-Bit mode. According to the PoO it is implementation - dependent if bit 32 remains unchanged or is set to zero. - Choose the latter so that the function can also be used for - TRT. */ - address &= 0x7fffffff; - env->regs[reg] = deposit64(env->regs[reg], 0, 32, address); - } - } -} - -static inline uint64_t wrap_length32(CPUS390XState *env, uint64_t length) -{ - if (!(env->psw.mask & PSW_MASK_64)) { - return (uint32_t)length; - } - return length; -} - -static inline uint64_t wrap_length31(CPUS390XState *env, uint64_t length) -{ - if (!(env->psw.mask & PSW_MASK_64)) { - /* 24-Bit and 31-Bit mode */ - length &= 0x7fffffff; - } - return length; -} - -static inline uint64_t get_length(CPUS390XState *env, int reg) -{ - return wrap_length31(env, env->regs[reg]); -} - -static inline void set_length(CPUS390XState *env, int reg, uint64_t length) -{ - if (env->psw.mask & PSW_MASK_64) { - /* 64-Bit mode */ - env->regs[reg] = length; - } else { - /* 24-Bit and 31-Bit mode */ - env->regs[reg] = deposit64(env->regs[reg], 0, 32, length); - } -} - -/* search string (c is byte to search, r2 is string, r1 end of string) */ -void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2) -{ - uintptr_t ra = GETPC(); - uint64_t end, str; - uint32_t len; - uint8_t v, c = env->regs[0]; - - /* Bits 32-55 must contain all 0. */ - if (env->regs[0] & 0xffffff00u) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - str = get_address(env, r2); - end = get_address(env, r1); - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 8k. */ - for (len = 0; len < 0x2000; ++len) { - if (str + len == end) { - /* Character not found. R1 & R2 are unmodified. */ - env->cc_op = 2; - return; - } - v = cpu_ldub_data_ra(env, str + len, ra); - if (v == c) { - /* Character found. Set R1 to the location; R2 is unmodified. */ - env->cc_op = 1; - set_address(env, r1, str + len); - return; - } - } - - /* CPU-determined bytes processed. Advance R2 to next byte to process. */ - env->cc_op = 3; - set_address(env, r2, str + len); -} - -void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2) -{ - uintptr_t ra = GETPC(); - uint32_t len; - uint16_t v, c = env->regs[0]; - uint64_t end, str, adj_end; - - /* Bits 32-47 of R0 must be zero. */ - if (env->regs[0] & 0xffff0000u) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - str = get_address(env, r2); - end = get_address(env, r1); - - /* If the LSB of the two addresses differ, use one extra byte. */ - adj_end = end + ((str ^ end) & 1); - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 8k. */ - for (len = 0; len < 0x2000; len += 2) { - if (str + len == adj_end) { - /* End of input found. */ - env->cc_op = 2; - return; - } - v = cpu_lduw_data_ra(env, str + len, ra); - if (v == c) { - /* Character found. Set R1 to the location; R2 is unmodified. */ - env->cc_op = 1; - set_address(env, r1, str + len); - return; - } - } - - /* CPU-determined bytes processed. Advance R2 to next byte to process. */ - env->cc_op = 3; - set_address(env, r2, str + len); -} - -/* unsigned string compare (c is string terminator) */ -uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) -{ - uintptr_t ra = GETPC(); - uint32_t len; - - c = c & 0xff; - s1 = wrap_address(env, s1); - s2 = wrap_address(env, s2); - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 8k. */ - for (len = 0; len < 0x2000; ++len) { - uint8_t v1 = cpu_ldub_data_ra(env, s1 + len, ra); - uint8_t v2 = cpu_ldub_data_ra(env, s2 + len, ra); - if (v1 == v2) { - if (v1 == c) { - /* Equal. CC=0, and don't advance the registers. */ - env->cc_op = 0; - env->retxl = s2; - return s1; - } - } else { - /* Unequal. CC={1,2}, and advance the registers. Note that - the terminator need not be zero, but the string that contains - the terminator is by definition "low". */ - env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2); - env->retxl = s2 + len; - return s1 + len; - } - } - - /* CPU-determined bytes equal; advance the registers. */ - env->cc_op = 3; - env->retxl = s2 + len; - return s1 + len; -} - -/* move page */ -uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint32_t r1, uint32_t r2) -{ - const uint64_t src = get_address(env, r2) & TARGET_PAGE_MASK; - const uint64_t dst = get_address(env, r1) & TARGET_PAGE_MASK; - const int mmu_idx = cpu_mmu_index(env, false); - const bool f = extract64(r0, 11, 1); - const bool s = extract64(r0, 10, 1); - const bool cco = extract64(r0, 8, 1); - uintptr_t ra = GETPC(); - S390Access srca, desta; - int exc; - - if ((f && s) || extract64(r0, 12, 4)) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); - } - - /* - * We always manually handle exceptions such that we can properly store - * r1/r2 to the lowcore on page-translation exceptions. - * - * TODO: Access key handling - */ - exc = access_prepare_nf(&srca, env, true, src, TARGET_PAGE_SIZE, - MMU_DATA_LOAD, mmu_idx, ra); - if (exc) { - if (cco) { - return 2; - } - goto inject_exc; - } - exc = access_prepare_nf(&desta, env, true, dst, TARGET_PAGE_SIZE, - MMU_DATA_STORE, mmu_idx, ra); - if (exc) { - if (cco && exc != PGM_PROTECTION) { - return 1; - } - goto inject_exc; - } - access_memmove(env, &desta, &srca, ra); - return 0; /* data moved */ -inject_exc: -#if !defined(CONFIG_USER_ONLY) - if (exc != PGM_ADDRESSING) { - stq_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, trans_exc_code), - env->tlb_fill_tec); - } - if (exc == PGM_PAGE_TRANS) { - stb_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, op_access_id), - r1 << 4 | r2); - } -#endif - tcg_s390_program_interrupt(env, exc, ra); -} - -/* string copy */ -uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) -{ - const int mmu_idx = cpu_mmu_index(env, false); - const uint64_t d = get_address(env, r1); - const uint64_t s = get_address(env, r2); - const uint8_t c = env->regs[0]; - const int len = MIN(-(d | TARGET_PAGE_MASK), -(s | TARGET_PAGE_MASK)); - S390Access srca, desta; - uintptr_t ra = GETPC(); - int i; - - if (env->regs[0] & 0xffffff00ull) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - /* - * Our access should not exceed single pages, as we must not report access - * exceptions exceeding the actually copied range (which we don't know at - * this point). We might over-indicate watchpoints within the pages - * (if we ever care, we have to limit processing to a single byte). - */ - srca = access_prepare(env, s, len, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, d, len, MMU_DATA_STORE, mmu_idx, ra); - for (i = 0; i < len; i++) { - const uint8_t v = access_get_byte(env, &srca, i, ra); - - access_set_byte(env, &desta, i, v, ra); - if (v == c) { - set_address_zero(env, r1, d + i); - return 1; - } - } - set_address_zero(env, r1, d + len); - set_address_zero(env, r2, s + len); - return 3; -} - -/* load access registers r1 to r3 from memory at a2 */ -void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - int i; - - if (a2 & 0x3) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - env->aregs[i] = cpu_ldl_data_ra(env, a2, ra); - a2 += 4; - - if (i == r3) { - break; - } - } -} - -/* store access registers r1 to r3 in memory at a2 */ -void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - int i; - - if (a2 & 0x3) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - cpu_stl_data_ra(env, a2, env->aregs[i], ra); - a2 += 4; - - if (i == r3) { - break; - } - } -} - -/* move long helper */ -static inline uint32_t do_mvcl(CPUS390XState *env, - uint64_t *dest, uint64_t *destlen, - uint64_t *src, uint64_t *srclen, - uint16_t pad, int wordsize, uintptr_t ra) -{ - const int mmu_idx = cpu_mmu_index(env, false); - int len = MIN(*destlen, -(*dest | TARGET_PAGE_MASK)); - S390Access srca, desta; - int i, cc; - - if (*destlen == *srclen) { - cc = 0; - } else if (*destlen < *srclen) { - cc = 1; - } else { - cc = 2; - } - - if (!*destlen) { - return cc; - } - - /* - * Only perform one type of type of operation (move/pad) at a time. - * Stay within single pages. - */ - if (*srclen) { - /* Copy the src array */ - len = MIN(MIN(*srclen, -(*src | TARGET_PAGE_MASK)), len); - *destlen -= len; - *srclen -= len; - srca = access_prepare(env, *src, len, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); - access_memmove(env, &desta, &srca, ra); - *src = wrap_address(env, *src + len); - *dest = wrap_address(env, *dest + len); - } else if (wordsize == 1) { - /* Pad the remaining area */ - *destlen -= len; - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); - access_memset(env, &desta, pad, ra); - *dest = wrap_address(env, *dest + len); - } else { - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); - - /* The remaining length selects the padding byte. */ - for (i = 0; i < len; (*destlen)--, i++) { - if (*destlen & 1) { - access_set_byte(env, &desta, i, pad, ra); - } else { - access_set_byte(env, &desta, i, pad >> 8, ra); - } - } - *dest = wrap_address(env, *dest + len); - } - - return *destlen ? 3 : cc; -} - -/* move long */ -uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) -{ - const int mmu_idx = cpu_mmu_index(env, false); - uintptr_t ra = GETPC(); - uint64_t destlen = env->regs[r1 + 1] & 0xffffff; - uint64_t dest = get_address(env, r1); - uint64_t srclen = env->regs[r2 + 1] & 0xffffff; - uint64_t src = get_address(env, r2); - uint8_t pad = env->regs[r2 + 1] >> 24; - CPUState *cs = env_cpu(env); - S390Access srca, desta; - uint32_t cc, cur_len; - - if (is_destructive_overlap(env, dest, src, MIN(srclen, destlen))) { - cc = 3; - } else if (srclen == destlen) { - cc = 0; - } else if (destlen < srclen) { - cc = 1; - } else { - cc = 2; - } - - /* We might have to zero-out some bits even if there was no action. */ - if (unlikely(!destlen || cc == 3)) { - set_address_zero(env, r2, src); - set_address_zero(env, r1, dest); - return cc; - } else if (!srclen) { - set_address_zero(env, r2, src); - } - - /* - * Only perform one type of type of operation (move/pad) in one step. - * Stay within single pages. - */ - while (destlen) { - cur_len = MIN(destlen, -(dest | TARGET_PAGE_MASK)); - if (!srclen) { - desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx, - ra); - access_memset(env, &desta, pad, ra); - } else { - cur_len = MIN(MIN(srclen, -(src | TARGET_PAGE_MASK)), cur_len); - - srca = access_prepare(env, src, cur_len, MMU_DATA_LOAD, mmu_idx, - ra); - desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx, - ra); - access_memmove(env, &desta, &srca, ra); - src = wrap_address(env, src + cur_len); - srclen -= cur_len; - env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen); - set_address_zero(env, r2, src); - } - dest = wrap_address(env, dest + cur_len); - destlen -= cur_len; - env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen); - set_address_zero(env, r1, dest); - - /* - * MVCL is interruptible. Return to the main loop if requested after - * writing back all state to registers. If no interrupt will get - * injected, we'll end up back in this handler and continue processing - * the remaining parts. - */ - if (destlen && unlikely(cpu_loop_exit_requested(cs))) { - cpu_loop_exit_restore(cs, ra); - } - } - return cc; -} - -/* move long extended */ -uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, - uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t destlen = get_length(env, r1 + 1); - uint64_t dest = get_address(env, r1); - uint64_t srclen = get_length(env, r3 + 1); - uint64_t src = get_address(env, r3); - uint8_t pad = a2; - uint32_t cc; - - cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra); - - set_length(env, r1 + 1, destlen); - set_length(env, r3 + 1, srclen); - set_address(env, r1, dest); - set_address(env, r3, src); - - return cc; -} - -/* move long unicode */ -uint32_t HELPER(mvclu)(CPUS390XState *env, uint32_t r1, uint64_t a2, - uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t destlen = get_length(env, r1 + 1); - uint64_t dest = get_address(env, r1); - uint64_t srclen = get_length(env, r3 + 1); - uint64_t src = get_address(env, r3); - uint16_t pad = a2; - uint32_t cc; - - cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 2, ra); - - set_length(env, r1 + 1, destlen); - set_length(env, r3 + 1, srclen); - set_address(env, r1, dest); - set_address(env, r3, src); - - return cc; -} - -/* compare logical long helper */ -static inline uint32_t do_clcl(CPUS390XState *env, - uint64_t *src1, uint64_t *src1len, - uint64_t *src3, uint64_t *src3len, - uint16_t pad, uint64_t limit, - int wordsize, uintptr_t ra) -{ - uint64_t len = MAX(*src1len, *src3len); - uint32_t cc = 0; - - check_alignment(env, *src1len | *src3len, wordsize, ra); - - if (!len) { - return cc; - } - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. */ - if (len > limit) { - len = limit; - cc = 3; - } - - for (; len; len -= wordsize) { - uint16_t v1 = pad; - uint16_t v3 = pad; - - if (*src1len) { - v1 = cpu_ldusize_data_ra(env, *src1, wordsize, ra); - } - if (*src3len) { - v3 = cpu_ldusize_data_ra(env, *src3, wordsize, ra); - } - - if (v1 != v3) { - cc = (v1 < v3) ? 1 : 2; - break; - } - - if (*src1len) { - *src1 += wordsize; - *src1len -= wordsize; - } - if (*src3len) { - *src3 += wordsize; - *src3len -= wordsize; - } - } - - return cc; -} - - -/* compare logical long */ -uint32_t HELPER(clcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) -{ - uintptr_t ra = GETPC(); - uint64_t src1len = extract64(env->regs[r1 + 1], 0, 24); - uint64_t src1 = get_address(env, r1); - uint64_t src3len = extract64(env->regs[r2 + 1], 0, 24); - uint64_t src3 = get_address(env, r2); - uint8_t pad = env->regs[r2 + 1] >> 24; - uint32_t cc; - - cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, -1, 1, ra); - - env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, src1len); - env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, src3len); - set_address(env, r1, src1); - set_address(env, r2, src3); - - return cc; -} - -/* compare logical long extended memcompare insn with padding */ -uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, - uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t src1len = get_length(env, r1 + 1); - uint64_t src1 = get_address(env, r1); - uint64_t src3len = get_length(env, r3 + 1); - uint64_t src3 = get_address(env, r3); - uint8_t pad = a2; - uint32_t cc; - - cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x2000, 1, ra); - - set_length(env, r1 + 1, src1len); - set_length(env, r3 + 1, src3len); - set_address(env, r1, src1); - set_address(env, r3, src3); - - return cc; -} - -/* compare logical long unicode memcompare insn with padding */ -uint32_t HELPER(clclu)(CPUS390XState *env, uint32_t r1, uint64_t a2, - uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t src1len = get_length(env, r1 + 1); - uint64_t src1 = get_address(env, r1); - uint64_t src3len = get_length(env, r3 + 1); - uint64_t src3 = get_address(env, r3); - uint16_t pad = a2; - uint32_t cc = 0; - - cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x1000, 2, ra); - - set_length(env, r1 + 1, src1len); - set_length(env, r3 + 1, src3len); - set_address(env, r1, src1); - set_address(env, r3, src3); - - return cc; -} - -/* checksum */ -uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1, - uint64_t src, uint64_t src_len) -{ - uintptr_t ra = GETPC(); - uint64_t max_len, len; - uint64_t cksm = (uint32_t)r1; - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 8k. */ - max_len = (src_len > 0x2000 ? 0x2000 : src_len); - - /* Process full words as available. */ - for (len = 0; len + 4 <= max_len; len += 4, src += 4) { - cksm += (uint32_t)cpu_ldl_data_ra(env, src, ra); - } - - switch (max_len - len) { - case 1: - cksm += cpu_ldub_data_ra(env, src, ra) << 24; - len += 1; - break; - case 2: - cksm += cpu_lduw_data_ra(env, src, ra) << 16; - len += 2; - break; - case 3: - cksm += cpu_lduw_data_ra(env, src, ra) << 16; - cksm += cpu_ldub_data_ra(env, src + 2, ra) << 8; - len += 3; - break; - } - - /* Fold the carry from the checksum. Note that we can see carry-out - during folding more than once (but probably not more than twice). */ - while (cksm > 0xffffffffull) { - cksm = (uint32_t)cksm + (cksm >> 32); - } - - /* Indicate whether or not we've processed everything. */ - env->cc_op = (len == src_len ? 0 : 3); - - /* Return both cksm and processed length. */ - env->retxl = cksm; - return len; -} - -void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src) -{ - uintptr_t ra = GETPC(); - int len_dest = len >> 4; - int len_src = len & 0xf; - uint8_t b; - - dest += len_dest; - src += len_src; - - /* last byte is special, it only flips the nibbles */ - b = cpu_ldub_data_ra(env, src, ra); - cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra); - src--; - len_src--; - - /* now pack every value */ - while (len_dest > 0) { - b = 0; - - if (len_src >= 0) { - b = cpu_ldub_data_ra(env, src, ra) & 0x0f; - src--; - len_src--; - } - if (len_src >= 0) { - b |= cpu_ldub_data_ra(env, src, ra) << 4; - src--; - len_src--; - } - - len_dest--; - dest--; - cpu_stb_data_ra(env, dest, b, ra); - } -} - -static inline void do_pkau(CPUS390XState *env, uint64_t dest, uint64_t src, - uint32_t srclen, int ssize, uintptr_t ra) -{ - int i; - /* The destination operand is always 16 bytes long. */ - const int destlen = 16; - - /* The operands are processed from right to left. */ - src += srclen - 1; - dest += destlen - 1; - - for (i = 0; i < destlen; i++) { - uint8_t b = 0; - - /* Start with a positive sign */ - if (i == 0) { - b = 0xc; - } else if (srclen > ssize) { - b = cpu_ldub_data_ra(env, src, ra) & 0x0f; - src -= ssize; - srclen -= ssize; - } - - if (srclen > ssize) { - b |= cpu_ldub_data_ra(env, src, ra) << 4; - src -= ssize; - srclen -= ssize; - } - - cpu_stb_data_ra(env, dest, b, ra); - dest--; - } -} - - -void HELPER(pka)(CPUS390XState *env, uint64_t dest, uint64_t src, - uint32_t srclen) -{ - do_pkau(env, dest, src, srclen, 1, GETPC()); -} - -void HELPER(pku)(CPUS390XState *env, uint64_t dest, uint64_t src, - uint32_t srclen) -{ - do_pkau(env, dest, src, srclen, 2, GETPC()); -} - -void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest, - uint64_t src) -{ - uintptr_t ra = GETPC(); - int len_dest = len >> 4; - int len_src = len & 0xf; - uint8_t b; - int second_nibble = 0; - - dest += len_dest; - src += len_src; - - /* last byte is special, it only flips the nibbles */ - b = cpu_ldub_data_ra(env, src, ra); - cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra); - src--; - len_src--; - - /* now pad every nibble with 0xf0 */ - - while (len_dest > 0) { - uint8_t cur_byte = 0; - - if (len_src > 0) { - cur_byte = cpu_ldub_data_ra(env, src, ra); - } - - len_dest--; - dest--; - - /* only advance one nibble at a time */ - if (second_nibble) { - cur_byte >>= 4; - len_src--; - src--; - } - second_nibble = !second_nibble; - - /* digit */ - cur_byte = (cur_byte & 0xf); - /* zone bits */ - cur_byte |= 0xf0; - - cpu_stb_data_ra(env, dest, cur_byte, ra); - } -} - -static inline uint32_t do_unpkau(CPUS390XState *env, uint64_t dest, - uint32_t destlen, int dsize, uint64_t src, - uintptr_t ra) -{ - int i; - uint32_t cc; - uint8_t b; - /* The source operand is always 16 bytes long. */ - const int srclen = 16; - - /* The operands are processed from right to left. */ - src += srclen - 1; - dest += destlen - dsize; - - /* Check for the sign. */ - b = cpu_ldub_data_ra(env, src, ra); - src--; - switch (b & 0xf) { - case 0xa: - case 0xc: - case 0xe ... 0xf: - cc = 0; /* plus */ - break; - case 0xb: - case 0xd: - cc = 1; /* minus */ - break; - default: - case 0x0 ... 0x9: - cc = 3; /* invalid */ - break; - } - - /* Now pad every nibble with 0x30, advancing one nibble at a time. */ - for (i = 0; i < destlen; i += dsize) { - if (i == (31 * dsize)) { - /* If length is 32/64 bytes, the leftmost byte is 0. */ - b = 0; - } else if (i % (2 * dsize)) { - b = cpu_ldub_data_ra(env, src, ra); - src--; - } else { - b >>= 4; - } - cpu_stsize_data_ra(env, dest, 0x30 + (b & 0xf), dsize, ra); - dest -= dsize; - } - - return cc; -} - -uint32_t HELPER(unpka)(CPUS390XState *env, uint64_t dest, uint32_t destlen, - uint64_t src) -{ - return do_unpkau(env, dest, destlen, 1, src, GETPC()); -} - -uint32_t HELPER(unpku)(CPUS390XState *env, uint64_t dest, uint32_t destlen, - uint64_t src) -{ - return do_unpkau(env, dest, destlen, 2, src, GETPC()); -} - -uint32_t HELPER(tp)(CPUS390XState *env, uint64_t dest, uint32_t destlen) -{ - uintptr_t ra = GETPC(); - uint32_t cc = 0; - int i; - - for (i = 0; i < destlen; i++) { - uint8_t b = cpu_ldub_data_ra(env, dest + i, ra); - /* digit */ - cc |= (b & 0xf0) > 0x90 ? 2 : 0; - - if (i == (destlen - 1)) { - /* sign */ - cc |= (b & 0xf) < 0xa ? 1 : 0; - } else { - /* digit */ - cc |= (b & 0xf) > 0x9 ? 2 : 0; - } - } - - return cc; -} - -static uint32_t do_helper_tr(CPUS390XState *env, uint32_t len, uint64_t array, - uint64_t trans, uintptr_t ra) -{ - uint32_t i; - - for (i = 0; i <= len; i++) { - uint8_t byte = cpu_ldub_data_ra(env, array + i, ra); - uint8_t new_byte = cpu_ldub_data_ra(env, trans + byte, ra); - cpu_stb_data_ra(env, array + i, new_byte, ra); - } - - return env->cc_op; -} - -void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array, - uint64_t trans) -{ - do_helper_tr(env, len, array, trans, GETPC()); -} - -uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, - uint64_t len, uint64_t trans) -{ - uintptr_t ra = GETPC(); - uint8_t end = env->regs[0] & 0xff; - uint64_t l = len; - uint64_t i; - uint32_t cc = 0; - - if (!(env->psw.mask & PSW_MASK_64)) { - array &= 0x7fffffff; - l = (uint32_t)l; - } - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 8k. */ - if (l > 0x2000) { - l = 0x2000; - cc = 3; - } - - for (i = 0; i < l; i++) { - uint8_t byte, new_byte; - - byte = cpu_ldub_data_ra(env, array + i, ra); - - if (byte == end) { - cc = 1; - break; - } - - new_byte = cpu_ldub_data_ra(env, trans + byte, ra); - cpu_stb_data_ra(env, array + i, new_byte, ra); - } - - env->cc_op = cc; - env->retxl = len - i; - return array + i; -} - -static inline uint32_t do_helper_trt(CPUS390XState *env, int len, - uint64_t array, uint64_t trans, - int inc, uintptr_t ra) -{ - int i; - - for (i = 0; i <= len; i++) { - uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra); - uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra); - - if (sbyte != 0) { - set_address(env, 1, array + i * inc); - env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte); - return (i == len) ? 2 : 1; - } - } - - return 0; -} - -static uint32_t do_helper_trt_fwd(CPUS390XState *env, uint32_t len, - uint64_t array, uint64_t trans, - uintptr_t ra) -{ - return do_helper_trt(env, len, array, trans, 1, ra); -} - -uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, - uint64_t trans) -{ - return do_helper_trt(env, len, array, trans, 1, GETPC()); -} - -static uint32_t do_helper_trt_bkwd(CPUS390XState *env, uint32_t len, - uint64_t array, uint64_t trans, - uintptr_t ra) -{ - return do_helper_trt(env, len, array, trans, -1, ra); -} - -uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array, - uint64_t trans) -{ - return do_helper_trt(env, len, array, trans, -1, GETPC()); -} - -/* Translate one/two to one/two */ -uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2, - uint32_t tst, uint32_t sizes) -{ - uintptr_t ra = GETPC(); - int dsize = (sizes & 1) ? 1 : 2; - int ssize = (sizes & 2) ? 1 : 2; - uint64_t tbl = get_address(env, 1); - uint64_t dst = get_address(env, r1); - uint64_t len = get_length(env, r1 + 1); - uint64_t src = get_address(env, r2); - uint32_t cc = 3; - int i; - - /* The lower address bits of TBL are ignored. For TROO, TROT, it's - the low 3 bits (double-word aligned). For TRTO, TRTT, it's either - the low 12 bits (4K, without ETF2-ENH) or 3 bits (with ETF2-ENH). */ - if (ssize == 2 && !s390_has_feat(S390_FEAT_ETF2_ENH)) { - tbl &= -4096; - } else { - tbl &= -8; - } - - check_alignment(env, len, ssize, ra); - - /* Lest we fail to service interrupts in a timely manner, */ - /* limit the amount of work we're willing to do. */ - for (i = 0; i < 0x2000; i++) { - uint16_t sval = cpu_ldusize_data_ra(env, src, ssize, ra); - uint64_t tble = tbl + (sval * dsize); - uint16_t dval = cpu_ldusize_data_ra(env, tble, dsize, ra); - if (dval == tst) { - cc = 1; - break; - } - cpu_stsize_data_ra(env, dst, dval, dsize, ra); - - len -= ssize; - src += ssize; - dst += dsize; - - if (len == 0) { - cc = 0; - break; - } - } - - set_address(env, r1, dst); - set_length(env, r1 + 1, len); - set_address(env, r2, src); - - return cc; -} - -void HELPER(cdsg)(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3) -{ - uintptr_t ra = GETPC(); - Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); - Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); - Int128 oldv; - uint64_t oldh, oldl; - bool fail; - - check_alignment(env, addr, 16, ra); - - oldh = cpu_ldq_data_ra(env, addr + 0, ra); - oldl = cpu_ldq_data_ra(env, addr + 8, ra); - - oldv = int128_make128(oldl, oldh); - fail = !int128_eq(oldv, cmpv); - if (fail) { - newv = oldv; - } - - cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra); - cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra); - - env->cc_op = fail; - env->regs[r1] = int128_gethi(oldv); - env->regs[r1 + 1] = int128_getlo(oldv); -} - -void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3) -{ - uintptr_t ra = GETPC(); - Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); - Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); - int mem_idx; - TCGMemOpIdx oi; - Int128 oldv; - bool fail; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - fail = !int128_eq(oldv, cmpv); - - env->cc_op = fail; - env->regs[r1] = int128_gethi(oldv); - env->regs[r1 + 1] = int128_getlo(oldv); -} - -static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, - uint64_t a2, bool parallel) -{ - uint32_t mem_idx = cpu_mmu_index(env, false); - uintptr_t ra = GETPC(); - uint32_t fc = extract32(env->regs[0], 0, 8); - uint32_t sc = extract32(env->regs[0], 8, 8); - uint64_t pl = get_address(env, 1) & -16; - uint64_t svh, svl; - uint32_t cc; - - /* Sanity check the function code and storage characteristic. */ - if (fc > 1 || sc > 3) { - if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) { - goto spec_exception; - } - if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) { - goto spec_exception; - } - } - - /* Sanity check the alignments. */ - if (extract32(a1, 0, fc + 2) || extract32(a2, 0, sc)) { - goto spec_exception; - } - - /* Sanity check writability of the store address. */ - probe_write(env, a2, 1 << sc, mem_idx, ra); - - /* - * Note that the compare-and-swap is atomic, and the store is atomic, - * but the complete operation is not. Therefore we do not need to - * assert serial context in order to implement this. That said, - * restart early if we can't support either operation that is supposed - * to be atomic. - */ - if (parallel) { - uint32_t max = 2; -#ifdef CONFIG_ATOMIC64 - max = 3; -#endif - if ((HAVE_CMPXCHG128 ? 0 : fc + 2 > max) || - (HAVE_ATOMIC128 ? 0 : sc > max)) { - cpu_loop_exit_atomic(env_cpu(env), ra); - } - } - - /* All loads happen before all stores. For simplicity, load the entire - store value area from the parameter list. */ - svh = cpu_ldq_data_ra(env, pl + 16, ra); - svl = cpu_ldq_data_ra(env, pl + 24, ra); - - switch (fc) { - case 0: - { - uint32_t nv = cpu_ldl_data_ra(env, pl, ra); - uint32_t cv = env->regs[r3]; - uint32_t ov; - - if (parallel) { -#ifdef CONFIG_USER_ONLY - uint32_t *haddr = g2h(env_cpu(env), a1); - ov = qatomic_cmpxchg__nocheck(haddr, cv, nv); -#else - TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx); - ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra); -#endif - } else { - ov = cpu_ldl_data_ra(env, a1, ra); - cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra); - } - cc = (ov != cv); - env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov); - } - break; - - case 1: - { - uint64_t nv = cpu_ldq_data_ra(env, pl, ra); - uint64_t cv = env->regs[r3]; - uint64_t ov; - - if (parallel) { -#ifdef CONFIG_ATOMIC64 -# ifdef CONFIG_USER_ONLY - uint64_t *haddr = g2h(env_cpu(env), a1); - ov = qatomic_cmpxchg__nocheck(haddr, cv, nv); -# else - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx); - ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra); -# endif -#else - /* Note that we asserted !parallel above. */ - g_assert_not_reached(); -#endif - } else { - ov = cpu_ldq_data_ra(env, a1, ra); - cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra); - } - cc = (ov != cv); - env->regs[r3] = ov; - } - break; - - case 2: - { - uint64_t nvh = cpu_ldq_data_ra(env, pl, ra); - uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra); - Int128 nv = int128_make128(nvl, nvh); - Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]); - Int128 ov; - - if (!parallel) { - uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra); - uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra); - - ov = int128_make128(ol, oh); - cc = !int128_eq(ov, cv); - if (cc) { - nv = ov; - } - - cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); - cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); - } else if (HAVE_CMPXCHG128) { - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); - cc = !int128_eq(ov, cv); - } else { - /* Note that we asserted !parallel above. */ - g_assert_not_reached(); - } - - env->regs[r3 + 0] = int128_gethi(ov); - env->regs[r3 + 1] = int128_getlo(ov); - } - break; - - default: - g_assert_not_reached(); - } - - /* Store only if the comparison succeeded. Note that above we use a pair - of 64-bit big-endian loads, so for sc < 3 we must extract the value - from the most-significant bits of svh. */ - if (cc == 0) { - switch (sc) { - case 0: - cpu_stb_data_ra(env, a2, svh >> 56, ra); - break; - case 1: - cpu_stw_data_ra(env, a2, svh >> 48, ra); - break; - case 2: - cpu_stl_data_ra(env, a2, svh >> 32, ra); - break; - case 3: - cpu_stq_data_ra(env, a2, svh, ra); - break; - case 4: - if (!parallel) { - cpu_stq_data_ra(env, a2 + 0, svh, ra); - cpu_stq_data_ra(env, a2 + 8, svl, ra); - } else if (HAVE_ATOMIC128) { - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - Int128 sv = int128_make128(svl, svh); - helper_atomic_sto_be_mmu(env, a2, sv, oi, ra); - } else { - /* Note that we asserted !parallel above. */ - g_assert_not_reached(); - } - break; - default: - g_assert_not_reached(); - } - } - - return cc; - - spec_exception: - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); -} - -uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2) -{ - return do_csst(env, r3, a1, a2, false); -} - -uint32_t HELPER(csst_parallel)(CPUS390XState *env, uint32_t r3, uint64_t a1, - uint64_t a2) -{ - return do_csst(env, r3, a1, a2, true); -} - -#if !defined(CONFIG_USER_ONLY) -void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - bool PERchanged = false; - uint64_t src = a2; - uint32_t i; - - if (src & 0x7) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - uint64_t val = cpu_ldq_data_ra(env, src, ra); - if (env->cregs[i] != val && i >= 9 && i <= 11) { - PERchanged = true; - } - env->cregs[i] = val; - HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n", - i, src, val); - src += sizeof(uint64_t); - - if (i == r3) { - break; - } - } - - if (PERchanged && env->psw.mask & PSW_MASK_PER) { - s390_cpu_recompute_watchpoints(env_cpu(env)); - } - - tlb_flush(env_cpu(env)); -} - -void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - bool PERchanged = false; - uint64_t src = a2; - uint32_t i; - - if (src & 0x3) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - uint32_t val = cpu_ldl_data_ra(env, src, ra); - if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) { - PERchanged = true; - } - env->cregs[i] = deposit64(env->cregs[i], 0, 32, val); - HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%x\n", i, src, val); - src += sizeof(uint32_t); - - if (i == r3) { - break; - } - } - - if (PERchanged && env->psw.mask & PSW_MASK_PER) { - s390_cpu_recompute_watchpoints(env_cpu(env)); - } - - tlb_flush(env_cpu(env)); -} - -void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t dest = a2; - uint32_t i; - - if (dest & 0x7) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - cpu_stq_data_ra(env, dest, env->cregs[i], ra); - dest += sizeof(uint64_t); - - if (i == r3) { - break; - } - } -} - -void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - uintptr_t ra = GETPC(); - uint64_t dest = a2; - uint32_t i; - - if (dest & 0x3) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - for (i = r1;; i = (i + 1) % 16) { - cpu_stl_data_ra(env, dest, env->cregs[i], ra); - dest += sizeof(uint32_t); - - if (i == r3) { - break; - } - } -} - -uint32_t HELPER(testblock)(CPUS390XState *env, uint64_t real_addr) -{ - uintptr_t ra = GETPC(); - int i; - - real_addr = wrap_address(env, real_addr) & TARGET_PAGE_MASK; - - for (i = 0; i < TARGET_PAGE_SIZE; i += 8) { - cpu_stq_mmuidx_ra(env, real_addr + i, 0, MMU_REAL_IDX, ra); - } - - return 0; -} - -uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2) -{ - S390CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - /* - * TODO: we currently don't handle all access protection types - * (including access-list and key-controlled) as well as AR mode. - */ - if (!s390_cpu_virt_mem_check_write(cpu, a1, 0, 1)) { - /* Fetching permitted; storing permitted */ - return 0; - } - - if (env->int_pgm_code == PGM_PROTECTION) { - /* retry if reading is possible */ - cs->exception_index = -1; - if (!s390_cpu_virt_mem_check_read(cpu, a1, 0, 1)) { - /* Fetching permitted; storing not permitted */ - return 1; - } - } - - switch (env->int_pgm_code) { - case PGM_PROTECTION: - /* Fetching not permitted; storing not permitted */ - cs->exception_index = -1; - return 2; - case PGM_ADDRESSING: - case PGM_TRANS_SPEC: - /* exceptions forwarded to the guest */ - s390_cpu_virt_mem_handle_exc(cpu, GETPC()); - return 0; - } - - /* Translation not available */ - cs->exception_index = -1; - return 3; -} - -/* insert storage key extended */ -uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - static S390SKeysState *ss; - static S390SKeysClass *skeyclass; - uint64_t addr = wrap_address(env, r2); - uint8_t key; - - if (addr > ms->ram_size) { - return 0; - } - - if (unlikely(!ss)) { - ss = s390_get_skeys_device(); - skeyclass = S390_SKEYS_GET_CLASS(ss); - } - - if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { - return 0; - } - return key; -} - -/* set storage key extended */ -void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - static S390SKeysState *ss; - static S390SKeysClass *skeyclass; - uint64_t addr = wrap_address(env, r2); - uint8_t key; - - if (addr > ms->ram_size) { - return; - } - - if (unlikely(!ss)) { - ss = s390_get_skeys_device(); - skeyclass = S390_SKEYS_GET_CLASS(ss); - } - - key = (uint8_t) r1; - skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - /* - * As we can only flush by virtual address and not all the entries - * that point to a physical address we have to flush the whole TLB. - */ - tlb_flush_all_cpus_synced(env_cpu(env)); -} - -/* reset reference bit extended */ -uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - static S390SKeysState *ss; - static S390SKeysClass *skeyclass; - uint8_t re, key; - - if (r2 > ms->ram_size) { - return 0; - } - - if (unlikely(!ss)) { - ss = s390_get_skeys_device(); - skeyclass = S390_SKEYS_GET_CLASS(ss); - } - - if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { - return 0; - } - - re = key & (SK_R | SK_C); - key &= ~SK_R; - - if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { - return 0; - } - /* - * As we can only flush by virtual address and not all the entries - * that point to a physical address we have to flush the whole TLB. - */ - tlb_flush_all_cpus_synced(env_cpu(env)); - - /* - * cc - * - * 0 Reference bit zero; change bit zero - * 1 Reference bit zero; change bit one - * 2 Reference bit one; change bit zero - * 3 Reference bit one; change bit one - */ - - return re >> 1; -} - -uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) -{ - const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC; - S390Access srca, desta; - uintptr_t ra = GETPC(); - int cc = 0; - - HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", - __func__, l, a1, a2); - - if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) || - psw_as == AS_HOME || psw_as == AS_ACCREG) { - s390_program_interrupt(env, PGM_SPECIAL_OP, ra); - } - - l = wrap_length32(env, l); - if (l > 256) { - /* max 256 */ - l = 256; - cc = 3; - } else if (!l) { - return cc; - } - - /* TODO: Access key handling */ - srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_PRIMARY_IDX, ra); - desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_SECONDARY_IDX, ra); - access_memmove(env, &desta, &srca, ra); - return cc; -} - -uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) -{ - const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC; - S390Access srca, desta; - uintptr_t ra = GETPC(); - int cc = 0; - - HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", - __func__, l, a1, a2); - - if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) || - psw_as == AS_HOME || psw_as == AS_ACCREG) { - s390_program_interrupt(env, PGM_SPECIAL_OP, ra); - } - - l = wrap_length32(env, l); - if (l > 256) { - /* max 256 */ - l = 256; - cc = 3; - } else if (!l) { - return cc; - } - - /* TODO: Access key handling */ - srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_SECONDARY_IDX, ra); - desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_PRIMARY_IDX, ra); - access_memmove(env, &desta, &srca, ra); - return cc; -} - -void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4) -{ - CPUState *cs = env_cpu(env); - const uintptr_t ra = GETPC(); - uint64_t table, entry, raddr; - uint16_t entries, i, index = 0; - - if (r2 & 0xff000) { - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); - } - - if (!(r2 & 0x800)) { - /* invalidation-and-clearing operation */ - table = r1 & ASCE_ORIGIN; - entries = (r2 & 0x7ff) + 1; - - switch (r1 & ASCE_TYPE_MASK) { - case ASCE_TYPE_REGION1: - index = (r2 >> 53) & 0x7ff; - break; - case ASCE_TYPE_REGION2: - index = (r2 >> 42) & 0x7ff; - break; - case ASCE_TYPE_REGION3: - index = (r2 >> 31) & 0x7ff; - break; - case ASCE_TYPE_SEGMENT: - index = (r2 >> 20) & 0x7ff; - break; - } - for (i = 0; i < entries; i++) { - /* addresses are not wrapped in 24/31bit mode but table index is */ - raddr = table + ((index + i) & 0x7ff) * sizeof(entry); - entry = cpu_ldq_mmuidx_ra(env, raddr, MMU_REAL_IDX, ra); - if (!(entry & REGION_ENTRY_I)) { - /* we are allowed to not store if already invalid */ - entry |= REGION_ENTRY_I; - cpu_stq_mmuidx_ra(env, raddr, entry, MMU_REAL_IDX, ra); - } - } - } - - /* We simply flush the complete tlb, therefore we can ignore r3. */ - if (m4 & 1) { - tlb_flush(cs); - } else { - tlb_flush_all_cpus_synced(cs); - } -} - -/* invalidate pte */ -void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr, - uint32_t m4) -{ - CPUState *cs = env_cpu(env); - const uintptr_t ra = GETPC(); - uint64_t page = vaddr & TARGET_PAGE_MASK; - uint64_t pte_addr, pte; - - /* Compute the page table entry address */ - pte_addr = (pto & SEGMENT_ENTRY_ORIGIN); - pte_addr += VADDR_PAGE_TX(vaddr) * 8; - - /* Mark the page table entry as invalid */ - pte = cpu_ldq_mmuidx_ra(env, pte_addr, MMU_REAL_IDX, ra); - pte |= PAGE_ENTRY_I; - cpu_stq_mmuidx_ra(env, pte_addr, pte, MMU_REAL_IDX, ra); - - /* XXX we exploit the fact that Linux passes the exact virtual - address here - it's not obliged to! */ - if (m4 & 1) { - if (vaddr & ~VADDR_PAGE_TX_MASK) { - tlb_flush_page(cs, page); - /* XXX 31-bit hack */ - tlb_flush_page(cs, page ^ 0x80000000); - } else { - /* looks like we don't have a valid virtual address */ - tlb_flush(cs); - } - } else { - if (vaddr & ~VADDR_PAGE_TX_MASK) { - tlb_flush_page_all_cpus_synced(cs, page); - /* XXX 31-bit hack */ - tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000); - } else { - /* looks like we don't have a valid virtual address */ - tlb_flush_all_cpus_synced(cs); - } - } -} - -/* flush local tlb */ -void HELPER(ptlb)(CPUS390XState *env) -{ - tlb_flush(env_cpu(env)); -} - -/* flush global tlb */ -void HELPER(purge)(CPUS390XState *env) -{ - tlb_flush_all_cpus_synced(env_cpu(env)); -} - -/* load real address */ -uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) -{ - uint64_t asc = env->psw.mask & PSW_MASK_ASC; - uint64_t ret, tec; - int flags, exc, cc; - - /* XXX incomplete - has more corner cases */ - if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) { - tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC()); - } - - exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec); - if (exc) { - cc = 3; - ret = exc | 0x80000000; - } else { - cc = 0; - ret |= addr & ~TARGET_PAGE_MASK; - } - - env->cc_op = cc; - return ret; -} -#endif - -/* load pair from quadword */ -uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr) -{ - uintptr_t ra = GETPC(); - uint64_t hi, lo; - - check_alignment(env, addr, 16, ra); - hi = cpu_ldq_data_ra(env, addr + 0, ra); - lo = cpu_ldq_data_ra(env, addr + 8, ra); - - env->retxl = lo; - return hi; -} - -uint64_t HELPER(lpq_parallel)(CPUS390XState *env, uint64_t addr) -{ - uintptr_t ra = GETPC(); - uint64_t hi, lo; - int mem_idx; - TCGMemOpIdx oi; - Int128 v; - - assert(HAVE_ATOMIC128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - v = helper_atomic_ldo_be_mmu(env, addr, oi, ra); - hi = int128_gethi(v); - lo = int128_getlo(v); - - env->retxl = lo; - return hi; -} - -/* store pair to quadword */ -void HELPER(stpq)(CPUS390XState *env, uint64_t addr, - uint64_t low, uint64_t high) -{ - uintptr_t ra = GETPC(); - - check_alignment(env, addr, 16, ra); - cpu_stq_data_ra(env, addr + 0, high, ra); - cpu_stq_data_ra(env, addr + 8, low, ra); -} - -void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr, - uint64_t low, uint64_t high) -{ - uintptr_t ra = GETPC(); - int mem_idx; - TCGMemOpIdx oi; - Int128 v; - - assert(HAVE_ATOMIC128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - v = int128_make128(low, high); - helper_atomic_sto_be_mmu(env, addr, v, oi, ra); -} - -/* Execute instruction. This instruction executes an insn modified with - the contents of r1. It does not change the executed instruction in memory; - it does not change the program counter. - - Perform this by recording the modified instruction in env->ex_value. - This will be noticed by cpu_get_tb_cpu_state and thus tb translation. -*/ -void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr) -{ - uint64_t insn = cpu_lduw_code(env, addr); - uint8_t opc = insn >> 8; - - /* Or in the contents of R1[56:63]. */ - insn |= r1 & 0xff; - - /* Load the rest of the instruction. */ - insn <<= 48; - switch (get_ilen(opc)) { - case 2: - break; - case 4: - insn |= (uint64_t)cpu_lduw_code(env, addr + 2) << 32; - break; - case 6: - insn |= (uint64_t)(uint32_t)cpu_ldl_code(env, addr + 2) << 16; - break; - default: - g_assert_not_reached(); - } - - /* The very most common cases can be sped up by avoiding a new TB. */ - if ((opc & 0xf0) == 0xd0) { - typedef uint32_t (*dx_helper)(CPUS390XState *, uint32_t, uint64_t, - uint64_t, uintptr_t); - static const dx_helper dx[16] = { - [0x0] = do_helper_trt_bkwd, - [0x2] = do_helper_mvc, - [0x4] = do_helper_nc, - [0x5] = do_helper_clc, - [0x6] = do_helper_oc, - [0x7] = do_helper_xc, - [0xc] = do_helper_tr, - [0xd] = do_helper_trt_fwd, - }; - dx_helper helper = dx[opc & 0xf]; - - if (helper) { - uint32_t l = extract64(insn, 48, 8); - uint32_t b1 = extract64(insn, 44, 4); - uint32_t d1 = extract64(insn, 32, 12); - uint32_t b2 = extract64(insn, 28, 4); - uint32_t d2 = extract64(insn, 16, 12); - uint64_t a1 = wrap_address(env, (b1 ? env->regs[b1] : 0) + d1); - uint64_t a2 = wrap_address(env, (b2 ? env->regs[b2] : 0) + d2); - - env->cc_op = helper(env, l, a1, a2, 0); - env->psw.addr += ilen; - return; - } - } else if (opc == 0x0a) { - env->int_svc_code = extract64(insn, 48, 8); - env->int_svc_ilen = ilen; - helper_exception(env, EXCP_SVC); - g_assert_not_reached(); - } - - /* Record the insn we want to execute as well as the ilen to use - during the execution of the target insn. This will also ensure - that ex_value is non-zero, which flags that we are in a state - that requires such execution. */ - env->ex_value = insn | ilen; -} - -uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, - uint64_t len) -{ - const uint8_t psw_key = (env->psw.mask & PSW_MASK_KEY) >> PSW_SHIFT_KEY; - const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC; - const uint64_t r0 = env->regs[0]; - const uintptr_t ra = GETPC(); - uint8_t dest_key, dest_as, dest_k, dest_a; - uint8_t src_key, src_as, src_k, src_a; - uint64_t val; - int cc = 0; - - HELPER_LOG("%s dest %" PRIx64 ", src %" PRIx64 ", len %" PRIx64 "\n", - __func__, dest, src, len); - - if (!(env->psw.mask & PSW_MASK_DAT)) { - tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); - } - - /* OAC (operand access control) for the first operand -> dest */ - val = (r0 & 0xffff0000ULL) >> 16; - dest_key = (val >> 12) & 0xf; - dest_as = (val >> 6) & 0x3; - dest_k = (val >> 1) & 0x1; - dest_a = val & 0x1; - - /* OAC (operand access control) for the second operand -> src */ - val = (r0 & 0x0000ffffULL); - src_key = (val >> 12) & 0xf; - src_as = (val >> 6) & 0x3; - src_k = (val >> 1) & 0x1; - src_a = val & 0x1; - - if (!dest_k) { - dest_key = psw_key; - } - if (!src_k) { - src_key = psw_key; - } - if (!dest_a) { - dest_as = psw_as; - } - if (!src_a) { - src_as = psw_as; - } - - if (dest_a && dest_as == AS_HOME && (env->psw.mask & PSW_MASK_PSTATE)) { - tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); - } - if (!(env->cregs[0] & CR0_SECONDARY) && - (dest_as == AS_SECONDARY || src_as == AS_SECONDARY)) { - tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); - } - if (!psw_key_valid(env, dest_key) || !psw_key_valid(env, src_key)) { - tcg_s390_program_interrupt(env, PGM_PRIVILEGED, ra); - } - - len = wrap_length32(env, len); - if (len > 4096) { - cc = 3; - len = 4096; - } - - /* FIXME: AR-mode and proper problem state mode (using PSW keys) missing */ - if (src_as == AS_ACCREG || dest_as == AS_ACCREG || - (env->psw.mask & PSW_MASK_PSTATE)) { - qemu_log_mask(LOG_UNIMP, "%s: AR-mode and PSTATE support missing\n", - __func__); - tcg_s390_program_interrupt(env, PGM_ADDRESSING, ra); - } - - /* FIXME: Access using correct keys and AR-mode */ - if (len) { - S390Access srca = access_prepare(env, src, len, MMU_DATA_LOAD, - mmu_idx_from_as(src_as), ra); - S390Access desta = access_prepare(env, dest, len, MMU_DATA_STORE, - mmu_idx_from_as(dest_as), ra); - - access_memmove(env, &desta, &srca, ra); - } - - return cc; -} - -/* Decode a Unicode character. A return value < 0 indicates success, storing - the UTF-32 result into OCHAR and the input length into OLEN. A return - value >= 0 indicates failure, and the CC value to be returned. */ -typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr, - uint64_t ilen, bool enh_check, uintptr_t ra, - uint32_t *ochar, uint32_t *olen); - -/* Encode a Unicode character. A return value < 0 indicates success, storing - the bytes into ADDR and the output length into OLEN. A return value >= 0 - indicates failure, and the CC value to be returned. */ -typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr, - uint64_t ilen, uintptr_t ra, uint32_t c, - uint32_t *olen); - -static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen, - bool enh_check, uintptr_t ra, - uint32_t *ochar, uint32_t *olen) -{ - uint8_t s0, s1, s2, s3; - uint32_t c, l; - - if (ilen < 1) { - return 0; - } - s0 = cpu_ldub_data_ra(env, addr, ra); - if (s0 <= 0x7f) { - /* one byte character */ - l = 1; - c = s0; - } else if (s0 <= (enh_check ? 0xc1 : 0xbf)) { - /* invalid character */ - return 2; - } else if (s0 <= 0xdf) { - /* two byte character */ - l = 2; - if (ilen < 2) { - return 0; - } - s1 = cpu_ldub_data_ra(env, addr + 1, ra); - c = s0 & 0x1f; - c = (c << 6) | (s1 & 0x3f); - if (enh_check && (s1 & 0xc0) != 0x80) { - return 2; - } - } else if (s0 <= 0xef) { - /* three byte character */ - l = 3; - if (ilen < 3) { - return 0; - } - s1 = cpu_ldub_data_ra(env, addr + 1, ra); - s2 = cpu_ldub_data_ra(env, addr + 2, ra); - c = s0 & 0x0f; - c = (c << 6) | (s1 & 0x3f); - c = (c << 6) | (s2 & 0x3f); - /* Fold the byte-by-byte range descriptions in the PoO into - tests against the complete value. It disallows encodings - that could be smaller, and the UTF-16 surrogates. */ - if (enh_check - && ((s1 & 0xc0) != 0x80 - || (s2 & 0xc0) != 0x80 - || c < 0x1000 - || (c >= 0xd800 && c <= 0xdfff))) { - return 2; - } - } else if (s0 <= (enh_check ? 0xf4 : 0xf7)) { - /* four byte character */ - l = 4; - if (ilen < 4) { - return 0; - } - s1 = cpu_ldub_data_ra(env, addr + 1, ra); - s2 = cpu_ldub_data_ra(env, addr + 2, ra); - s3 = cpu_ldub_data_ra(env, addr + 3, ra); - c = s0 & 0x07; - c = (c << 6) | (s1 & 0x3f); - c = (c << 6) | (s2 & 0x3f); - c = (c << 6) | (s3 & 0x3f); - /* See above. */ - if (enh_check - && ((s1 & 0xc0) != 0x80 - || (s2 & 0xc0) != 0x80 - || (s3 & 0xc0) != 0x80 - || c < 0x010000 - || c > 0x10ffff)) { - return 2; - } - } else { - /* invalid character */ - return 2; - } - - *ochar = c; - *olen = l; - return -1; -} - -static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen, - bool enh_check, uintptr_t ra, - uint32_t *ochar, uint32_t *olen) -{ - uint16_t s0, s1; - uint32_t c, l; - - if (ilen < 2) { - return 0; - } - s0 = cpu_lduw_data_ra(env, addr, ra); - if ((s0 & 0xfc00) != 0xd800) { - /* one word character */ - l = 2; - c = s0; - } else { - /* two word character */ - l = 4; - if (ilen < 4) { - return 0; - } - s1 = cpu_lduw_data_ra(env, addr + 2, ra); - c = extract32(s0, 6, 4) + 1; - c = (c << 6) | (s0 & 0x3f); - c = (c << 10) | (s1 & 0x3ff); - if (enh_check && (s1 & 0xfc00) != 0xdc00) { - /* invalid surrogate character */ - return 2; - } - } - - *ochar = c; - *olen = l; - return -1; -} - -static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen, - bool enh_check, uintptr_t ra, - uint32_t *ochar, uint32_t *olen) -{ - uint32_t c; - - if (ilen < 4) { - return 0; - } - c = cpu_ldl_data_ra(env, addr, ra); - if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) { - /* invalid unicode character */ - return 2; - } - - *ochar = c; - *olen = 4; - return -1; -} - -static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen, - uintptr_t ra, uint32_t c, uint32_t *olen) -{ - uint8_t d[4]; - uint32_t l, i; - - if (c <= 0x7f) { - /* one byte character */ - l = 1; - d[0] = c; - } else if (c <= 0x7ff) { - /* two byte character */ - l = 2; - d[1] = 0x80 | extract32(c, 0, 6); - d[0] = 0xc0 | extract32(c, 6, 5); - } else if (c <= 0xffff) { - /* three byte character */ - l = 3; - d[2] = 0x80 | extract32(c, 0, 6); - d[1] = 0x80 | extract32(c, 6, 6); - d[0] = 0xe0 | extract32(c, 12, 4); - } else { - /* four byte character */ - l = 4; - d[3] = 0x80 | extract32(c, 0, 6); - d[2] = 0x80 | extract32(c, 6, 6); - d[1] = 0x80 | extract32(c, 12, 6); - d[0] = 0xf0 | extract32(c, 18, 3); - } - - if (ilen < l) { - return 1; - } - for (i = 0; i < l; ++i) { - cpu_stb_data_ra(env, addr + i, d[i], ra); - } - - *olen = l; - return -1; -} - -static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen, - uintptr_t ra, uint32_t c, uint32_t *olen) -{ - uint16_t d0, d1; - - if (c <= 0xffff) { - /* one word character */ - if (ilen < 2) { - return 1; - } - cpu_stw_data_ra(env, addr, c, ra); - *olen = 2; - } else { - /* two word character */ - if (ilen < 4) { - return 1; - } - d1 = 0xdc00 | extract32(c, 0, 10); - d0 = 0xd800 | extract32(c, 10, 6); - d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1); - cpu_stw_data_ra(env, addr + 0, d0, ra); - cpu_stw_data_ra(env, addr + 2, d1, ra); - *olen = 4; - } - - return -1; -} - -static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen, - uintptr_t ra, uint32_t c, uint32_t *olen) -{ - if (ilen < 4) { - return 1; - } - cpu_stl_data_ra(env, addr, c, ra); - *olen = 4; - return -1; -} - -static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1, - uint32_t r2, uint32_t m3, uintptr_t ra, - decode_unicode_fn decode, - encode_unicode_fn encode) -{ - uint64_t dst = get_address(env, r1); - uint64_t dlen = get_length(env, r1 + 1); - uint64_t src = get_address(env, r2); - uint64_t slen = get_length(env, r2 + 1); - bool enh_check = m3 & 1; - int cc, i; - - /* Lest we fail to service interrupts in a timely manner, limit the - amount of work we're willing to do. For now, let's cap at 256. */ - for (i = 0; i < 256; ++i) { - uint32_t c, ilen, olen; - - cc = decode(env, src, slen, enh_check, ra, &c, &ilen); - if (unlikely(cc >= 0)) { - break; - } - cc = encode(env, dst, dlen, ra, c, &olen); - if (unlikely(cc >= 0)) { - break; - } - - src += ilen; - slen -= ilen; - dst += olen; - dlen -= olen; - cc = 3; - } - - set_address(env, r1, dst); - set_length(env, r1 + 1, dlen); - set_address(env, r2, src); - set_length(env, r2 + 1, slen); - - return cc; -} - -uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf8, encode_utf16); -} - -uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf8, encode_utf32); -} - -uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf16, encode_utf8); -} - -uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf16, encode_utf32); -} - -uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf32, encode_utf8); -} - -uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) -{ - return convert_unicode(env, r1, r2, m3, GETPC(), - decode_utf32, encode_utf16); -} - -void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, - uintptr_t ra) -{ - /* test the actual access, not just any access to the page due to LAP */ - while (len) { - const uint64_t pagelen = -(addr | TARGET_PAGE_MASK); - const uint64_t curlen = MIN(pagelen, len); - - probe_write(env, addr, curlen, cpu_mmu_index(env, false), ra); - addr = wrap_address(env, addr + curlen); - len -= curlen; - } -} - -void HELPER(probe_write_access)(CPUS390XState *env, uint64_t addr, uint64_t len) -{ - probe_write_access(env, addr, len, GETPC()); -} |