On T4 and later, a block-initializing store to the first doubleword of a 32-byte block zeroes the rest of the block (measured on a T4). glibc's niagara4 memset and Linux's NG4memset rely on this, and OpenSSL's T4 AES code uses the FP form, stdfa.
QEMU only accepted the integer form, on HYPV CPUs, as a plain store, so with the crypto hwcap a multiarch glibc memset leaves stale data. Accept both forms when the CPU has the crypto feature, which implies T4 or later, and zero the 32-byte block as T4 does. Signed-off-by: Stian Halseth <[email protected]> --- target/sparc/translate.c | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1fc8439a47..009340a3bb 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1853,6 +1853,26 @@ static void gen_ld_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) } } +/* + * On T4 and later, a block-initializing store to the first doubleword of + * a 32-byte block zeroes the rest of the block. glibc and Linux memset + * rely on this and store only one doubleword per block when zeroing. + */ +static void gen_blkinit_zero(DisasContext *dc, DisasASI *da, TCGv addr) +{ + TCGLabel *skip = gen_new_label(); + TCGv t = tcg_temp_new(); + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_andi_tl(t, addr, 31); + tcg_gen_brcondi_tl(TCG_COND_NE, t, 0, skip); + for (int i = 0; i < 32; i += 8) { + tcg_gen_addi_tl(t, addr, i); + tcg_gen_qemu_st_i64(zero, t, da->mem_idx, MO_UQ); + } + gen_set_label(skip); +} + static void gen_st_asi(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) { switch (da->type) { @@ -1863,12 +1883,19 @@ static void gen_st_asi(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) if (TARGET_LONG_BITS == 32) { gen_exception(dc, TT_ILL_INSN); break; - } else if (!(dc->def->features & CPU_FEATURE_HYPV)) { - /* Pre OpenSPARC CPUs don't have these */ + } else if (!(dc->def->features & + (CPU_FEATURE_HYPV | CPU_FEATURE_CRYPTO))) { + /* + * Pre-OpenSPARC CPUs lack block-init stores. Crypto + * instructions imply T4 or later, which has them. + */ gen_exception(dc, TT_ILL_INSN); break; } /* In OpenSPARC T1+ CPUs TWINX ASIs in store are ST_BLKINIT_ ASIs */ + if (dc->def->features & CPU_FEATURE_CRYPTO) { + gen_blkinit_zero(dc, da, addr); + } /* fall through */ case GET_ASI_DIRECT: @@ -2169,6 +2196,17 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, } break; + case GET_ASI_DTWINX: + /* stdfa with a block-initializing ASI, T4 and later */ + if (orig_size == MO_64 && (dc->def->features & CPU_FEATURE_CRYPTO)) { + gen_blkinit_zero(dc, da, addr); + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN); + } else { + gen_exception(dc, TT_ILL_INSN); + } + break; + case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ if (orig_size == MO_64 && (rd & 7) == 0) { -- 2.43.0
