@samitouri / QOSamiQemu / commits / e3c6d52fa0

target/mips: add Octeon CHORD and LLM COP2 helpers

Add the Octeon CHORD hardware register access path and the LLM 36-bit and 64-bit read and write windows. Model both CHORD access forms, including the RDHWR $30 path and the legacy DMFC2 alias. Implement sparse backing storage for the two LLM sets so user-mode code can save, restore, and probe the architectural state without allocating a full hardware-sized backing array. Signed-off-by: James Hilliard <james.hilliard1@gmail.com> Message-ID: <20260608-mips-octeon-missing-insns-v2-v16-13-daef7a0d8b04@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

James Hilliard committed Jun 8, 2026 at 12:59 UTC e3c6d52fa01c5513360ea75e13ae9479b8811e9c
8 files changed +241
target/mips/cpu.c
+67
@@ -26,6 +26,7 @@
26 #include "cpu.h"
27 #include "internal.h"
28 #include "qemu/module.h"
29 +#include "qemu/qtree.h"
30 #include "system/qtest.h"
31 #include "hw/core/qdev-properties.h"
32 #include "hw/core/qdev-clock.h"
@@ -181,6 +182,57 @@ static bool mips_cpu_has_work(CPUState *cs)
182
183 #include "cpu-defs.c.inc"
184
185 +static gint mips_octeon_u64_tree_compare(gconstpointer a, gconstpointer b,
186 + gpointer user_data)
187 +{
188 + uint64_t av = *(const uint64_t *)a;
189 + uint64_t bv = *(const uint64_t *)b;
190 +
191 + return (av > bv) - (av < bv);
192 +}
193 +
194 +QTree *mips_octeon_llm_tree_new(void)
195 +{
196 + return q_tree_new_full(mips_octeon_u64_tree_compare,
197 + NULL, g_free, g_free);
198 +}
199 +
200 +uint64_t mips_octeon_llm_load(QTree *tree, uint64_t addr)
201 +{
202 + uint64_t key = addr;
203 + uint64_t *value = tree ? q_tree_lookup(tree, &key) : NULL;
204 +
205 + return value ? *value : 0;
206 +}
207 +
208 +void mips_octeon_llm_store(QTree **treep, uint64_t addr, uint64_t value)
209 +{
210 + uint64_t *key;
211 + uint64_t *stored;
212 +
213 + if (!*treep) {
214 + *treep = mips_octeon_llm_tree_new();
215 + }
216 +
217 + key = g_new(uint64_t, 1);
218 + stored = g_new(uint64_t, 1);
219 + *key = addr;
220 + *stored = value;
221 + q_tree_replace(*treep, key, stored);
222 +}
223 +
224 +static void mips_octeon_destroy_llm_state(MIPSOcteonCryptoState *crypto)
225 +{
226 + if (crypto->llm36) {
227 + q_tree_destroy(crypto->llm36);
228 + crypto->llm36 = NULL;
229 + }
230 + if (crypto->llm64) {
231 + q_tree_destroy(crypto->llm64);
232 + crypto->llm64 = NULL;
233 + }
234 +}
235 +
236 static void mips_cpu_reset_hold(Object *obj, ResetType type)
237 {
238 CPUState *cs = CPU(obj);
@@ -192,6 +244,7 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
244 mcc->parent_phases.hold(obj, type);
245 }
246
247 + mips_octeon_destroy_llm_state(&env->octeon_crypto);
248 memset(env, 0, offsetof(CPUMIPSState, end_reset_fields));
249
250 /* Reset registers to their default values */
@@ -246,6 +299,9 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
299 env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31;
300 env->msair = env->cpu_model->MSAIR;
301 env->insn_flags = env->cpu_model->insn_flags;
302 + if (env->insn_flags & INSN_OCTEON) {
303 + env->octeon_crypto.chord = 1;
304 + }
305
306 #if defined(CONFIG_USER_ONLY)
307 env->CP0_Status = (MIPS_HFLAG_UM << CP0St_KSU);
@@ -262,6 +318,9 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
318 * hardware registers.
319 */
320 env->CP0_HWREna |= 0x0000000F;
321 + if (env->insn_flags & INSN_OCTEON) {
322 + env->CP0_HWREna |= 0x40000000u;
323 + }
324 if (env->CP0_Config1 & (1 << CP0C1_FP)) {
325 env->CP0_Status |= (1 << CP0St_CU1);
326 }
@@ -417,6 +476,13 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type)
476 #endif
477 }
478
479 +static void mips_cpu_finalize(Object *obj)
480 +{
481 + MIPSCPU *cpu = MIPS_CPU(obj);
482 +
483 + mips_octeon_destroy_llm_state(&cpu->env.octeon_crypto);
484 +}
485 +
486 static void mips_cpu_disas_set_info(const CPUState *cs, disassemble_info *info)
487 {
488 const MIPSCPU *cpu = MIPS_CPU(cs);
@@ -645,6 +711,7 @@ static const TypeInfo mips_cpu_type_info = {
711 .instance_size = sizeof(MIPSCPU),
712 .instance_align = __alignof(MIPSCPU),
713 .instance_init = mips_cpu_initfn,
714 + .instance_finalize = mips_cpu_finalize,
715 .abstract = true,
716 .class_size = sizeof(MIPSCPUClass),
717 .class_init = mips_cpu_class_init,
target/mips/cpu.h
+5
@@ -11,6 +11,7 @@
11 #include "fpu/softfloat-types.h"
12 #include "hw/core/clock.h"
13 #include "mips-defs.h"
14 +#include "qemu/qtree.h"
15
16 typedef struct CPUMIPSTLBContext CPUMIPSTLBContext;
17
@@ -559,6 +560,10 @@ typedef struct MIPSOcteonCryptoState {
560 uint16_t gfm_poly;
561 uint8_t aes_keylen;
562 uint8_t crc_len;
563 + uint64_t chord;
564 + uint64_t llm_data[2];
565 + QTree *llm36;
566 + QTree *llm64;
567 } MIPSOcteonCryptoState;
568
569 typedef struct CPUArchState {
target/mips/helper.h
+9
@@ -74,6 +74,14 @@ DEF_HELPER_2(octeon_cp2_mt_hsh_startmd5, void, env, i64)
74 DEF_HELPER_2(octeon_cp2_mt_hsh_startsha256, void, env, i64)
75 DEF_HELPER_2(octeon_cp2_mt_hsh_startsha, void, env, i64)
76 DEF_HELPER_2(octeon_cp2_mt_hsh_startsha512, void, env, i64)
77 +DEF_HELPER_2(octeon_cp2_mt_llm_read_addr0, void, env, i64)
78 +DEF_HELPER_2(octeon_cp2_mt_llm_write_addr0, void, env, i64)
79 +DEF_HELPER_2(octeon_cp2_mt_llm_read64_addr0, void, env, i64)
80 +DEF_HELPER_2(octeon_cp2_mt_llm_write64_addr0, void, env, i64)
81 +DEF_HELPER_2(octeon_cp2_mt_llm_read_addr1, void, env, i64)
82 +DEF_HELPER_2(octeon_cp2_mt_llm_write_addr1, void, env, i64)
83 +DEF_HELPER_2(octeon_cp2_mt_llm_read64_addr1, void, env, i64)
84 +DEF_HELPER_2(octeon_cp2_mt_llm_write64_addr1, void, env, i64)
85
86 /* microMIPS functions */
87 DEF_HELPER_4(lwm, void, env, tl, tl, i32)
@@ -245,6 +253,7 @@ DEF_HELPER_1(rdhwr_cc, tl, env)
253 DEF_HELPER_1(rdhwr_ccres, tl, env)
254 DEF_HELPER_1(rdhwr_performance, tl, env)
255 DEF_HELPER_1(rdhwr_xnp, tl, env)
256 +DEF_HELPER_1(rdhwr_chord, tl, env)
257 DEF_HELPER_2(pmon, void, env, int)
258 DEF_HELPER_1(wait, void, env)
259
target/mips/internal.h
+3
@@ -93,6 +93,9 @@ extern const int mips_defs_number;
93
94 int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
95 int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
96 +QTree *mips_octeon_llm_tree_new(void);
97 +uint64_t mips_octeon_llm_load(QTree *tree, uint64_t addr);
98 +void mips_octeon_llm_store(QTree **treep, uint64_t addr, uint64_t value);
99
100 #define USEG_LIMIT ((target_ulong)(int32_t)0x7FFFFFFFUL)
101 #define KSEG0_BASE ((target_ulong)(int32_t)0x80000000UL)
target/mips/system/machine.c
+67
@@ -131,6 +131,69 @@ static const VMStateDescription vmstate_octeon_multiplier_tc = {
131 }
132 };
133
134 +typedef struct OcteonLLMTreePutData {
135 + QEMUFile *f;
136 +} OcteonLLMTreePutData;
137 +
138 +static gboolean put_octeon_llm_tree_entry(gpointer key, gpointer value,
139 + gpointer user_data)
140 +{
141 + OcteonLLMTreePutData *data = user_data;
142 +
143 + qemu_put_be64(data->f, *(uint64_t *)key);
144 + qemu_put_be64(data->f, *(uint64_t *)value);
145 + return false;
146 +}
147 +
148 +static int put_octeon_llm_tree(QEMUFile *f, void *pv, size_t size,
149 + const VMStateField *field, JSONWriter *vmdesc)
150 +{
151 + QTree *tree = *(QTree **)pv;
152 + OcteonLLMTreePutData data = { .f = f };
153 + uint32_t nnodes = tree ? q_tree_nnodes(tree) : 0;
154 +
155 + qemu_put_be32(f, nnodes);
156 + if (tree) {
157 + q_tree_foreach(tree, put_octeon_llm_tree_entry, &data);
158 + }
159 +
160 + return 0;
161 +}
162 +
163 +static int get_octeon_llm_tree(QEMUFile *f, void *pv, size_t size,
164 + const VMStateField *field)
165 +{
166 + QTree **treep = pv;
167 + uint32_t nnodes = qemu_get_be32(f);
168 +
169 + if (*treep) {
170 + q_tree_destroy(*treep);
171 + }
172 + *treep = mips_octeon_llm_tree_new();
173 +
174 + for (uint32_t i = 0; i < nnodes; i++) {
175 + uint64_t addr = qemu_get_be64(f);
176 + uint64_t value = qemu_get_be64(f);
177 +
178 + mips_octeon_llm_store(treep, addr, value);
179 + }
180 +
181 + return 0;
182 +}
183 +
184 +static const VMStateInfo vmstate_info_octeon_llm_tree = {
185 + .name = "octeon_llm_tree",
186 + .get = get_octeon_llm_tree,
187 + .put = put_octeon_llm_tree,
188 +};
189 +
190 +#define VMSTATE_OCTEON_LLM_TREE(_f, _s) { \
191 + .name = stringify(_f), \
192 + .version_id = 1, \
193 + .info = &vmstate_info_octeon_llm_tree, \
194 + .offset = vmstate_offset_pointer(_s, _f, QTree), \
195 +}
196 +
197 /* MVP state */
198
199 static const VMStateDescription vmstate_mvp = {
@@ -301,6 +364,10 @@ static const VMStateDescription mips_vmstate_octeon_crypto = {
364 VMSTATE_UINT16(env.octeon_crypto.gfm_poly, MIPSCPU),
365 VMSTATE_UINT8(env.octeon_crypto.aes_keylen, MIPSCPU),
366 VMSTATE_UINT8(env.octeon_crypto.crc_len, MIPSCPU),
367 + VMSTATE_UINT64(env.octeon_crypto.chord, MIPSCPU),
368 + VMSTATE_UINT64_ARRAY(env.octeon_crypto.llm_data, MIPSCPU, 2),
369 + VMSTATE_OCTEON_LLM_TREE(env.octeon_crypto.llm36, MIPSCPU),
370 + VMSTATE_OCTEON_LLM_TREE(env.octeon_crypto.llm64, MIPSCPU),
371 VMSTATE_END_OF_LIST()
372 }
373 };
target/mips/tcg/octeon_crypto.c
+76
@@ -16,6 +16,42 @@
16 #include "qemu/bitops.h"
17 #include "qemu/host-utils.h"
18
19 +#define OCTEON_LLM_NARROW_MASK ((1ULL << 36) - 1)
20 +
21 +static uint64_t octeon_llm_pack_narrow(uint64_t value)
22 +{
23 + value &= OCTEON_LLM_NARROW_MASK;
24 + return value | ((uint64_t)(ctpop64(value) & 1) << 36);
25 +}
26 +
27 +static void octeon_llm_read(MIPSOcteonCryptoState *crypto, unsigned int set,
28 + uint64_t addr, bool wide)
29 +{
30 + uint64_t value;
31 +
32 + if (wide) {
33 + value = mips_octeon_llm_load(crypto->llm64, addr);
34 + } else {
35 + value = octeon_llm_pack_narrow(
36 + mips_octeon_llm_load(crypto->llm36, addr));
37 + }
38 +
39 + crypto->llm_data[set] = value;
40 +}
41 +
42 +static void octeon_llm_write(MIPSOcteonCryptoState *crypto, unsigned int set,
43 + uint64_t addr, bool wide)
44 +{
45 + uint64_t value = crypto->llm_data[set];
46 +
47 + if (wide) {
48 + mips_octeon_llm_store(&crypto->llm64, addr, value);
49 + } else {
50 + mips_octeon_llm_store(&crypto->llm36, addr,
51 + value & OCTEON_LLM_NARROW_MASK);
52 + }
53 +}
54 +
55 static uint32_t octeon_crc_reflect32_by_byte(uint32_t v)
56 {
57 return bswap32(revbit32(v));
@@ -2225,3 +2261,43 @@ void helper_octeon_cp2_mt_crc_write_var_reflect(CPUMIPSState *env,
2261
2262 octeon_crc_update_reflect(crypto, value, MIN(8U, crypto->crc_len & 0xf));
2263 }
2264 +
2265 +void helper_octeon_cp2_mt_llm_read_addr0(CPUMIPSState *env, uint64_t value)
2266 +{
2267 + octeon_llm_read(&env->octeon_crypto, 0, value, false);
2268 +}
2269 +
2270 +void helper_octeon_cp2_mt_llm_write_addr0(CPUMIPSState *env, uint64_t value)
2271 +{
2272 + octeon_llm_write(&env->octeon_crypto, 0, value, false);
2273 +}
2274 +
2275 +void helper_octeon_cp2_mt_llm_read64_addr0(CPUMIPSState *env, uint64_t value)
2276 +{
2277 + octeon_llm_read(&env->octeon_crypto, 0, value, true);
2278 +}
2279 +
2280 +void helper_octeon_cp2_mt_llm_write64_addr0(CPUMIPSState *env, uint64_t value)
2281 +{
2282 + octeon_llm_write(&env->octeon_crypto, 0, value, true);
2283 +}
2284 +
2285 +void helper_octeon_cp2_mt_llm_read_addr1(CPUMIPSState *env, uint64_t value)
2286 +{
2287 + octeon_llm_read(&env->octeon_crypto, 1, value, false);
2288 +}
2289 +
2290 +void helper_octeon_cp2_mt_llm_write_addr1(CPUMIPSState *env, uint64_t value)
2291 +{
2292 + octeon_llm_write(&env->octeon_crypto, 1, value, false);
2293 +}
2294 +
2295 +void helper_octeon_cp2_mt_llm_read64_addr1(CPUMIPSState *env, uint64_t value)
2296 +{
2297 + octeon_llm_read(&env->octeon_crypto, 1, value, true);
2298 +}
2299 +
2300 +void helper_octeon_cp2_mt_llm_write64_addr1(CPUMIPSState *env, uint64_t value)
2301 +{
2302 + octeon_llm_write(&env->octeon_crypto, 1, value, true);
2303 +}
target/mips/tcg/op_helper.c
+6
@@ -255,6 +255,12 @@ target_ulong helper_rdhwr_xnp(CPUMIPSState *env)
255 return (env->CP0_Config5 >> CP0C5_XNP) & 1;
256 }
257
258 +target_ulong helper_rdhwr_chord(CPUMIPSState *env)
259 +{
260 + check_hwrena(env, 30, GETPC());
261 + return env->octeon_crypto.chord;
262 +}
263 +
264 void helper_pmon(CPUMIPSState *env, int function)
265 {
266 function /= 2;
target/mips/tcg/translate.c
+8
@@ -10925,6 +10925,14 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel)
10925 }
10926 break;
10927 #endif
10928 + case 30:
10929 + if (!(ctx->insn_flags & INSN_OCTEON)) {
10930 + gen_reserved_instruction(ctx);
10931 + break;
10932 + }
10933 + gen_helper_rdhwr_chord(t0, tcg_env);
10934 + gen_store_gpr(t0, rt);
10935 + break;
10936 default: /* Invalid */
10937 MIPS_INVAL("rdhwr");
10938 gen_reserved_instruction(ctx);