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
 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
 148static 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
 169static 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
 187void 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
 202static 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
 215void 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
 234void 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
 245bool 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
 264void 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
 280bool 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
 310void 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
 326void 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