@samitouri / QOSamiQemu / commits / 73e26eee08

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