aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorDavid Hildenbrand <david@redhat.com>2019-09-05 19:36:30 +0200
committerDavid Hildenbrand <david@redhat.com>2019-09-23 09:28:29 +0200
commitab89acd0b77355888dcbd68725fdcba03b08e466 (patch)
treefb67580317b8bd6fa285b01e2799142bbf51c475 /target
parentbb36ed88e91252c8315c151d12df227999604e62 (diff)
downloadqemu-ab89acd0b77355888dcbd68725fdcba03b08e466.zip
qemu-ab89acd0b77355888dcbd68725fdcba03b08e466.tar.gz
qemu-ab89acd0b77355888dcbd68725fdcba03b08e466.tar.bz2
s390x/tcg: MVO: Fault-safe handling
Each operand can have a maximum length of 16. Make sure to prepare all reads/writes before writing. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: David Hildenbrand <david@redhat.com>
Diffstat (limited to 'target')
-rw-r--r--target/s390x/mem_helper.c27
1 files changed, 15 insertions, 12 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 4a4b4ea..44e5358 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -522,31 +522,34 @@ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
/* 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();
- int len_dest = l >> 4;
- int len_src = l & 0xf;
uint8_t byte_dest, byte_src;
- int i;
+ S390Access srca, desta;
+ int i, j;
- src += len_src;
- dest += len_dest;
+ 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_src = cpu_ldub_data_ra(env, src, ra);
- byte_dest = cpu_ldub_data_ra(env, dest, ra);
+ 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);
- cpu_stb_data_ra(env, dest, byte_dest, ra);
+ access_set_byte(env, &desta, len_dest - 1, byte_dest, ra);
/* Process remaining bytes from right to left */
- for (i = 1; i <= len_dest; i++) {
+ for (i = len_dest - 2, j = len_src - 2; i >= 0; i--, j--) {
byte_dest = byte_src >> 4;
- if (len_src - i >= 0) {
- byte_src = cpu_ldub_data_ra(env, src - i, ra);
+ if (j >= 0) {
+ byte_src = access_get_byte(env, &srca, j, ra);
} else {
byte_src = 0;
}
byte_dest |= byte_src << 4;
- cpu_stb_data_ra(env, dest - i, byte_dest, ra);
+ access_set_byte(env, &desta, i, byte_dest, ra);
}
}