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