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