From 936fda4d771fdc51d3640bdb0cc8ceec14165730 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 22 Feb 2021 16:40:35 -0300 Subject: target/ppc: Fix bcdsub. emulation when result overflows The commit d03b174a83 (target/ppc: simplify bcdadd/sub functions) meant to simplify some of the code but it inadvertently altered the way the CR6 field is set after the operation has overflowed. The CR6 bits are set based on the *unbounded* result of the operation, so we need to look at the result before returning from bcd_add_mag, otherwise we will look at 0 when it overflows. Consider the following subtraction: v0 = 0x9999999999999999999999999999999c (maximum positive BCD value) v1 = 0x0000000000000000000000000000001d (negative one BCD value) bcdsub. v0,v0,v1,0 The Power ISA 2.07B says: If the unbounded result is greater than zero, do the following. If PS=0, the sign code of the result is set to 0b1100. If PS=1, the sign code of the result is set to 0b1111. If the operation overflows, CR field 6 is set to 0b0101. Otherwise, CR field 6 is set to 0b0100. POWER9 hardware: vr0 = 0x0000000000000000000000000000000c (positive zero BCD value) cr6 = 0b0101 (0x5) (positive, overflow) QEMU: vr0 = 0x0000000000000000000000000000000c (positive zero BCD value) cr6 = 0b0011 (0x3) (zero, overflow) <--- wrong This patch reverts the part of d03b174a83 that introduced the problem and adds a test-case to avoid further regressions: before: $ make run-tcg-tests-ppc64le-linux-user (...) TEST bcdsub on ppc64le bcdsub: qemu/tests/tcg/ppc64le/bcdsub.c:58: test_bcdsub_gt: Assertion `(cr >> 4) == ((1 << 2) | (1 << 0))' failed. Fixes: d03b174a83 (target/ppc: simplify bcdadd/sub functions) Reported-by: Paul Clarke Signed-off-by: Fabiano Rosas Message-Id: <20210222194035.2723056-1-farosas@linux.ibm.com> Signed-off-by: David Gibson --- target/ppc/int_helper.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'target/ppc') diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 0b682a1f94..429de28494 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -2175,14 +2175,17 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) return 0; } -static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); if (digit > 9) { carry = 1; digit -= 10; @@ -2194,6 +2197,7 @@ static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } *overflow = carry; + return is_zero; } static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, @@ -2225,14 +2229,15 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) int sgnb = bcd_get_sgn(b); int invalid = (sgna == 0) || (sgnb == 0); int overflow = 0; + int zero = 0; uint32_t cr = 0; ppc_avr_t result = { .u64 = { 0, 0 } }; if (!invalid) { if (sgna == sgnb) { result.VsrB(BCD_DIG_BYTE(0)) = bcd_preferred_sgn(sgna, ps); - bcd_add_mag(&result, a, b, &invalid, &overflow); - cr = bcd_cmp_zero(&result); + zero = bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? CRF_GT : CRF_LT; } else { int magnitude = bcd_cmp_mag(a, b); if (magnitude > 0) { @@ -2255,6 +2260,8 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) cr = CRF_SO; } else if (overflow) { cr |= CRF_SO; + } else if (zero) { + cr |= CRF_EQ; } *r = result; -- cgit v1.2.3-55-g7522 From 09db2216aa06be8bacd0101723d9c257f75c803f Mon Sep 17 00:00:00 2001 From: Vitaly Cheptsov Date: Wed, 3 Mar 2021 17:08:51 +0300 Subject: target/ppc: fix icount support on Book-e vms accessing SPRs Failing to guard SPR access with gen_io_start/gen_stop_exception causes "Bad icount read" exceptions when running VMs with e500mc and e500v2 CPUs with an icount parameter. Cc: David Gibson Cc: Greg Kurz Cc: Paolo Bonzini Signed-off-by: Vitaly Cheptsov Message-Id: <20210303140851.78383-1-cheptsov@ispras.ru> Signed-off-by: David Gibson --- target/ppc/translate_init.c.inc | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'target/ppc') diff --git a/target/ppc/translate_init.c.inc b/target/ppc/translate_init.c.inc index e7324e85cd..09c9ae2c98 100644 --- a/target/ppc/translate_init.c.inc +++ b/target/ppc/translate_init.c.inc @@ -567,35 +567,71 @@ static void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_store_spr(sprn, cpu_gpr[gprn]); gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } #endif -- cgit v1.2.3-55-g7522