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