Commit 6d085130b2 for qemu.org

commit 6d085130b20eb9e624dc8f9fd3acb0c6defff1be
Author: Stian Halseth <stian@itx.no>
Date:   Wed Oct 28 12:38:15 2026 +0100

    target/sparc: Model block-initializing stores

    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 <stian@itx.no>
    Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
    Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
    Message-ID: <20261028113816.1651212-2-stian@itx.no>
    Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>

diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 1fc8439a47..416625c6dc 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) {