From: Stian Halseth <[email protected]> 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]> Reviewed-by: Mark Cave-Ayland <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Message-ID: <[email protected]> Signed-off-by: Mark Cave-Ayland <[email protected]> --- target/sparc/translate.c | 43 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1fc8439a475..416625c6dc9 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1853,6 +1853,27 @@ 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, + MemOp size) +{ + TCGLabel *skip = gen_new_label(); + TCGv t = tcg_temp_new(); + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_brcondi_tl(TCG_COND_TSTNE, addr, 31, skip); + /* A narrower store also zeroes the rest of the first doubleword. */ + for (int i = size == MO_64 ? 8 : 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 +1884,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, da->memop & MO_SIZE); + } /* fall through */ case GET_ASI_DIRECT: @@ -2169,6 +2197,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, MO_64); + 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.47.3
