xref: /openbmc/qemu/target/xtensa/helper.c (revision b097ba37)
1 /*
2  * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the Open Source and Linux Lab nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "qemu/osdep.h"
29 #include "cpu.h"
30 #include "exec/exec-all.h"
31 #include "exec/gdbstub.h"
32 #include "exec/helper-proto.h"
33 #include "qemu/error-report.h"
34 #include "qemu/qemu-print.h"
35 #include "qemu/host-utils.h"
36 
37 static struct XtensaConfigList *xtensa_cores;
38 
39 static void add_translator_to_hash(GHashTable *translator,
40                                    const char *name,
41                                    const XtensaOpcodeOps *opcode)
42 {
43     if (!g_hash_table_insert(translator, (void *)name, (void *)opcode)) {
44         error_report("Multiple definitions of '%s' opcode in a single table",
45                      name);
46     }
47 }
48 
49 static GHashTable *hash_opcode_translators(const XtensaOpcodeTranslators *t)
50 {
51     unsigned i, j;
52     GHashTable *translator = g_hash_table_new(g_str_hash, g_str_equal);
53 
54     for (i = 0; i < t->num_opcodes; ++i) {
55         if (t->opcode[i].op_flags & XTENSA_OP_NAME_ARRAY) {
56             const char * const *name = t->opcode[i].name;
57 
58             for (j = 0; name[j]; ++j) {
59                 add_translator_to_hash(translator,
60                                        (void *)name[j],
61                                        (void *)(t->opcode + i));
62             }
63         } else {
64             add_translator_to_hash(translator,
65                                    (void *)t->opcode[i].name,
66                                    (void *)(t->opcode + i));
67         }
68     }
69     return translator;
70 }
71 
72 static XtensaOpcodeOps *
73 xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t,
74                        const char *name)
75 {
76     static GHashTable *translators;
77     GHashTable *translator;
78 
79     if (translators == NULL) {
80         translators = g_hash_table_new(g_direct_hash, g_direct_equal);
81     }
82     translator = g_hash_table_lookup(translators, t);
83     if (translator == NULL) {
84         translator = hash_opcode_translators(t);
85         g_hash_table_insert(translators, (void *)t, translator);
86     }
87     return g_hash_table_lookup(translator, name);
88 }
89 
90 static void init_libisa(XtensaConfig *config)
91 {
92     unsigned i, j;
93     unsigned opcodes;
94     unsigned formats;
95     unsigned regfiles;
96 
97     config->isa = xtensa_isa_init(config->isa_internal, NULL, NULL);
98     assert(xtensa_isa_maxlength(config->isa) <= MAX_INSN_LENGTH);
99     opcodes = xtensa_isa_num_opcodes(config->isa);
100     formats = xtensa_isa_num_formats(config->isa);
101     regfiles = xtensa_isa_num_regfiles(config->isa);
102     config->opcode_ops = g_new(XtensaOpcodeOps *, opcodes);
103 
104     for (i = 0; i < formats; ++i) {
105         assert(xtensa_format_num_slots(config->isa, i) <= MAX_INSN_SLOTS);
106     }
107 
108     for (i = 0; i < opcodes; ++i) {
109         const char *opc_name = xtensa_opcode_name(config->isa, i);
110         XtensaOpcodeOps *ops = NULL;
111 
112         assert(xtensa_opcode_num_operands(config->isa, i) <= MAX_OPCODE_ARGS);
113         if (!config->opcode_translators) {
114             ops = xtensa_find_opcode_ops(&xtensa_core_opcodes, opc_name);
115         } else {
116             for (j = 0; !ops && config->opcode_translators[j]; ++j) {
117                 ops = xtensa_find_opcode_ops(config->opcode_translators[j],
118                                              opc_name);
119             }
120         }
121 #ifdef DEBUG
122         if (ops == NULL) {
123             fprintf(stderr,
124                     "opcode translator not found for %s's opcode '%s'\n",
125                     config->name, opc_name);
126         }
127 #endif
128         config->opcode_ops[i] = ops;
129     }
130     config->a_regfile = xtensa_regfile_lookup(config->isa, "AR");
131 
132     config->regfile = g_new(void **, regfiles);
133     for (i = 0; i < regfiles; ++i) {
134         const char *name = xtensa_regfile_name(config->isa, i);
135 
136         config->regfile[i] = xtensa_get_regfile_by_name(name);
137 #ifdef DEBUG
138         if (config->regfile[i] == NULL) {
139             fprintf(stderr, "regfile '%s' not found for %s\n",
140                     name, config->name);
141         }
142 #endif
143     }
144 }
145 
146 static void xtensa_finalize_config(XtensaConfig *config)
147 {
148     if (config->isa_internal) {
149         init_libisa(config);
150     }
151 
152     if (config->gdb_regmap.num_regs == 0 ||
153         config->gdb_regmap.num_core_regs == 0) {
154         unsigned n_regs = 0;
155         unsigned n_core_regs = 0;
156 
157         xtensa_count_regs(config, &n_regs, &n_core_regs);
158         if (config->gdb_regmap.num_regs == 0) {
159             config->gdb_regmap.num_regs = n_regs;
160         }
161         if (config->gdb_regmap.num_core_regs == 0) {
162             config->gdb_regmap.num_core_regs = n_core_regs;
163         }
164     }
165 }
166 
167 static void xtensa_core_class_init(ObjectClass *oc, void *data)
168 {
169     CPUClass *cc = CPU_CLASS(oc);
170     XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
171     XtensaConfig *config = data;
172 
173     xtensa_finalize_config(config);
174     xcc->config = config;
175 
176     /*
177      * Use num_core_regs to see only non-privileged registers in an unmodified
178      * gdb. Use num_regs to see all registers. gdb modification is required
179      * for that: reset bit 0 in the 'flags' field of the registers definitions
180      * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
181      */
182     cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
183 }
184 
185 void xtensa_register_core(XtensaConfigList *node)
186 {
187     TypeInfo type = {
188         .parent = TYPE_XTENSA_CPU,
189         .class_init = xtensa_core_class_init,
190         .class_data = (void *)node->config,
191     };
192 
193     node->next = xtensa_cores;
194     xtensa_cores = node;
195     type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
196     type_register(&type);
197     g_free((gpointer)type.name);
198 }
199 
200 static uint32_t check_hw_breakpoints(CPUXtensaState *env)
201 {
202     unsigned i;
203 
204     for (i = 0; i < env->config->ndbreak; ++i) {
205         if (env->cpu_watchpoint[i] &&
206                 env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) {
207             return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT);
208         }
209     }
210     return 0;
211 }
212 
213 void xtensa_breakpoint_handler(CPUState *cs)
214 {
215     XtensaCPU *cpu = XTENSA_CPU(cs);
216     CPUXtensaState *env = &cpu->env;
217 
218     if (cs->watchpoint_hit) {
219         if (cs->watchpoint_hit->flags & BP_CPU) {
220             uint32_t cause;
221 
222             cs->watchpoint_hit = NULL;
223             cause = check_hw_breakpoints(env);
224             if (cause) {
225                 debug_exception_env(env, cause);
226             }
227             cpu_loop_exit_noexc(cs);
228         }
229     }
230 }
231 
232 void xtensa_cpu_list(void)
233 {
234     XtensaConfigList *core = xtensa_cores;
235     qemu_printf("Available CPUs:\n");
236     for (; core; core = core->next) {
237         qemu_printf("  %s\n", core->config->name);
238     }
239 }
240 
241 #ifdef CONFIG_USER_ONLY
242 
243 int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw,
244                                 int mmu_idx)
245 {
246     XtensaCPU *cpu = XTENSA_CPU(cs);
247     CPUXtensaState *env = &cpu->env;
248 
249     qemu_log_mask(CPU_LOG_INT,
250                   "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n",
251                   __func__, rw, address, size);
252     env->sregs[EXCVADDR] = address;
253     env->sregs[EXCCAUSE] = rw ? STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE;
254     cs->exception_index = EXC_USER;
255     return 1;
256 }
257 
258 #else
259 
260 void xtensa_cpu_do_unaligned_access(CPUState *cs,
261                                     vaddr addr, MMUAccessType access_type,
262                                     int mmu_idx, uintptr_t retaddr)
263 {
264     XtensaCPU *cpu = XTENSA_CPU(cs);
265     CPUXtensaState *env = &cpu->env;
266 
267     if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
268         !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) {
269         cpu_restore_state(CPU(cpu), retaddr, true);
270         HELPER(exception_cause_vaddr)(env,
271                                       env->pc, LOAD_STORE_ALIGNMENT_CAUSE,
272                                       addr);
273     }
274 }
275 
276 void tlb_fill(CPUState *cs, target_ulong vaddr, int size,
277               MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
278 {
279     XtensaCPU *cpu = XTENSA_CPU(cs);
280     CPUXtensaState *env = &cpu->env;
281     uint32_t paddr;
282     uint32_t page_size;
283     unsigned access;
284     int ret = xtensa_get_physical_addr(env, true, vaddr, access_type, mmu_idx,
285                                        &paddr, &page_size, &access);
286 
287     qemu_log_mask(CPU_LOG_MMU, "%s(%08x, %d, %d) -> %08x, ret = %d\n",
288                   __func__, vaddr, access_type, mmu_idx, paddr, ret);
289 
290     if (ret == 0) {
291         tlb_set_page(cs,
292                      vaddr & TARGET_PAGE_MASK,
293                      paddr & TARGET_PAGE_MASK,
294                      access, mmu_idx, page_size);
295     } else {
296         cpu_restore_state(cs, retaddr, true);
297         HELPER(exception_cause_vaddr)(env, env->pc, ret, vaddr);
298     }
299 }
300 
301 void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
302                                       unsigned size, MMUAccessType access_type,
303                                       int mmu_idx, MemTxAttrs attrs,
304                                       MemTxResult response, uintptr_t retaddr)
305 {
306     XtensaCPU *cpu = XTENSA_CPU(cs);
307     CPUXtensaState *env = &cpu->env;
308 
309     cpu_restore_state(cs, retaddr, true);
310     HELPER(exception_cause_vaddr)(env, env->pc,
311                                   access_type == MMU_INST_FETCH ?
312                                   INSTR_PIF_ADDR_ERROR_CAUSE :
313                                   LOAD_STORE_PIF_ADDR_ERROR_CAUSE,
314                                   addr);
315 }
316 
317 void xtensa_runstall(CPUXtensaState *env, bool runstall)
318 {
319     CPUState *cpu = CPU(xtensa_env_get_cpu(env));
320 
321     env->runstall = runstall;
322     cpu->halted = runstall;
323     if (runstall) {
324         cpu_interrupt(cpu, CPU_INTERRUPT_HALT);
325     } else {
326         qemu_cpu_kick(cpu);
327     }
328 }
329 #endif
330