/* memmove */ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); /* mvc with source pointing to the byte after the destination is the same as memset with the first source byte */ if (dest == (src + 1)) { fast_memset(env, dest, cpu_ldub_data(env, src), l + 1); return; } /* mvc and memmove do not behave the same when areas overlap! */ if ((dest < src) || (src + l < dest)) { fast_memmove(env, dest, src, l + 1); return; } /* slow version with byte accesses which always work */ for (i = 0; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } }
/* compare unsigned byte arrays */ uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2) { int i; unsigned char x, y; uint32_t cc; HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", __func__, l, s1, s2); for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, s1 + i); y = cpu_ldub_data(env, s2 + i); HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y); if (x < y) { cc = 1; goto done; } else if (x > y) { cc = 2; goto done; } } cc = 0; done: HELPER_LOG("\n"); return cc; }
/* xor on array */ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); /* xor with itself is the same as memset(0) */ if (src == dest) { fast_memset(env, dest, 0, l + 1); return 0; } for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
/* xor on array */ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); #ifndef CONFIG_USER_ONLY /* xor with itself is the same as memset(0) */ if ((l > 32) && (src == dest) && (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) { mvc_fast_memset(env, l + 1, dest, 0); return 0; } #else if (src == dest) { memset(g2h(dest), 0, l + 1); return 0; } #endif for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src, uint32_t l) { int mmu_idx = cpu_mmu_index(env, false); while (l > 0) { void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx); void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx); if (src_p && dest_p) { /* Access to both whole pages granted. */ int l_adj = adj_len_to_page(l, src); l_adj = adj_len_to_page(l_adj, dest); memmove(dest_p, src_p, l_adj); src += l_adj; dest += l_adj; l -= l_adj; } else { /* We failed to get access to one or both whole pages. The next read or write access will likely fill the QEMU TLB for the next iteration. */ cpu_stb_data(env, dest, cpu_ldub_data(env, src)); src++; dest++; l--; } } }
/* compare logical under mask */ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, uint64_t addr) { uint8_t r, d; uint32_t cc; HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, mask, addr); cc = 0; while (mask) { if (mask & 8) { d = cpu_ldub_data(env, addr); r = (r1 & 0xff000000UL) >> 24; 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; }
static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { hwaddr dest_phys; hwaddr src_phys; hwaddr len = l; void *dest_p; void *src_p; uint64_t asc = env->psw.mask & PSW_MASK_ASC; int flags; if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { cpu_stb_data(env, dest, 0); cpu_abort(env, "should never reach here"); } dest_phys |= dest & ~TARGET_PAGE_MASK; if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) { cpu_ldub_data(env, src); cpu_abort(env, "should never reach here"); } src_phys |= src & ~TARGET_PAGE_MASK; dest_p = cpu_physical_memory_map(dest_phys, &len, 1); src_p = cpu_physical_memory_map(src_phys, &len, 0); memmove(dest_p, src_p, len); cpu_physical_memory_unmap(dest_p, 1, len, len); cpu_physical_memory_unmap(src_p, 0, len, len); }
/* or on array */ uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i; unsigned char x; uint32_t cc = 0; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); for (i = 0; i <= l; i++) { x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i); if (x) { cc = 1; } cpu_stb_data(env, dest + i, x); } return cc; }
/* memmove */ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { int i = 0; int x = 0; uint32_t l_64 = (l + 1) / 8; HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", __func__, l, dest, src); #ifndef CONFIG_USER_ONLY if ((l > 32) && (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) && (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) { if (dest == (src + 1)) { mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src)); return; } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { mvc_fast_memmove(env, l + 1, dest, src); return; } } #else if (dest == (src + 1)) { memset(g2h(dest), cpu_ldub_data(env, src), l + 1); return; } else { memmove(g2h(dest), g2h(src), l + 1); return; } #endif /* handle the parts that fit into 8-byte loads/stores */ if (dest != (src + 1)) { for (i = 0; i < l_64; i++) { cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x)); x += 8; } } /* slow version crossing pages with byte accesses */ for (i = x; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } }