On Mon, Jul 6, 2026 at 2:41 PM Philippe Mathieu-Daudé
<[email protected]> wrote:
>
> On 6/7/26 20:13, James Hilliard wrote:
> > On Mon, Jul 6, 2026 at 10:57 AM Philippe Mathieu-Daudé
> > <[email protected]> wrote:
> >>
> >> Hi James,
> >>
> >> On 8/6/26 20:59, James Hilliard wrote:
> >>> Add helper support for the Octeon HSH hash selectors. This includes the
> >>> base HSH data/IV windows, MD5, SHA1, SHA256, and SHA512 transform paths,
> >>> and the shared HSH/SHA512 register-window readback and write operations.
> >>>
> >>> The SHA512 path shares the wide HSH register bank with SHA3, SNOW3G, and
> >>> ZUC. Keep the aliased readback and write paths centralized so selector
> >>> decode can route register accesses through these helpers when side
> >>> effects are required.
> >>>
> >>> Signed-off-by: James Hilliard <[email protected]>
> >>> ---
> >>> Changes v13 -> v14:
> >>>     - Keep HSH narrow DAT/IV state updates in the low 32-bit halves of the
> >>>       architectural HSH register bank so paired selector transfers 
> >>> preserve
> >>>       the wide shared-window state.
> >>>
> >>> Changes v10 -> v13:
> >>>     - Keep the 0x0057 SDK compatibility path as an explicit STARTSHA1
> >>>       compatibility helper instead of a generic STARTSHA name.
> >>>
> >>> Changes v9 -> v10:
> >>>     - Remove references to shared-mode tracking; aliased state is kept in 
> >>> the
> >>>       architectural HSH register banks.
> >>>
> >>> Changes v8 -> v9:
> >>>     - Split HSH/SHA helpers into their own COP2 helper patch.
> >>>     - Replace generic selector dispatch with per-operation HSH helpers.
> >>>     - Keep shared-window readback and write helpers grouped with HSH.
> >>>     - Add matching helper.h declarations with the helper implementation.
> >>> ---
> >>>    target/mips/helper.h            |   5 +
> >>>    target/mips/tcg/octeon_crypto.c | 375 
> >>> ++++++++++++++++++++++++++++++++++++++++
> >>>    2 files changed, 380 insertions(+)
> >>>
> >>> diff --git a/target/mips/helper.h b/target/mips/helper.h
> >>> index 71b7a2b30e..834f2d0769 100644
> >>> --- a/target/mips/helper.h
> >>> +++ b/target/mips/helper.h
> >>> @@ -69,6 +69,11 @@ DEF_HELPER_2(octeon_cp2_mt_des3_dec, void, env, i64)
> >>>    DEF_HELPER_2(octeon_cp2_mt_camellia_fl, void, env, i64)
> >>>    DEF_HELPER_2(octeon_cp2_mt_camellia_flinv, void, env, i64)
> >>>    DEF_HELPER_2(octeon_cp2_mt_camellia_round, void, env, i64)
> >>> +DEF_HELPER_2(octeon_cp2_mt_hsh_startsha1_compat, void, env, i64)
> >>> +DEF_HELPER_2(octeon_cp2_mt_hsh_startmd5, void, env, i64)
> >>> +DEF_HELPER_2(octeon_cp2_mt_hsh_startsha256, void, env, i64)
> >>> +DEF_HELPER_2(octeon_cp2_mt_hsh_startsha, void, env, i64)
> >>> +DEF_HELPER_2(octeon_cp2_mt_hsh_startsha512, void, env, i64)
> >>>
> >>>    /* microMIPS functions */
> >>>    DEF_HELPER_4(lwm, void, env, tl, tl, i32)
> >>> diff --git a/target/mips/tcg/octeon_crypto.c 
> >>> b/target/mips/tcg/octeon_crypto.c
> >>> index 67959618cc..9523c84667 100644
> >>> --- a/target/mips/tcg/octeon_crypto.c
> >>> +++ b/target/mips/tcg/octeon_crypto.c
> >>> @@ -153,6 +153,348 @@ static void octeon_gfm_mul64_uia2(const uint64_t 
> >>> x[2], const uint64_t y[2],
> >>>        out[1] = revbit64(res);
> >>>    }
> >>>
> >>> +static inline uint32_t octeon_hsh_get32(const uint64_t *regs,
> >>> +                                        unsigned int index)
> >>> +{
> >>> +    return regs[index];
> >>> +}
> >>> +
> >>> +static inline void octeon_hsh_set32(uint64_t *regs, unsigned int index,
> >>> +                                    uint32_t value)
> >>> +{
> >>> +    regs[index] = (regs[index] & ~(uint64_t)UINT32_MAX) | value;
> >>> +}
> >>> +
> >>> +static inline void octeon_hsh_set_pair(uint64_t *regs, unsigned int 
> >>> index,
> >>> +                                       uint64_t value)
> >>> +{
> >>> +    octeon_hsh_set32(regs, index * 2, value >> 32);
> >>> +    octeon_hsh_set32(regs, index * 2 + 1, value);
> >>> +}
> >>> +
> >>> +static void octeon_md5_transform(MIPSOcteonCryptoState *crypto)
> >>> +{
> >>> +    static const uint32_t k[64] = {
> >>> +        0xd76aa478U, 0xe8c7b756U, 0x242070dbU, 0xc1bdceeeU,
> >>> +        0xf57c0fafU, 0x4787c62aU, 0xa8304613U, 0xfd469501U,
> >>> +        0x698098d8U, 0x8b44f7afU, 0xffff5bb1U, 0x895cd7beU,
> >>> +        0x6b901122U, 0xfd987193U, 0xa679438eU, 0x49b40821U,
> >>> +        0xf61e2562U, 0xc040b340U, 0x265e5a51U, 0xe9b6c7aaU,
> >>> +        0xd62f105dU, 0x02441453U, 0xd8a1e681U, 0xe7d3fbc8U,
> >>> +        0x21e1cde6U, 0xc33707d6U, 0xf4d50d87U, 0x455a14edU,
> >>> +        0xa9e3e905U, 0xfcefa3f8U, 0x676f02d9U, 0x8d2a4c8aU,
> >>> +        0xfffa3942U, 0x8771f681U, 0x6d9d6122U, 0xfde5380cU,
> >>> +        0xa4beea44U, 0x4bdecfa9U, 0xf6bb4b60U, 0xbebfbc70U,
> >>> +        0x289b7ec6U, 0xeaa127faU, 0xd4ef3085U, 0x04881d05U,
> >>> +        0xd9d4d039U, 0xe6db99e5U, 0x1fa27cf8U, 0xc4ac5665U,
> >>> +        0xf4292244U, 0x432aff97U, 0xab9423a7U, 0xfc93a039U,
> >>> +        0x655b59c3U, 0x8f0ccc92U, 0xffeff47dU, 0x85845dd1U,
> >>> +        0x6fa87e4fU, 0xfe2ce6e0U, 0xa3014314U, 0x4e0811a1U,
> >>> +        0xf7537e82U, 0xbd3af235U, 0x2ad7d2bbU, 0xeb86d391U,
> >>> +    };
> >>> +    static const uint8_t s[64] = {
> >>> +        7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
> >>> +        5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
> >>> +        4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
> >>> +        6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
> >>> +    };
> >>> +    uint32_t m[16];
> >>> +    uint32_t a, b, c, d;
> >>> +    uint32_t aa, bb, cc, dd;
> >>> +    int i;
> >>> +
> >>> +    for (i = 0; i < 16; i++) {
> >>> +        m[i] = bswap32(octeon_hsh_get32(crypto->hsh_dat, i));
> >>> +    }
> >>> +
> >>> +    a = bswap32(octeon_hsh_get32(crypto->hsh_iv, 0));
> >>> +    b = bswap32(octeon_hsh_get32(crypto->hsh_iv, 1));
> >>> +    c = bswap32(octeon_hsh_get32(crypto->hsh_iv, 2));
> >>> +    d = bswap32(octeon_hsh_get32(crypto->hsh_iv, 3));
> >>
> >> The MD5 algorithm is little-endian, so I think for these we want
> >> cpu_to_le32(), ...
> >
> > Looking at the CN71XX manual, I think the current handling matches the
> > architectural HSH register view.  The HSH DAT/IV narrow selectors build
> > HASHDAT/HASHIV words from GPR halves, and the SHA-512 wide selectors move
> > whole GPR values into HASHDAT/HASHIV.  These are COP2 register values,
> > not target-memory byte arrays.
> >
> > The manual calls out MD5 specially: HASHDAT/HASHIV are in the big-endian
> > HSH register-word order, while MD5 is little-endian, so the MD5 operation
> > byte-swaps HASHDAT/HASHIV on entry and exit.  That is what the current
> > bswap32() calls model.
>
> This call works on little endian hosts, because the target CPU is only
> built for big-endian in user-mode. I don't expect your tests to pass on
> a big-endian host, because we don't want to swap in this case.

I agree those helpers are host-endian conversions, but that is why I
don't think they apply here.

The HSH state is modeled as architectural COP2 register values, not as a
host byte array. The narrow HSH DAT/IV selectors split GPR[rt] into
HASHDAT/HASHIV low-32 register words, and the translator already handles
host layout when placing those low 32 bits in the uint64_t state. After
that, octeon_hsh_get32() is reading an architectural register word.

For MD5, the CN71XX manual describes a byte swap between the HSH
big-endian register-word view and MD5's little-endian word order. That
swap is part of the Octeon operation, so it must be unconditional with
respect to the QEMU host. Using cpu_to_le32() would make it host-dependent:
identity on little-endian hosts and swapped on big-endian hosts.

Likewise, the SHA paths consume the HSH register words directly in the
manual pseudo-code, with no analogous MD5 byte-swap step. Using
cpu_to_be32()/be32_to_cpu() there would also introduce host-dependent
behavior that the modeled hardware operation does not have.

> See in include/qemu/bswap.h:
>
>   60  * Endianness conversion functions between host cpu and specified
> endianness.
>   63  *
>   64  * uint16_t le16_to_cpu(uint16_t v);
>   65  * uint32_t le32_to_cpu(uint32_t v);
>   66  * uint64_t le64_to_cpu(uint64_t v);
>   67  * uint16_t be16_to_cpu(uint16_t v);
>   68  * uint32_t be32_to_cpu(uint32_t v);
>   69  * uint64_t be64_to_cpu(uint64_t v);
>   70  *
>   71  * Convert the value @v from the specified format to the native
>   72  * endianness of the host CPU by byteswapping if necessary, and
>   73  * return the converted value.
>   74  *
>   75  * uint16_t cpu_to_le16(uint16_t v);
>   76  * uint32_t cpu_to_le32(uint32_t v);
>   77  * uint64_t cpu_to_le64(uint64_t v);
>   78  * uint16_t cpu_to_be16(uint16_t v);
>   79  * uint32_t cpu_to_be32(uint32_t v);
>   80  * uint64_t cpu_to_be64(uint64_t v);
>   81  *
>   82  * Convert the value @v from the native endianness of the host CPU
>   83  * to the specified format by byteswapping if necessary, and return
>   84  * the converted value.
>
> >
> >>> +    aa = a;
> >>> +    bb = b;
> >>> +    cc = c;
> >>> +    dd = d;
> >>> +
> >>> +    for (i = 0; i < 64; i++) {
> >>> +        uint32_t f, g, tmp;
> >>> +
> >>> +        if (i < 16) {
> >>> +            f = (b & c) | ((~b) & d);
> >>> +            g = i;
> >>> +        } else if (i < 32) {
> >>> +            f = (d & b) | ((~d) & c);
> >>> +            g = (5 * i + 1) & 0xf;
> >>> +        } else if (i < 48) {
> >>> +            f = b ^ c ^ d;
> >>> +            g = (3 * i + 5) & 0xf;
> >>> +        } else {
> >>> +            f = c ^ (b | (~d));
> >>> +            g = (7 * i) & 0xf;
> >>> +        }
> >>> +
> >>> +        tmp = d;
> >>> +        d = c;
> >>> +        c = b;
> >>> +        b = b + rol32(a + f + k[i] + m[g], s[i]);
> >>> +        a = tmp;
> >>> +    }
> >>> +
> >>> +    a += aa;
> >>> +    b += bb;
> >>> +    c += cc;
> >>> +    d += dd;
> >>> +    octeon_hsh_set32(crypto->hsh_iv, 0, bswap32(a));
> >>> +    octeon_hsh_set32(crypto->hsh_iv, 1, bswap32(b));
> >>> +    octeon_hsh_set32(crypto->hsh_iv, 2, bswap32(c));
> >>> +    octeon_hsh_set32(crypto->hsh_iv, 3, bswap32(d));
> >>
> >> ... and le32_to_cpu() here.
> >>
> >>> +}
> >>> +
> >>> +static void octeon_sha1_transform(MIPSOcteonCryptoState *crypto)
> >>> +{
> >>> +    uint32_t w[80];
> >>> +    uint32_t a, b, c, d, e;
> >>> +    uint32_t orig[5];
> >>> +    int i;
> >>> +
> >>> +    for (i = 0; i < 16; i++) {
> >>> +        w[i] = octeon_hsh_get32(crypto->hsh_dat, i);
> >>> +    }
> >>> +    for (i = 16; i < 80; i++) {
> >>> +        w[i] = rol32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
> >>> +    }
> >>> +
> >>> +    for (i = 0; i < 5; i++) {
> >>> +        orig[i] = octeon_hsh_get32(crypto->hsh_iv, i);
> >>> +    }
> >>
> >> SHA is big-endian so we are good, but watch out this CPU could switch
> >> endianness.
> >>
> >> Maybe safer to use cpu_to_be32() and be32_to_cpu()?
> >
> > For SHA-1, SHA-256, and SHA-512, the manual pseudo-code feeds the HSH
> > register words directly into the SHA operation and does not describe a
> > similar byte-swap.  So using cpu_to_be32()/be32_to_cpu() here would
> > byte-swap based on host endianness, which is not the architectural HSH
> > operation.
> >
> >>> +    a = orig[0];
> >>> +    b = orig[1];
> >>> +    c = orig[2];
> >>> +    d = orig[3];
> >>> +    e = orig[4];
> >>> +
> >>> +    for (i = 0; i < 80; i++) {
> >>> +        uint32_t f, k, temp;
> >>> +
> >>> +        if (i < 20) {
> >>> +            f = (b & c) | ((~b) & d);
> >>> +            k = 0x5a827999;
> >>> +        } else if (i < 40) {
> >>> +            f = b ^ c ^ d;
> >>> +            k = 0x6ed9eba1;
> >>> +        } else if (i < 60) {
> >>> +            f = (b & c) | (b & d) | (c & d);
> >>> +            k = 0x8f1bbcdc;
> >>> +        } else {
> >>> +            f = b ^ c ^ d;
> >>> +            k = 0xca62c1d6;
> >>> +        }
> >>> +
> >>> +        temp = rol32(a, 5) + f + e + k + w[i];
> >>> +        e = d;
> >>> +        d = c;
> >>> +        c = rol32(b, 30);
> >>> +        b = a;
> >>> +        a = temp;
> >>> +    }
> >>> +
> >>> +    orig[0] += a;
> >>> +    orig[1] += b;
> >>> +    orig[2] += c;
> >>> +    orig[3] += d;
> >>> +    orig[4] += e;
> >>> +    for (i = 0; i < 5; i++) {
> >>> +        octeon_hsh_set32(crypto->hsh_iv, i, orig[i]);
> >>> +    }
> >>> +}
> >>> +
> >>> +static void octeon_sha256_transform(MIPSOcteonCryptoState *crypto)
> >>> +{
> >>
> >> (ditto)
> >>
> >>> +}
> >>> +
> >>> +static void octeon_sha512_transform(MIPSOcteonCryptoState *crypto)
> >>> +{
> >>
> >> (ditto)
> >>
> >>> +}
> >
>

Reply via email to