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