xref: /openbmc/qemu/target/xtensa/helper.c (revision 38dc1294)
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     xtensa_collect_sr_names(config);
145 }
146 
147 static void xtensa_finalize_config(XtensaConfig *config)
148 {
149     if (config->isa_internal) {
150         init_libisa(config);
151     }
152 
153     if (config->gdb_regmap.num_regs == 0 ||
154         config->gdb_regmap.num_core_regs == 0) {
155         unsigned n_regs = 0;
156         unsigned n_core_regs = 0;
157 
158         xtensa_count_regs(config, &n_regs, &n_core_regs);
159         if (config->gdb_regmap.num_regs == 0) {
160             config->gdb_regmap.num_regs = n_regs;
161         }
162         if (config->gdb_regmap.num_core_regs == 0) {
163             config->gdb_regmap.num_core_regs = n_core_regs;
164         }
165     }
166 }
167 
168 static void xtensa_core_class_init(ObjectClass *oc, void *data)
169 {
170     CPUClass *cc = CPU_CLASS(oc);
171     XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
172     XtensaConfig *config = data;
173 
174     xtensa_finalize_config(config);
175     xcc->config = config;
176 
177     /*
178      * Use num_core_regs to see only non-privileged registers in an unmodified
179      * gdb. Use num_regs to see all registers. gdb modification is required
180      * for that: reset bit 0 in the 'flags' field of the registers definitions
181      * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
182      */
183     cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
184 }
185 
186 void xtensa_register_core(XtensaConfigList *node)
187 {
188     TypeInfo type = {
189         .parent = TYPE_XTENSA_CPU,
190         .class_init = xtensa_core_class_init,
191         .class_data = (void *)node->config,
192     };
193 
194     node->next = xtensa_cores;
195     xtensa_cores = node;
196     type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
197     type_register(&type);
198     g_free((gpointer)type.name);
199 }
200 
201 static uint32_t check_hw_breakpoints(CPUXtensaState *env)
202 {
203     unsigned i;
204 
205     for (i = 0; i < env->config->ndbreak; ++i) {
206         if (env->cpu_watchpoint[i] &&
207                 env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) {
208             return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT);
209         }
210     }
211     return 0;
212 }
213 
214 void xtensa_breakpoint_handler(CPUState *cs)
215 {
216     XtensaCPU *cpu = XTENSA_CPU(cs);
217     CPUXtensaState *env = &cpu->env;
218 
219     if (cs->watchpoint_hit) {
220         if (cs->watchpoint_hit->flags & BP_CPU) {
221             uint32_t cause;
222 
223             cs->watchpoint_hit = NULL;
224             cause = check_hw_breakpoints(env);
225             if (cause) {
226                 debug_exception_env(env, cause);
227             }
228             cpu_loop_exit_noexc(cs);
229         }
230     }
231 }
232 
233 void xtensa_cpu_list(void)
234 {
235     XtensaConfigList *core = xtensa_cores;
236     qemu_printf("Available CPUs:\n");
237     for (; core; core = core->next) {
238         qemu_printf("  %s\n", core->config->name);
239     }
240 }
241 
242 #ifdef CONFIG_USER_ONLY
243 
244 bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
245                          MMUAccessType access_type, int mmu_idx,
246                          bool probe, uintptr_t retaddr)
247 {
248     XtensaCPU *cpu = XTENSA_CPU(cs);
249     CPUXtensaState *env = &cpu->env;
250 
251     qemu_log_mask(CPU_LOG_INT,
252                   "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n",
253                   __func__, access_type, address, size);
254     env->sregs[EXCVADDR] = address;
255     env->sregs[EXCCAUSE] = (access_type == MMU_DATA_STORE ?
256                             STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE);
257     cs->exception_index = EXC_USER;
258     cpu_loop_exit_restore(cs, retaddr);
259 }
260 
261 #else
262 
263 void xtensa_cpu_do_unaligned_access(CPUState *cs,
264                                     vaddr addr, MMUAccessType access_type,
265                                     int mmu_idx, uintptr_t retaddr)
266 {
267     XtensaCPU *cpu = XTENSA_CPU(cs);
268     CPUXtensaState *env = &cpu->env;
269 
270     if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
271         !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) {
272         cpu_restore_state(CPU(cpu), retaddr, true);
273         HELPER(exception_cause_vaddr)(env,
274                                       env->pc, LOAD_STORE_ALIGNMENT_CAUSE,
275                                       addr);
276     }
277 }
278 
279 bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
280                          MMUAccessType access_type, int mmu_idx,
281                          bool probe, uintptr_t retaddr)
282 {
283     XtensaCPU *cpu = XTENSA_CPU(cs);
284     CPUXtensaState *env = &cpu->env;
285     uint32_t paddr;
286     uint32_t page_size;
287     unsigned access;
288     int ret = xtensa_get_physical_addr(env, true, address, access_type,
289                                        mmu_idx, &paddr, &page_size, &access);
290 
291     qemu_log_mask(CPU_LOG_MMU, "%s(%08" VADDR_PRIx
292                   ", %d, %d) -> %08x, ret = %d\n",
293                   __func__, address, access_type, mmu_idx, paddr, ret);
294 
295     if (ret == 0) {
296         tlb_set_page(cs,
297                      address & TARGET_PAGE_MASK,
298                      paddr & TARGET_PAGE_MASK,
299                      access, mmu_idx, page_size);
300         return true;
301     } else if (probe) {
302         return false;
303     } else {
304         cpu_restore_state(cs, retaddr, true);
305         HELPER(exception_cause_vaddr)(env, env->pc, ret, address);
306     }
307 }
308 
309 void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
310                                       unsigned size, MMUAccessType access_type,
311                                       int mmu_idx, MemTxAttrs attrs,
312                                       MemTxResult response, uintptr_t retaddr)
313 {
314     XtensaCPU *cpu = XTENSA_CPU(cs);
315     CPUXtensaState *env = &cpu->env;
316 
317     cpu_restore_state(cs, retaddr, true);
318     HELPER(exception_cause_vaddr)(env, env->pc,
319                                   access_type == MMU_INST_FETCH ?
320                                   INSTR_PIF_ADDR_ERROR_CAUSE :
321                                   LOAD_STORE_PIF_ADDR_ERROR_CAUSE,
322                                   addr);
323 }
324 
325 void xtensa_runstall(CPUXtensaState *env, bool runstall)
326 {
327     CPUState *cpu = CPU(xtensa_env_get_cpu(env));
328 
329     env->runstall = runstall;
330     cpu->halted = runstall;
331     if (runstall) {
332         cpu_interrupt(cpu, CPU_INTERRUPT_HALT);
333     } else {
334         qemu_cpu_kick(cpu);
335     }
336 }
337 #endif
338