@samitouri / QOSamiQemu / commits / c69401bbf6

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>

James Hilliard committed Jun 8, 2026 at 12:59 UTC c69401bbf659be4de8e699a7e0d230051c0e4765
2 files changed +151
target/mips/helper.h
+9
@@ -27,6 +27,10 @@ DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32)
27
28 /* Octeon COP2 selector operation helpers. */
29 DEF_HELPER_1(octeon_cp2_mf_crc_iv_reflect, i64, env)
30 +DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect0, i64, env)
31 +DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect1, i64, env)
32 +DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect0, i64, env)
33 +DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect1, i64, env)
34 DEF_HELPER_2(octeon_cp2_mt_crc_write_iv_reflect, void, env, i64)
35 DEF_HELPER_2(octeon_cp2_mt_crc_write_byte, void, env, i64)
36 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)
42 DEF_HELPER_2(octeon_cp2_mt_crc_write_var, void, env, i64)
43 DEF_HELPER_2(octeon_cp2_mt_crc_write_dword_reflect, void, env, i64)
44 DEF_HELPER_2(octeon_cp2_mt_crc_write_var_reflect, void, env, i64)
45 +DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect0, void, env, i64)
46 +DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect1, void, env, i64)
47 +DEF_HELPER_2(octeon_cp2_mt_gfm_xor0_reflect, void, env, i64)
48 +DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1_reflect, void, env, i64)
49 +DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1, void, env, i64)
50
51 /* microMIPS functions */
52 DEF_HELPER_4(lwm, void, env, tl, tl, i32)
target/mips/tcg/octeon_crypto.c
+142
@@ -11,6 +11,7 @@
11 #include "internal.h"
12 #include "exec/helper-proto.h"
13 #include "crypto/aes.h"
14 +#include "crypto/clmul.h"
15 #include "crypto/sm4.h"
16 #include "qemu/bitops.h"
17 #include "qemu/host-utils.h"
@@ -75,11 +76,152 @@ static void octeon_crc_update_reflect(MIPSOcteonCryptoState *crypto,
76 octeon_crc_set_state_reflect(crypto, crc);
77 }
78
79 +static void octeon_gfm_mul(const uint64_t x[2], const uint64_t y[2],
80 + uint16_t poly, uint64_t out[2])
81 +{
82 + uint64_t zh = 0, zl = 0;
83 + uint64_t vh = y[0], vl = y[1];
84 + uint64_t rh = (uint64_t)poly << 48;
85 + int i;
86 +
87 + /*
88 + * Keep the reflected-shift formulation used by Octeon software: the
89 + * selector polynomial is already in reflected bit order, and the software
90 + * view folds its 16 reduction bits from the top of the high word.
91 + */
92 + for (i = 0; i < 128; i++) {
93 + bool bit;
94 + bool lsb;
95 +
96 + if (i < 64) {
97 + bit = (x[0] >> (63 - i)) & 1;
98 + } else {
99 + bit = (x[1] >> (127 - i)) & 1;
100 + }
101 + if (bit) {
102 + zh ^= vh;
103 + zl ^= vl;
104 + }
105 +
106 + lsb = vl & 1;
107 + vl = (vh << 63) | (vl >> 1);
108 + vh >>= 1;
109 + if (lsb) {
110 + vh ^= rh;
111 + }
112 + }
113 +
114 + out[0] = zh;
115 + out[1] = zl;
116 +}
117 +
118 +static uint64_t octeon_gfm_reduce64(Int128 product, uint8_t poly)
119 +{
120 + uint64_t lo = int128_getlo(product);
121 + uint64_t hi = int128_gethi(product);
122 +
123 + while (hi) {
124 + int bit = 63 - clz64(hi);
125 +
126 + hi ^= 1ULL << bit;
127 + lo ^= (uint64_t)poly << bit;
128 + if (bit > 56) {
129 + hi ^= (uint64_t)poly >> (64 - bit);
130 + }
131 + }
132 +
133 + return lo;
134 +}
135 +
136 +static void octeon_gfm_mul64_uia2(const uint64_t x[2], const uint64_t y[2],
137 + uint8_t poly, uint64_t out[2])
138 +{
139 + /*
140 + * SNOW3G UIA2 uses the GFM datapath as a reflected 64-bit multiply in
141 + * the low half of the 128-bit register pair. When RESINP[0], MUL[1],
142 + * and the high polynomial byte are all zero, octeon_gfm_mul() observes
143 + * only x[1], y[0], and the low 8-bit polynomial. Reflect those operands
144 + * into normal carryless-multiply order and reflect the reduced result
145 + * back into RESINP[1].
146 + */
147 + uint64_t vx = revbit64(x[1]);
148 + uint64_t vy = revbit64(y[0]);
149 + Int128 product = clmul_64(vx, vy);
150 + uint64_t res = octeon_gfm_reduce64(product, revbit32(poly) >> 24);
151 +
152 + out[0] = 0;
153 + out[1] = revbit64(res);
154 +}
155 +
156 uint64_t helper_octeon_cp2_mf_crc_iv_reflect(CPUMIPSState *env)
157 {
158 return octeon_crc_reflect32_by_byte(env->octeon_crypto.crc_iv);
159 }
160
161 +uint64_t helper_octeon_cp2_mf_gfm_mul_reflect0(CPUMIPSState *env)
162 +{
163 + return revbit64(env->octeon_crypto.gfm_mul[0]);
164 +}
165 +
166 +uint64_t helper_octeon_cp2_mf_gfm_mul_reflect1(CPUMIPSState *env)
167 +{
168 + return revbit64(env->octeon_crypto.gfm_mul[1]);
169 +}
170 +
171 +uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect0(CPUMIPSState *env)
172 +{
173 + return revbit64(env->octeon_crypto.gfm_resinp[0]);
174 +}
175 +
176 +uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect1(CPUMIPSState *env)
177 +{
178 + return revbit64(env->octeon_crypto.gfm_resinp[1]);
179 +}
180 +
181 +void helper_octeon_cp2_mt_gfm_mul_reflect0(CPUMIPSState *env, uint64_t value)
182 +{
183 + env->octeon_crypto.gfm_mul[0] = revbit64(value);
184 +}
185 +
186 +void helper_octeon_cp2_mt_gfm_mul_reflect1(CPUMIPSState *env, uint64_t value)
187 +{
188 + env->octeon_crypto.gfm_mul[1] = revbit64(value);
189 +}
190 +
191 +void helper_octeon_cp2_mt_gfm_xor0_reflect(CPUMIPSState *env, uint64_t value)
192 +{
193 + env->octeon_crypto.gfm_resinp[0] ^= revbit64(value);
194 +}
195 +
196 +static void octeon_gfm_xormul1_common(MIPSOcteonCryptoState *crypto,
197 + uint64_t value)
198 +{
199 + crypto->gfm_resinp[1] ^= value;
200 + if (crypto->gfm_poly <= 0xff && crypto->gfm_mul[1] == 0 &&
201 + crypto->gfm_resinp[0] == 0) {
202 + octeon_gfm_mul64_uia2(crypto->gfm_resinp, crypto->gfm_mul,
203 + crypto->gfm_poly, crypto->gfm_resinp);
204 + } else {
205 + octeon_gfm_mul(crypto->gfm_resinp, crypto->gfm_mul, crypto->gfm_poly,
206 + crypto->gfm_resinp);
207 + }
208 +}
209 +
210 +void helper_octeon_cp2_mt_gfm_xormul1_reflect(CPUMIPSState *env,
211 + uint64_t value)
212 +{
213 + MIPSOcteonCryptoState *crypto = &env->octeon_crypto;
214 +
215 + octeon_gfm_xormul1_common(crypto, revbit64(value));
216 +}
217 +
218 +void helper_octeon_cp2_mt_gfm_xormul1(CPUMIPSState *env, uint64_t value)
219 +{
220 + MIPSOcteonCryptoState *crypto = &env->octeon_crypto;
221 +
222 + octeon_gfm_xormul1_common(crypto, value);
223 +}
224 +
225 void helper_octeon_cp2_mt_crc_write_iv_reflect(CPUMIPSState *env,
226 uint64_t value)
227 {