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