target/mips: add Octeon GFM COP2 helpers

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 <james.hilliard1@gmail.com>
Message-ID: <20260608-mips-octeon-missing-insns-v2-v16-4-daef7a0d8b04@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
This commit is contained in:
James Hilliard
2026-06-08 12:59:29 -06:00
committed by Philippe Mathieu-Daudé
parent e1ac6b2775
commit c69401bbf6
2 changed files with 151 additions and 0 deletions

View File

@@ -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)

View File

@@ -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)
{