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/qemu-print.h"
  35#include "qemu/host-utils.h"
  36
  37static struct XtensaConfigList *xtensa_cores;
  38
  39static 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
  49static 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
  72static XtensaOpcodeOps *
  73xtensa_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
  90static 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
 150static 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
 171static 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
 189void 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
 204static 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
 217void 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
 236void 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#ifndef CONFIG_USER_ONLY
 246void xtensa_cpu_do_unaligned_access(CPUState *cs,
 247                                    vaddr addr, MMUAccessType access_type,
 248                                    int mmu_idx, uintptr_t retaddr)
 249{
 250    XtensaCPU *cpu = XTENSA_CPU(cs);
 251    CPUXtensaState *env = &cpu->env;
 252
 253    assert(xtensa_option_enabled(env->config,
 254                                 XTENSA_OPTION_UNALIGNED_EXCEPTION));
 255    cpu_restore_state(CPU(cpu), retaddr, true);
 256    HELPER(exception_cause_vaddr)(env,
 257                                  env->pc, LOAD_STORE_ALIGNMENT_CAUSE,
 258                                  addr);
 259}
 260
 261bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 262                         MMUAccessType access_type, int mmu_idx,
 263                         bool probe, uintptr_t retaddr)
 264{
 265    XtensaCPU *cpu = XTENSA_CPU(cs);
 266    CPUXtensaState *env = &cpu->env;
 267    uint32_t paddr;
 268    uint32_t page_size;
 269    unsigned access;
 270    int ret = xtensa_get_physical_addr(env, true, address, access_type,
 271                                       mmu_idx, &paddr, &page_size, &access);
 272
 273    qemu_log_mask(CPU_LOG_MMU, "%s(%08" VADDR_PRIx
 274                  ", %d, %d) -> %08x, ret = %d\n",
 275                  __func__, address, access_type, mmu_idx, paddr, ret);
 276
 277    if (ret == 0) {
 278        tlb_set_page(cs,
 279                     address & TARGET_PAGE_MASK,
 280                     paddr & TARGET_PAGE_MASK,
 281                     access, mmu_idx, page_size);
 282        return true;
 283    } else if (probe) {
 284        return false;
 285    } else {
 286        cpu_restore_state(cs, retaddr, true);
 287        HELPER(exception_cause_vaddr)(env, env->pc, ret, address);
 288    }
 289}
 290
 291void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
 292                                      unsigned size, MMUAccessType access_type,
 293                                      int mmu_idx, MemTxAttrs attrs,
 294                                      MemTxResult response, uintptr_t retaddr)
 295{
 296    XtensaCPU *cpu = XTENSA_CPU(cs);
 297    CPUXtensaState *env = &cpu->env;
 298
 299    cpu_restore_state(cs, retaddr, true);
 300    HELPER(exception_cause_vaddr)(env, env->pc,
 301                                  access_type == MMU_INST_FETCH ?
 302                                  INSTR_PIF_ADDR_ERROR_CAUSE :
 303                                  LOAD_STORE_PIF_ADDR_ERROR_CAUSE,
 304                                  addr);
 305}
 306
 307void xtensa_runstall(CPUXtensaState *env, bool runstall)
 308{
 309    CPUState *cpu = env_cpu(env);
 310
 311    env->runstall = runstall;
 312    cpu->halted = runstall;
 313    if (runstall) {
 314        cpu_interrupt(cpu, CPU_INTERRUPT_HALT);
 315    } else {
 316        qemu_cpu_kick(cpu);
 317    }
 318}
 319#endif /* !CONFIG_USER_ONLY */
 320