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.

> > +    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