qemu/target/xtensa/helper.c
<<
>>
Prefs
   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/host-utils.h"
  35
  36static struct XtensaConfigList *xtensa_cores;
  37
  38static void add_translator_to_hash(GHashTable *translator,
  39                                   const char *name,
  40                                   const XtensaOpcodeOps *opcode)
  41{
  42    if (!g_hash_table_insert(translator, (void *)name, (void *)opcode)) {
  43        error_report("Multiple definitions of '%s' opcode in a single table",
  44                     name);
  45    }
  46}
  47
  48static GHashTable *hash_opcode_translators(const XtensaOpcodeTranslators *t)
  49{
  50    unsigned i, j;
  51    GHashTable *translator = g_hash_table_new(g_str_hash, g_str_equal);
  52
  53    for (i = 0; i < t->num_opcodes; ++i) {
  54        if (t->opcode[i].op_flags & XTENSA_OP_NAME_ARRAY) {
  55            const char * const *name = t->opcode[i].name;
  56
  57            for (j = 0; name[j]; ++j) {
  58                add_translator_to_hash(translator,
  59                                       (void *)name[j],
  60                                       (void *)(t->opcode + i));
  61            }
  62        } else {
  63            add_translator_to_hash(translator,
  64                                   (void *)t->opcode[i].name,
  65                                   (void *)(t->opcode + i));
  66        }
  67    }
  68    return translator;
  69}
  70
  71static XtensaOpcodeOps *
  72xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t,
  73                       const char *name)
  74{
  75    static GHashTable *translators;
  76    GHashTable *translator;
  77
  78    if (translators == NULL) {
  79        translators = g_hash_table_new(g_direct_hash, g_direct_equal);
  80    }
  81    translator = g_hash_table_lookup(translators, t);
  82    if (translator == NULL) {
  83        translator = hash_opcode_translators(t);
  84        g_hash_table_insert(translators, (void *)t, translator);
  85    }
  86    return g_hash_table_lookup(translator, name);
  87}
  88
  89static void init_libisa(XtensaConfig *config)
  90{
  91    unsigned i, j;
  92    unsigned opcodes;
  93    unsigned formats;
  94    unsigned regfiles;
  95
  96    config->isa = xtensa_isa_init(config->isa_internal, NULL, NULL);
  97    assert(xtensa_isa_maxlength(config->isa) <= MAX_INSN_LENGTH);
  98    opcodes = xtensa_isa_num_opcodes(config->isa);
  99    formats = xtensa_isa_num_formats(config->isa);
 100    regfiles = xtensa_isa_num_regfiles(config->isa);
 101    config->opcode_ops = g_new(XtensaOpcodeOps *, opcodes);
 102
 103    for (i = 0; i < formats; ++i) {
 104        assert(xtensa_format_num_slots(config->isa, i) <= MAX_INSN_SLOTS);
 105    }
 106
 107    for (i = 0; i < opcodes; ++i) {
 108        const char *opc_name = xtensa_opcode_name(config->isa, i);
 109        XtensaOpcodeOps *ops = NULL;
 110
 111        assert(xtensa_opcode_num_operands(config->isa, i) <= MAX_OPCODE_ARGS);
 112        if (!config->opcode_translators) {
 113            ops = xtensa_find_opcode_ops(&xtensa_core_opcodes, opc_name);
 114        } else {
 115            for (j = 0; !ops && config->opcode_translators[j]; ++j) {
 116                ops = xtensa_find_opcode_ops(config->opcode_translators[j],
 117                                             opc_name);
 118            }
 119        }
 120#ifdef DEBUG
 121        if (ops == NULL) {
 122            fprintf(stderr,
 123                    "opcode translator not found for %s's opcode '%s'\n",
 124                    config->name, opc_name);
 125        }
 126#endif
 127        config->opcode_ops[i] = ops;
 128    }
 129    config->a_regfile = xtensa_regfile_lookup(config->isa, "AR");
 130
 131    config->regfile = g_new(void **, regfiles);
 132    for (i = 0; i < regfiles; ++i) {
 133        const char *name = xtensa_regfile_name(config->isa, i);
 134
 135        config->regfile[i] = xtensa_get_regfile_by_name(name);
 136#ifdef DEBUG
 137        if (config->regfile[i] == NULL) {
 138            fprintf(stderr, "regfile '%s' not found for %s\n",
 139                    name, config->name);
 140        }
 141#endif
 142    }
 143}
 144
 145static void xtensa_finalize_config(XtensaConfig *config)
 146{
 147    if (config->isa_internal) {
 148        init_libisa(config);
 149    }
 150
 151    if (config->gdb_regmap.num_regs == 0 ||
 152        config->gdb_regmap.num_core_regs == 0) {
 153        unsigned n_regs = 0;
 154        unsigned n_core_regs = 0;
 155
 156        xtensa_count_regs(config, &n_regs, &n_core_regs);
 157        if (config->gdb_regmap.num_regs == 0) {
 158            config->gdb_regmap.num_regs = n_regs;
 159        }
 160        if (config->gdb_regmap.num_core_regs == 0) {
 161            config->gdb_regmap.num_core_regs = n_core_regs;
 162        }
 163    }
 164}
 165
 166static void xtensa_core_class_init(ObjectClass *oc, void *data)
 167{
 168    CPUClass *cc = CPU_CLASS(oc);
 169    XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
 170    XtensaConfig *config = data;
 171
 172    xtensa_finalize_config(config);
 173    xcc->config = config;
 174
 175    /*
 176     * Use num_core_regs to see only non-privileged registers in an unmodified
 177     * gdb. Use num_regs to see all registers. gdb modification is required
 178     * for that: reset bit 0 in the 'flags' field of the registers definitions
 179     * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
 180     */
 181    cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
 182}
 183
 184void xtensa_register_core(XtensaConfigList *node)
 185{
 186    TypeInfo type = {
 187        .parent = TYPE_XTENSA_CPU,
 188        .class_init = xtensa_core_class_init,
 189        .class_data = (void *)node->config,
 190    };
 191
 192    node->next = xtensa_cores;
 193    xtensa_cores = node;
 194    type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
 195    type_register(&type);
 196    g_free((gpointer)type.name);
 197}
 198
 199static uint32_t check_hw_breakpoints(CPUXtensaState *env)
 200{
 201    unsigned i;
 202
 203    for (i = 0; i < env->config->ndbreak; ++i) {
 204        if (env->cpu_watchpoint[i] &&
 205                env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) {
 206            return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT);
 207        }
 208    }
 209    return 0;
 210}
 211
 212void xtensa_breakpoint_handler(CPUState *cs)
 213{
 214    XtensaCPU *cpu = XTENSA_CPU(cs);
 215    CPUXtensaState *env = &cpu->env;
 216
 217    if (cs->watchpoint_hit) {
 218        if (cs->watchpoint_hit->flags & BP_CPU) {
 219            uint32_t cause;
 220
 221            cs->watchpoint_hit = NULL;
 222            cause = check_hw_breakpoints(env);
 223            if (cause) {
 224                debug_exception_env(env, cause);
 225            }
 226            cpu_loop_exit_noexc(cs);
 227        }
 228    }
 229}
 230
 231void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 232{
 233    XtensaConfigList *core = xtensa_cores;
 234    cpu_fprintf(f, "Available CPUs:\n");
 235    for (; core; core = core->next) {
 236        cpu_fprintf(f, "  %s\n", core->config->name);
 237    }
 238}
 239
 240#ifdef CONFIG_USER_ONLY
 241
 242int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw,
 243                                int mmu_idx)
 244{
 245    XtensaCPU *cpu = XTENSA_CPU(cs);
 246    CPUXtensaState *env = &cpu->env;
 247
 248    qemu_log_mask(CPU_LOG_INT,
 249                  "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n",
 250                  __func__, rw, address, size);
 251    env->sregs[EXCVADDR] = address;
 252    env->sregs[EXCCAUSE] = rw ? STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE;
 253    cs->exception_index = EXC_USER;
 254    return 1;
 255}
 256
 257#else
 258
 259void xtensa_cpu_do_unaligned_access(CPUState *cs,
 260                                    vaddr addr, MMUAccessType access_type,
 261                                    int mmu_idx, uintptr_t retaddr)
 262{
 263    XtensaCPU *cpu = XTENSA_CPU(cs);
 264    CPUXtensaState *env = &cpu->env;
 265
 266    if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
 267        !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) {
 268        cpu_restore_state(CPU(cpu), retaddr, true);
 269        HELPER(exception_cause_vaddr)(env,
 270                                      env->pc, LOAD_STORE_ALIGNMENT_CAUSE,
 271                                      addr);
 272    }
 273}
 274
 275void tlb_fill(CPUState *cs, target_ulong vaddr, int size,
 276              MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
 277{
 278    XtensaCPU *cpu = XTENSA_CPU(cs);
 279    CPUXtensaState *env = &cpu->env;
 280    uint32_t paddr;
 281    uint32_t page_size;
 282    unsigned access;
 283    int ret = xtensa_get_physical_addr(env, true, vaddr, access_type, mmu_idx,
 284                                       &paddr, &page_size, &access);
 285
 286    qemu_log_mask(CPU_LOG_MMU, "%s(%08x, %d, %d) -> %08x, ret = %d\n",
 287                  __func__, vaddr, access_type, mmu_idx, paddr, ret);
 288
 289    if (ret == 0) {
 290        tlb_set_page(cs,
 291                     vaddr & TARGET_PAGE_MASK,
 292                     paddr & TARGET_PAGE_MASK,
 293                     access, mmu_idx, page_size);
 294    } else {
 295        cpu_restore_state(cs, retaddr, true);
 296        HELPER(exception_cause_vaddr)(env, env->pc, ret, vaddr);
 297    }
 298}
 299
 300void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
 301                                      unsigned size, MMUAccessType access_type,
 302                                      int mmu_idx, MemTxAttrs attrs,
 303                                      MemTxResult response, uintptr_t retaddr)
 304{
 305    XtensaCPU *cpu = XTENSA_CPU(cs);
 306    CPUXtensaState *env = &cpu->env;
 307
 308    cpu_restore_state(cs, retaddr, true);
 309    HELPER(exception_cause_vaddr)(env, env->pc,
 310                                  access_type == MMU_INST_FETCH ?
 311                                  INSTR_PIF_ADDR_ERROR_CAUSE :
 312                                  LOAD_STORE_PIF_ADDR_ERROR_CAUSE,
 313                                  addr);
 314}
 315
 316void xtensa_runstall(CPUXtensaState *env, bool runstall)
 317{
 318    CPUState *cpu = CPU(xtensa_env_get_cpu(env));
 319
 320    env->runstall = runstall;
 321    cpu->halted = runstall;
 322    if (runstall) {
 323        cpu_interrupt(cpu, CPU_INTERRUPT_HALT);
 324    } else {
 325        qemu_cpu_kick(cpu);
 326    }
 327}
 328#endif
 329