]>
Commit | Line | Data |
---|---|---|
45183ccd TS |
1 | /* |
2 | * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include "qemu/osdep.h" | |
19 | #include "qemu/log.h" | |
20 | #include "qemu/qemu-print.h" | |
21 | #include "cpu.h" | |
22 | #include "internal.h" | |
23 | #include "exec/exec-all.h" | |
24 | #include "qapi/error.h" | |
25 | #include "hw/qdev-properties.h" | |
26 | ||
27 | static void hexagon_v67_cpu_init(Object *obj) | |
28 | { | |
29 | } | |
30 | ||
31 | static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) | |
32 | { | |
33 | ObjectClass *oc; | |
34 | char *typename; | |
35 | char **cpuname; | |
36 | ||
37 | cpuname = g_strsplit(cpu_model, ",", 1); | |
38 | typename = g_strdup_printf(HEXAGON_CPU_TYPE_NAME("%s"), cpuname[0]); | |
39 | oc = object_class_by_name(typename); | |
40 | g_strfreev(cpuname); | |
41 | g_free(typename); | |
42 | if (!oc || !object_class_dynamic_cast(oc, TYPE_HEXAGON_CPU) || | |
43 | object_class_is_abstract(oc)) { | |
44 | return NULL; | |
45 | } | |
46 | return oc; | |
47 | } | |
48 | ||
49 | static Property hexagon_lldb_compat_property = | |
50 | DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false); | |
51 | static Property hexagon_lldb_stack_adjust_property = | |
52 | DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, | |
53 | 0, qdev_prop_uint32, target_ulong); | |
54 | ||
55 | const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { | |
56 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
57 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | |
58 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | |
59 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | |
60 | "sa0", "lc0", "sa1", "lc1", "p3_0", "c5", "m0", "m1", | |
61 | "usr", "pc", "ugp", "gp", "cs0", "cs1", "c14", "c15", | |
62 | "c16", "c17", "c18", "c19", "pkt_cnt", "insn_cnt", "c22", "c23", | |
63 | "c24", "c25", "c26", "c27", "c28", "c29", "c30", "c31", | |
64 | }; | |
65 | ||
66 | /* | |
67 | * One of the main debugging techniques is to use "-d cpu" and compare against | |
68 | * LLDB output when single stepping. However, the target and qemu put the | |
69 | * stacks at different locations. This is used to compensate so the diff is | |
70 | * cleaner. | |
71 | */ | |
72 | static inline target_ulong adjust_stack_ptrs(CPUHexagonState *env, | |
73 | target_ulong addr) | |
74 | { | |
75 | HexagonCPU *cpu = container_of(env, HexagonCPU, env); | |
76 | target_ulong stack_adjust = cpu->lldb_stack_adjust; | |
77 | target_ulong stack_start = env->stack_start; | |
78 | target_ulong stack_size = 0x10000; | |
79 | ||
80 | if (stack_adjust == 0) { | |
81 | return addr; | |
82 | } | |
83 | ||
84 | if (stack_start + 0x1000 >= addr && addr >= (stack_start - stack_size)) { | |
85 | return addr - stack_adjust; | |
86 | } | |
87 | return addr; | |
88 | } | |
89 | ||
90 | /* HEX_REG_P3_0 (aka C4) is an alias for the predicate registers */ | |
91 | static inline target_ulong read_p3_0(CPUHexagonState *env) | |
92 | { | |
93 | int32_t control_reg = 0; | |
94 | int i; | |
95 | for (i = NUM_PREGS - 1; i >= 0; i--) { | |
96 | control_reg <<= 8; | |
97 | control_reg |= env->pred[i] & 0xff; | |
98 | } | |
99 | return control_reg; | |
100 | } | |
101 | ||
102 | static void print_reg(FILE *f, CPUHexagonState *env, int regnum) | |
103 | { | |
104 | target_ulong value; | |
105 | ||
106 | if (regnum == HEX_REG_P3_0) { | |
107 | value = read_p3_0(env); | |
108 | } else { | |
109 | value = regnum < 32 ? adjust_stack_ptrs(env, env->gpr[regnum]) | |
110 | : env->gpr[regnum]; | |
111 | } | |
112 | ||
113 | qemu_fprintf(f, " %s = 0x" TARGET_FMT_lx "\n", | |
114 | hexagon_regnames[regnum], value); | |
115 | } | |
116 | ||
117 | static void hexagon_dump(CPUHexagonState *env, FILE *f) | |
118 | { | |
119 | HexagonCPU *cpu = container_of(env, HexagonCPU, env); | |
120 | ||
121 | if (cpu->lldb_compat) { | |
122 | /* | |
123 | * When comparing with LLDB, it doesn't step through single-cycle | |
124 | * hardware loops the same way. So, we just skip them here | |
125 | */ | |
126 | if (env->gpr[HEX_REG_PC] == env->last_pc_dumped) { | |
127 | return; | |
128 | } | |
129 | env->last_pc_dumped = env->gpr[HEX_REG_PC]; | |
130 | } | |
131 | ||
132 | qemu_fprintf(f, "General Purpose Registers = {\n"); | |
133 | for (int i = 0; i < 32; i++) { | |
134 | print_reg(f, env, i); | |
135 | } | |
136 | print_reg(f, env, HEX_REG_SA0); | |
137 | print_reg(f, env, HEX_REG_LC0); | |
138 | print_reg(f, env, HEX_REG_SA1); | |
139 | print_reg(f, env, HEX_REG_LC1); | |
140 | print_reg(f, env, HEX_REG_M0); | |
141 | print_reg(f, env, HEX_REG_M1); | |
142 | print_reg(f, env, HEX_REG_USR); | |
143 | print_reg(f, env, HEX_REG_P3_0); | |
144 | print_reg(f, env, HEX_REG_GP); | |
145 | print_reg(f, env, HEX_REG_UGP); | |
146 | print_reg(f, env, HEX_REG_PC); | |
147 | #ifdef CONFIG_USER_ONLY | |
148 | /* | |
149 | * Not modelled in user mode, print junk to minimize the diff's | |
150 | * with LLDB output | |
151 | */ | |
152 | qemu_fprintf(f, " cause = 0x000000db\n"); | |
153 | qemu_fprintf(f, " badva = 0x00000000\n"); | |
154 | qemu_fprintf(f, " cs0 = 0x00000000\n"); | |
155 | qemu_fprintf(f, " cs1 = 0x00000000\n"); | |
156 | #else | |
157 | print_reg(f, env, HEX_REG_CAUSE); | |
158 | print_reg(f, env, HEX_REG_BADVA); | |
159 | print_reg(f, env, HEX_REG_CS0); | |
160 | print_reg(f, env, HEX_REG_CS1); | |
161 | #endif | |
162 | qemu_fprintf(f, "}\n"); | |
163 | } | |
164 | ||
165 | static void hexagon_dump_state(CPUState *cs, FILE *f, int flags) | |
166 | { | |
167 | HexagonCPU *cpu = HEXAGON_CPU(cs); | |
168 | CPUHexagonState *env = &cpu->env; | |
169 | ||
170 | hexagon_dump(env, f); | |
171 | } | |
172 | ||
173 | void hexagon_debug(CPUHexagonState *env) | |
174 | { | |
175 | hexagon_dump(env, stdout); | |
176 | } | |
177 | ||
178 | static void hexagon_cpu_set_pc(CPUState *cs, vaddr value) | |
179 | { | |
180 | HexagonCPU *cpu = HEXAGON_CPU(cs); | |
181 | CPUHexagonState *env = &cpu->env; | |
182 | env->gpr[HEX_REG_PC] = value; | |
183 | } | |
184 | ||
185 | static void hexagon_cpu_synchronize_from_tb(CPUState *cs, | |
186 | const TranslationBlock *tb) | |
187 | { | |
188 | HexagonCPU *cpu = HEXAGON_CPU(cs); | |
189 | CPUHexagonState *env = &cpu->env; | |
190 | env->gpr[HEX_REG_PC] = tb->pc; | |
191 | } | |
192 | ||
193 | static bool hexagon_cpu_has_work(CPUState *cs) | |
194 | { | |
195 | return true; | |
196 | } | |
197 | ||
198 | void restore_state_to_opc(CPUHexagonState *env, TranslationBlock *tb, | |
199 | target_ulong *data) | |
200 | { | |
201 | env->gpr[HEX_REG_PC] = data[0]; | |
202 | } | |
203 | ||
204 | static void hexagon_cpu_reset(DeviceState *dev) | |
205 | { | |
206 | CPUState *cs = CPU(dev); | |
207 | HexagonCPU *cpu = HEXAGON_CPU(cs); | |
208 | HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(cpu); | |
209 | ||
210 | mcc->parent_reset(dev); | |
211 | } | |
212 | ||
213 | static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) | |
214 | { | |
215 | info->print_insn = print_insn_hexagon; | |
216 | } | |
217 | ||
218 | static void hexagon_cpu_realize(DeviceState *dev, Error **errp) | |
219 | { | |
220 | CPUState *cs = CPU(dev); | |
221 | HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev); | |
222 | Error *local_err = NULL; | |
223 | ||
224 | cpu_exec_realizefn(cs, &local_err); | |
225 | if (local_err != NULL) { | |
226 | error_propagate(errp, local_err); | |
227 | return; | |
228 | } | |
229 | ||
230 | qemu_init_vcpu(cs); | |
231 | cpu_reset(cs); | |
232 | ||
233 | mcc->parent_realize(dev, errp); | |
234 | } | |
235 | ||
236 | static void hexagon_cpu_init(Object *obj) | |
237 | { | |
238 | HexagonCPU *cpu = HEXAGON_CPU(obj); | |
239 | ||
240 | cpu_set_cpustate_pointers(cpu); | |
241 | qdev_property_add_static(DEVICE(obj), &hexagon_lldb_compat_property); | |
242 | qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property); | |
243 | } | |
244 | ||
245 | static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size, | |
246 | MMUAccessType access_type, int mmu_idx, | |
247 | bool probe, uintptr_t retaddr) | |
248 | { | |
249 | #ifdef CONFIG_USER_ONLY | |
250 | switch (access_type) { | |
251 | case MMU_INST_FETCH: | |
252 | cs->exception_index = HEX_EXCP_FETCH_NO_UPAGE; | |
253 | break; | |
254 | case MMU_DATA_LOAD: | |
255 | cs->exception_index = HEX_EXCP_PRIV_NO_UREAD; | |
256 | break; | |
257 | case MMU_DATA_STORE: | |
258 | cs->exception_index = HEX_EXCP_PRIV_NO_UWRITE; | |
259 | break; | |
260 | } | |
261 | cpu_loop_exit_restore(cs, retaddr); | |
262 | #else | |
263 | #error System mode not implemented for Hexagon | |
264 | #endif | |
265 | } | |
266 | ||
267 | #include "hw/core/tcg-cpu-ops.h" | |
268 | ||
269 | static struct TCGCPUOps hexagon_tcg_ops = { | |
270 | .initialize = hexagon_translate_init, | |
271 | .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, | |
272 | .tlb_fill = hexagon_tlb_fill, | |
273 | }; | |
274 | ||
275 | static void hexagon_cpu_class_init(ObjectClass *c, void *data) | |
276 | { | |
277 | HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c); | |
278 | CPUClass *cc = CPU_CLASS(c); | |
279 | DeviceClass *dc = DEVICE_CLASS(c); | |
280 | ||
281 | device_class_set_parent_realize(dc, hexagon_cpu_realize, | |
282 | &mcc->parent_realize); | |
283 | ||
284 | device_class_set_parent_reset(dc, hexagon_cpu_reset, &mcc->parent_reset); | |
285 | ||
286 | cc->class_by_name = hexagon_cpu_class_by_name; | |
287 | cc->has_work = hexagon_cpu_has_work; | |
288 | cc->dump_state = hexagon_dump_state; | |
289 | cc->set_pc = hexagon_cpu_set_pc; | |
290 | cc->gdb_read_register = hexagon_gdb_read_register; | |
291 | cc->gdb_write_register = hexagon_gdb_write_register; | |
292 | cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS; | |
293 | cc->gdb_stop_before_watchpoint = true; | |
294 | cc->disas_set_info = hexagon_cpu_disas_set_info; | |
295 | cc->tcg_ops = &hexagon_tcg_ops; | |
296 | } | |
297 | ||
298 | #define DEFINE_CPU(type_name, initfn) \ | |
299 | { \ | |
300 | .name = type_name, \ | |
301 | .parent = TYPE_HEXAGON_CPU, \ | |
302 | .instance_init = initfn \ | |
303 | } | |
304 | ||
305 | static const TypeInfo hexagon_cpu_type_infos[] = { | |
306 | { | |
307 | .name = TYPE_HEXAGON_CPU, | |
308 | .parent = TYPE_CPU, | |
309 | .instance_size = sizeof(HexagonCPU), | |
310 | .instance_init = hexagon_cpu_init, | |
311 | .abstract = true, | |
312 | .class_size = sizeof(HexagonCPUClass), | |
313 | .class_init = hexagon_cpu_class_init, | |
314 | }, | |
315 | DEFINE_CPU(TYPE_HEXAGON_CPU_V67, hexagon_v67_cpu_init), | |
316 | }; | |
317 | ||
318 | DEFINE_TYPES(hexagon_cpu_type_infos) |