From c69401bbf659be4de8e699a7e0d230051c0e4765 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Mon, 8 Jun 2026 12:59:29 -0600 Subject: [PATCH] target/mips: add Octeon GFM COP2 helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper support for the Octeon GFM carryless multiply selectors. This models the normal and reflected multiplication paths, including the XOR-and-multiply forms that update the result/input state used by Octeon crypto code. Reflected selectors operate on the architectural GFM register bank using bit-reflected register transfers rather than a separate shadow state. Keep the 64-bit UIA2 reduction path used by SNOW3G F9 and share that shortcut between the normal and reflected XORMUL1 paths. Signed-off-by: James Hilliard Message-ID: <20260608-mips-octeon-missing-insns-v2-v16-4-daef7a0d8b04@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/helper.h | 9 ++ target/mips/tcg/octeon_crypto.c | 142 ++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/target/mips/helper.h b/target/mips/helper.h index e802f50fd6..20ffbfb709 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -27,6 +27,10 @@ DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) /* Octeon COP2 selector operation helpers. */ DEF_HELPER_1(octeon_cp2_mf_crc_iv_reflect, i64, env) +DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect0, i64, env) +DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect1, i64, env) +DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect0, i64, env) +DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect1, i64, env) DEF_HELPER_2(octeon_cp2_mt_crc_write_iv_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_byte, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_half, void, env, i64) @@ -38,6 +42,11 @@ DEF_HELPER_2(octeon_cp2_mt_crc_write_dword, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_dword_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var_reflect, void, env, i64) +DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect0, void, env, i64) +DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect1, void, env, i64) +DEF_HELPER_2(octeon_cp2_mt_gfm_xor0_reflect, void, env, i64) +DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1_reflect, void, env, i64) +DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1, 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 c589077ea7..b417dcf53b 100644 --- a/target/mips/tcg/octeon_crypto.c +++ b/target/mips/tcg/octeon_crypto.c @@ -11,6 +11,7 @@ #include "internal.h" #include "exec/helper-proto.h" #include "crypto/aes.h" +#include "crypto/clmul.h" #include "crypto/sm4.h" #include "qemu/bitops.h" #include "qemu/host-utils.h" @@ -75,11 +76,152 @@ static void octeon_crc_update_reflect(MIPSOcteonCryptoState *crypto, octeon_crc_set_state_reflect(crypto, crc); } +static void octeon_gfm_mul(const uint64_t x[2], const uint64_t y[2], + uint16_t poly, uint64_t out[2]) +{ + uint64_t zh = 0, zl = 0; + uint64_t vh = y[0], vl = y[1]; + uint64_t rh = (uint64_t)poly << 48; + int i; + + /* + * Keep the reflected-shift formulation used by Octeon software: the + * selector polynomial is already in reflected bit order, and the software + * view folds its 16 reduction bits from the top of the high word. + */ + for (i = 0; i < 128; i++) { + bool bit; + bool lsb; + + if (i < 64) { + bit = (x[0] >> (63 - i)) & 1; + } else { + bit = (x[1] >> (127 - i)) & 1; + } + if (bit) { + zh ^= vh; + zl ^= vl; + } + + lsb = vl & 1; + vl = (vh << 63) | (vl >> 1); + vh >>= 1; + if (lsb) { + vh ^= rh; + } + } + + out[0] = zh; + out[1] = zl; +} + +static uint64_t octeon_gfm_reduce64(Int128 product, uint8_t poly) +{ + uint64_t lo = int128_getlo(product); + uint64_t hi = int128_gethi(product); + + while (hi) { + int bit = 63 - clz64(hi); + + hi ^= 1ULL << bit; + lo ^= (uint64_t)poly << bit; + if (bit > 56) { + hi ^= (uint64_t)poly >> (64 - bit); + } + } + + return lo; +} + +static void octeon_gfm_mul64_uia2(const uint64_t x[2], const uint64_t y[2], + uint8_t poly, uint64_t out[2]) +{ + /* + * SNOW3G UIA2 uses the GFM datapath as a reflected 64-bit multiply in + * the low half of the 128-bit register pair. When RESINP[0], MUL[1], + * and the high polynomial byte are all zero, octeon_gfm_mul() observes + * only x[1], y[0], and the low 8-bit polynomial. Reflect those operands + * into normal carryless-multiply order and reflect the reduced result + * back into RESINP[1]. + */ + uint64_t vx = revbit64(x[1]); + uint64_t vy = revbit64(y[0]); + Int128 product = clmul_64(vx, vy); + uint64_t res = octeon_gfm_reduce64(product, revbit32(poly) >> 24); + + out[0] = 0; + out[1] = revbit64(res); +} + uint64_t helper_octeon_cp2_mf_crc_iv_reflect(CPUMIPSState *env) { return octeon_crc_reflect32_by_byte(env->octeon_crypto.crc_iv); } +uint64_t helper_octeon_cp2_mf_gfm_mul_reflect0(CPUMIPSState *env) +{ + return revbit64(env->octeon_crypto.gfm_mul[0]); +} + +uint64_t helper_octeon_cp2_mf_gfm_mul_reflect1(CPUMIPSState *env) +{ + return revbit64(env->octeon_crypto.gfm_mul[1]); +} + +uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect0(CPUMIPSState *env) +{ + return revbit64(env->octeon_crypto.gfm_resinp[0]); +} + +uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect1(CPUMIPSState *env) +{ + return revbit64(env->octeon_crypto.gfm_resinp[1]); +} + +void helper_octeon_cp2_mt_gfm_mul_reflect0(CPUMIPSState *env, uint64_t value) +{ + env->octeon_crypto.gfm_mul[0] = revbit64(value); +} + +void helper_octeon_cp2_mt_gfm_mul_reflect1(CPUMIPSState *env, uint64_t value) +{ + env->octeon_crypto.gfm_mul[1] = revbit64(value); +} + +void helper_octeon_cp2_mt_gfm_xor0_reflect(CPUMIPSState *env, uint64_t value) +{ + env->octeon_crypto.gfm_resinp[0] ^= revbit64(value); +} + +static void octeon_gfm_xormul1_common(MIPSOcteonCryptoState *crypto, + uint64_t value) +{ + crypto->gfm_resinp[1] ^= value; + if (crypto->gfm_poly <= 0xff && crypto->gfm_mul[1] == 0 && + crypto->gfm_resinp[0] == 0) { + octeon_gfm_mul64_uia2(crypto->gfm_resinp, crypto->gfm_mul, + crypto->gfm_poly, crypto->gfm_resinp); + } else { + octeon_gfm_mul(crypto->gfm_resinp, crypto->gfm_mul, crypto->gfm_poly, + crypto->gfm_resinp); + } +} + +void helper_octeon_cp2_mt_gfm_xormul1_reflect(CPUMIPSState *env, + uint64_t value) +{ + MIPSOcteonCryptoState *crypto = &env->octeon_crypto; + + octeon_gfm_xormul1_common(crypto, revbit64(value)); +} + +void helper_octeon_cp2_mt_gfm_xormul1(CPUMIPSState *env, uint64_t value) +{ + MIPSOcteonCryptoState *crypto = &env->octeon_crypto; + + octeon_gfm_xormul1_common(crypto, value); +} + void helper_octeon_cp2_mt_crc_write_iv_reflect(CPUMIPSState *env, uint64_t value) {