target/hexagon: Update TARGET_PAGE_BITS, stubs for modify_ssr/get_exe_mode
Add hex_mmu.[ch], cpu mode helpers, and additional includes/stubs that integrate the TLB device with the CPU model. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Brian Cain committed
Jun 22, 2026 at 15:28 UTC
73e26eee0822f53f7d38e85391b23dfe2f479fc8
7 files changed
+366
-1
target/hexagon/cpu-param.h
+1
-1
@@ -18,7 +18,7 @@
18
#ifndef HEXAGON_CPU_PARAM_H
19
#define HEXAGON_CPU_PARAM_H
20
21
-#define TARGET_PAGE_BITS 16 /* 64K pages */
21
+#define TARGET_PAGE_BITS 12 /* 4K pages */
22
23
#define TARGET_VIRT_ADDR_SPACE_BITS 32
24
target/hexagon/cpu.c
+36
@@ -23,9 +23,17 @@
23
#include "qapi/error.h"
24
#include "hw/core/qdev-properties.h"
25
#include "fpu/softfloat-helpers.h"
26
+#include "hw/hexagon/hexagon_tlb.h"
27
#include "tcg/tcg.h"
28
#include "exec/gdbstub.h"
29
#include "accel/tcg/cpu-ops.h"
30
+#include "cpu_helper.h"
31
+#include "hex_mmu.h"
32
+
33
+#ifndef CONFIG_USER_ONLY
34
+#include "sys_macros.h"
35
+#include "accel/tcg/cpu-ldst.h"
36
+#endif
37
38
static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
39
{
@@ -43,6 +51,11 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
51
}
52
53
static const Property hexagon_cpu_properties[] = {
54
+#ifndef CONFIG_USER_ONLY
55
+ DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
56
+ HexagonTLBState *),
57
+ DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0),
58
+#endif
59
DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false),
60
DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0,
61
qdev_prop_uint32, target_ulong),
@@ -269,7 +282,11 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs)
282
}
283
284
#ifndef CONFIG_USER_ONLY
285
+ hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX,
286
+ cpu_mmu_index(env_cpu(env), false));
287
hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, 1);
288
+#else
289
+ hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX);
290
#endif
291
292
return (TCGTBCPUState){ .pc = pc, .flags = hex_flags };
@@ -289,11 +306,15 @@ static void hexagon_restore_state_to_opc(CPUState *cs,
306
cpu_env(cs)->gpr[HEX_REG_PC] = data[0];
307
}
308
309
+
310
static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
311
{
312
CPUState *cs = CPU(obj);
313
HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj);
314
CPUHexagonState *env = cpu_env(cs);
315
+#ifndef CONFIG_USER_ONLY
316
+ HexagonCPU *cpu = HEXAGON_CPU(cs);
317
+#endif
318
319
if (mcc->parent_phases.hold) {
320
mcc->parent_phases.hold(obj, type);
@@ -307,7 +328,14 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
328
memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS);
329
memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS);
330
env->wait_next_pc = 0;
331
+ env->tlb_lock_state = HEX_LOCK_UNLOCKED;
332
+ env->k0_lock_state = HEX_LOCK_UNLOCKED;
333
+ env->tlb_lock_count = 0;
334
+ env->k0_lock_count = 0;
335
env->next_PC = 0;
336
+
337
+ env->t_sreg[HEX_SREG_HTID] = cpu->htid;
338
+ env->threadId = cpu->htid;
339
#endif
340
env->cause_code = HEX_EVENT_NONE;
341
}
@@ -337,7 +365,15 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
365
hexagon_hvx_gdb_write_register,
366
gdb_find_static_feature("hexagon-hvx.xml"));
367
368
+#ifndef CONFIG_USER_ONLY
369
+ if (!HEXAGON_CPU(dev)->tlb) {
370
+ error_setg(errp, "hexagon cpu requires 'tlb' link property to be set");
371
+ return;
372
+ }
373
+#endif
374
+
375
qemu_init_vcpu(cs);
376
+
377
cpu_reset(cs);
378
mcc->parent_realize(dev, errp);
379
}
target/hexagon/cpu.h
+21
@@ -27,6 +27,9 @@
27
#define SREG_WRITES_MAX 2
28
#endif
29
30
+typedef struct HexagonTLBState HexagonTLBState;
31
+typedef struct HexagonGlobalRegState HexagonGlobalRegState;
32
+
33
#include "cpu-qom.h"
34
#include "exec/cpu-common.h"
35
#include "exec/target_long.h"
@@ -39,6 +42,7 @@
42
#error "Hexagon does not support system emulation"
43
#endif
44
45
+
46
#define NUM_PREGS 4
47
#define TOTAL_PER_THREAD_REGS 64
48
@@ -47,10 +51,13 @@
51
#define REG_WRITES_MAX 32
52
#define PRED_WRITES_MAX 5 /* 4 insns + endloop */
53
#define VSTORES_MAX 2
54
+#define MAX_TLB_ENTRIES 1024
55
56
#define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU
57
#ifndef CONFIG_USER_ONLY
58
#define CPU_INTERRUPT_SWI CPU_INTERRUPT_TGT_INT_0
59
+#define CPU_INTERRUPT_K0_UNLOCK CPU_INTERRUPT_TGT_INT_1
60
+#define CPU_INTERRUPT_TLB_UNLOCK CPU_INTERRUPT_TGT_INT_2
61
62
#define HEX_CPU_MODE_USER 1
63
#define HEX_CPU_MODE_GUEST 2
@@ -67,6 +74,12 @@
74
#define MMU_GUEST_IDX 1
75
#define MMU_KERNEL_IDX 2
76
77
+typedef enum {
78
+ HEX_LOCK_UNLOCKED = 0,
79
+ HEX_LOCK_WAITING = 1,
80
+ HEX_LOCK_OWNER = 2,
81
+ HEX_LOCK_QUEUED = 3
82
+} hex_lock_state_t;
83
#endif
84
85
@@ -128,6 +141,10 @@ typedef struct CPUArchState {
141
142
/* This alias of CPUState.cpu_index is used by imported sources: */
143
uint32_t threadId;
144
+ hex_lock_state_t tlb_lock_state;
145
+ hex_lock_state_t k0_lock_state;
146
+ uint32_t tlb_lock_count;
147
+ uint32_t k0_lock_count;
148
uint64_t t_cycle_count;
149
#endif
150
uint32_t next_PC;
@@ -178,6 +195,10 @@ struct ArchCPU {
195
bool lldb_compat;
196
target_ulong lldb_stack_adjust;
197
bool short_circuit;
198
+#ifndef CONFIG_USER_ONLY
199
+ HexagonTLBState *tlb;
200
+ uint32_t htid;
201
+#endif
202
};
203
204
#include "cpu_bits.h"
target/hexagon/hex_mmu.c
new
+270
@@ -0,0 +1,270 @@
1
+/*
2
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3
+ *
4
+ * SPDX-License-Identifier: GPL-2.0-or-later
5
+ */
6
+
7
+#include "qemu/osdep.h"
8
+#include "qemu/log.h"
9
+#include "qemu/main-loop.h"
10
+#include "qemu/qemu-print.h"
11
+#include "cpu.h"
12
+#include "system/cpus.h"
13
+#include "internal.h"
14
+#include "exec/cpu-interrupt.h"
15
+#include "cpu_helper.h"
16
+#include "exec/cputlb.h"
17
+#include "hex_mmu.h"
18
+#include "macros.h"
19
+#include "sys_macros.h"
20
+#include "hw/hexagon/hexagon_tlb.h"
21
+#include "hw/hexagon/hexagon_globalreg.h"
22
+
23
+static inline void hex_log_tlbw(uint32_t index, uint64_t entry)
24
+{
25
+ qemu_log_mask(CPU_LOG_MMU,
26
+ "tlbw[%03" PRIu32 "]: 0x%016" PRIx64 "\n",
27
+ index, entry);
28
+}
29
+
30
+void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value)
31
+{
32
+ uint32_t myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(index));
33
+ HexagonTLBState *tlb = env_archcpu(env)->tlb;
34
+ uint64_t old_entry = hexagon_tlb_read(tlb, myidx);
35
+
36
+ bool old_entry_valid = extract64(old_entry, 63, 1);
37
+ if (old_entry_valid && hexagon_cpu_mmu_enabled(env)) {
38
+ CPUState *cs = env_cpu(env);
39
+ tlb_flush(cs);
40
+ }
41
+ hexagon_tlb_write(tlb, myidx, value);
42
+ hex_log_tlbw(myidx, value);
43
+}
44
+
45
+void hex_mmu_on(CPUHexagonState *env)
46
+{
47
+ CPUState *cs = env_cpu(env);
48
+ qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned on!\n");
49
+ tlb_flush(cs);
50
+}
51
+
52
+void hex_mmu_off(CPUHexagonState *env)
53
+{
54
+ CPUState *cs = env_cpu(env);
55
+ qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned off!\n");
56
+ tlb_flush(cs);
57
+}
58
+
59
+void hex_mmu_mode_change(CPUHexagonState *env)
60
+{
61
+ qemu_log_mask(CPU_LOG_MMU, "Hexagon mode change!\n");
62
+ CPUState *cs = env_cpu(env);
63
+ tlb_flush(cs);
64
+}
65
+
66
+bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA,
67
+ MMUAccessType access_type, hwaddr *PA, int *prot,
68
+ uint64_t *size, int32_t *excp, int mmu_idx)
69
+{
70
+ HexagonCPU *cpu = env_archcpu(env);
71
+ uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
72
+ uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr);
73
+ int cause_code = 0;
74
+
75
+ bool found = hexagon_tlb_find_match(cpu->tlb, asid, VA, access_type,
76
+ PA, prot, size, excp, &cause_code,
77
+ mmu_idx);
78
+ if (cause_code) {
79
+ env->cause_code = cause_code;
80
+ }
81
+ return found;
82
+}
83
+
84
+/* Called from tlbp instruction */
85
+uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA)
86
+{
87
+ HexagonCPU *cpu = env_archcpu(env);
88
+ uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr);
89
+ int cause_code = 0;
90
+
91
+ uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA, &cause_code);
92
+ if (cause_code) {
93
+ env->cause_code = cause_code;
94
+ }
95
+ return result;
96
+}
97
+
98
+/*
99
+ * Return codes:
100
+ * 0 or positive index of match
101
+ * -1 multiple matches
102
+ * -2 no match
103
+ */
104
+int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, uint64_t index)
105
+{
106
+ HexagonCPU *cpu = env_archcpu(env);
107
+ return hexagon_tlb_check_overlap(cpu->tlb, entry, index);
108
+}
109
+
110
+void dump_mmu(Monitor *mon, CPUHexagonState *env)
111
+{
112
+ HexagonCPU *cpu = env_archcpu(env);
113
+ hexagon_tlb_dump(mon, cpu->tlb);
114
+}
115
+
116
+static inline void print_thread(const char *str, CPUState *cs)
117
+{
118
+ g_assert(bql_locked());
119
+ CPUHexagonState *thread = cpu_env(cs);
120
+ bool is_stopped = cpu_is_stopped(cs);
121
+ int exe_mode = get_exe_mode(thread);
122
+ hex_lock_state_t lock_state = thread->tlb_lock_state;
123
+ qemu_log_mask(CPU_LOG_MMU,
124
+ "%s: threadId = %" PRIu32 ": %s,"
125
+ " exe_mode = %s, tlb_lock_state = %s\n",
126
+ str,
127
+ thread->threadId,
128
+ is_stopped ? "stopped" : "running",
129
+ exe_mode == HEX_EXE_MODE_OFF ? "off" :
130
+ exe_mode == HEX_EXE_MODE_RUN ? "run" :
131
+ exe_mode == HEX_EXE_MODE_WAIT ? "wait" :
132
+ exe_mode == HEX_EXE_MODE_DEBUG ? "debug" :
133
+ "unknown",
134
+ lock_state == HEX_LOCK_UNLOCKED ? "unlocked" :
135
+ lock_state == HEX_LOCK_WAITING ? "waiting" :
136
+ lock_state == HEX_LOCK_OWNER ? "owner" :
137
+ "unknown");
138
+}
139
+
140
+static inline void print_thread_states(const char *str)
141
+{
142
+ CPUState *cs;
143
+ CPU_FOREACH(cs) {
144
+ print_thread(str, cs);
145
+ }
146
+}
147
+
148
+void hex_tlb_lock(CPUHexagonState *env)
149
+{
150
+ qemu_log_mask(CPU_LOG_MMU, "hex_tlb_lock: " TARGET_FMT_ld "\n",
151
+ env->threadId);
152
+ BQL_LOCK_GUARD();
153
+ g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1));
154
+
155
+ HexagonCPU *cpu = env_archcpu(env);
156
+ uint32_t syscfg = cpu->globalregs ?
157
+ hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
158
+ env->threadId) : 0;
159
+ uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg);
160
+ if (tlb_lock) {
161
+ if (env->tlb_lock_state == HEX_LOCK_QUEUED) {
162
+ env->next_PC += 4;
163
+ env->tlb_lock_count++;
164
+ env->tlb_lock_state = HEX_LOCK_OWNER;
165
+ SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1);
166
+ return;
167
+ }
168
+ if (env->tlb_lock_state == HEX_LOCK_OWNER) {
169
+ qemu_log_mask(CPU_LOG_MMU | LOG_GUEST_ERROR,
170
+ "Double tlblock at PC: 0x%" PRIx32
171
+ ", thread may hang\n",
172
+ env->next_PC);
173
+ env->next_PC += 4;
174
+ CPUState *cs = env_cpu(env);
175
+ cpu_interrupt(cs, CPU_INTERRUPT_HALT);
176
+ return;
177
+ }
178
+ env->tlb_lock_state = HEX_LOCK_WAITING;
179
+ CPUState *cs = env_cpu(env);
180
+ cpu_interrupt(cs, CPU_INTERRUPT_HALT);
181
+ } else {
182
+ env->next_PC += 4;
183
+ env->tlb_lock_count++;
184
+ env->tlb_lock_state = HEX_LOCK_OWNER;
185
+ SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1);
186
+ }
187
+
188
+ if (qemu_loglevel_mask(CPU_LOG_MMU)) {
189
+ qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_lock:\n");
190
+ print_thread_states("\tThread");
191
+ }
192
+}
193
+
194
+void hex_tlb_unlock(CPUHexagonState *env)
195
+{
196
+ BQL_LOCK_GUARD();
197
+ g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1));
198
+
199
+ /* Nothing to do if the TLB isn't locked by this thread */
200
+ HexagonCPU *cpu = env_archcpu(env);
201
+ uint32_t syscfg = cpu->globalregs ?
202
+ hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
203
+ env->threadId) : 0;
204
+ uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg);
205
+ if ((tlb_lock == 0) ||
206
+ (env->tlb_lock_state != HEX_LOCK_OWNER)) {
207
+ qemu_log_mask(LOG_GUEST_ERROR,
208
+ "thread %" PRIu32 " attempted to tlbunlock"
209
+ " without having the lock, tlb_lock state = %d\n",
210
+ env->threadId, env->tlb_lock_state);
211
+ g_assert(env->tlb_lock_state != HEX_LOCK_WAITING);
212
+ return;
213
+ }
214
+
215
+ env->tlb_lock_count--;
216
+ env->tlb_lock_state = HEX_LOCK_UNLOCKED;
217
+ SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 0);
218
+
219
+ /* Look for a thread to unlock */
220
+ unsigned int this_threadId = env->threadId;
221
+ CPUHexagonState *unlock_thread = NULL;
222
+ CPUState *cs;
223
+ CPU_FOREACH(cs) {
224
+ CPUHexagonState *thread = cpu_env(cs);
225
+
226
+ /*
227
+ * The hardware implements round-robin fairness, so we look for threads
228
+ * starting at env->threadId + 1 and incrementing modulo the number of
229
+ * threads.
230
+ *
231
+ * To implement this, we check if thread is a earlier in the modulo
232
+ * sequence than unlock_thread.
233
+ * if unlock thread is higher than this thread
234
+ * thread must be between this thread and unlock_thread
235
+ * else
236
+ * thread higher than this thread is ahead of unlock_thread
237
+ * thread must be lower then unlock thread
238
+ */
239
+ if (thread->tlb_lock_state == HEX_LOCK_WAITING) {
240
+ if (!unlock_thread) {
241
+ unlock_thread = thread;
242
+ } else if (unlock_thread->threadId > this_threadId) {
243
+ if (this_threadId < thread->threadId &&
244
+ thread->threadId < unlock_thread->threadId) {
245
+ unlock_thread = thread;
246
+ }
247
+ } else {
248
+ if (thread->threadId > this_threadId) {
249
+ unlock_thread = thread;
250
+ }
251
+ if (thread->threadId < unlock_thread->threadId) {
252
+ unlock_thread = thread;
253
+ }
254
+ }
255
+ }
256
+ }
257
+ if (unlock_thread) {
258
+ cs = env_cpu(unlock_thread);
259
+ print_thread("\tWaiting thread found", cs);
260
+ unlock_thread->tlb_lock_state = HEX_LOCK_QUEUED;
261
+ SET_SYSCFG_FIELD(unlock_thread, SYSCFG_TLBLOCK, 1);
262
+ cpu_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK);
263
+ }
264
+
265
+ if (qemu_loglevel_mask(CPU_LOG_MMU)) {
266
+ qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_unlock:\n");
267
+ print_thread_states("\tThread");
268
+ }
269
+
270
+}
target/hexagon/hex_mmu.h
new
+26
@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3
+ *
4
+ * SPDX-License-Identifier: GPL-2.0-or-later
5
+ */
6
+
7
+#ifndef HEXAGON_MMU_H
8
+#define HEXAGON_MMU_H
9
+
10
+#include "cpu.h"
11
+#include "monitor/monitor.h"
12
+
13
+extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value);
14
+extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA);
15
+extern void hex_mmu_on(CPUHexagonState *env);
16
+extern void hex_mmu_off(CPUHexagonState *env);
17
+extern void hex_mmu_mode_change(CPUHexagonState *env);
18
+extern bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA,
19
+ MMUAccessType access_type, hwaddr *PA, int *prot,
20
+ uint64_t *size, int32_t *excp, int mmu_idx);
21
+extern int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry,
22
+ uint64_t index);
23
+extern void hex_tlb_lock(CPUHexagonState *env);
24
+extern void hex_tlb_unlock(CPUHexagonState *env);
25
+void dump_mmu(Monitor *mon, CPUHexagonState *env);
26
+#endif
target/hexagon/internal.h
+9
@@ -36,6 +36,15 @@ void G_NORETURN do_raise_exception(CPUHexagonState *env,
36
uint32_t PC,
37
uintptr_t retaddr);
38
39
+#define hexagon_cpu_mmu_enabled(env) ({ \
40
+ HexagonCPU *cpu = env_archcpu(env); \
41
+ cpu->globalregs ? \
42
+ GET_SYSCFG_FIELD(SYSCFG_MMUEN, \
43
+ hexagon_globalreg_read(cpu->globalregs, \
44
+ HEX_SREG_SYSCFG, (env)->threadId)) : \
45
+ 0; \
46
+})
47
+
48
#ifndef CONFIG_USER_ONLY
49
extern const VMStateDescription vmstate_hexagon_cpu;
50
#endif
target/hexagon/sys_macros.h
+3
@@ -141,6 +141,9 @@
141
#define fDCINVIDX(REG)
142
#define fDCINVA(REG) do { REG = REG; } while (0) /* Nothing to do in qemu */
143
144
+#define fSET_TLB_LOCK() hex_tlb_lock(env);
145
+#define fCLEAR_TLB_LOCK() hex_tlb_unlock(env);
146
+
147
#define fTLB_IDXMASK(INDEX) \
148
((INDEX) & (fPOW2_ROUNDUP( \
149
fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1))