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 "qemu/host-utils.h"
  33#if !defined(CONFIG_USER_ONLY)
  34#include "hw/loader.h"
  35#endif
  36
  37static struct XtensaConfigList *xtensa_cores;
  38
  39static void xtensa_core_class_init(ObjectClass *oc, void *data)
  40{
  41    CPUClass *cc = CPU_CLASS(oc);
  42    XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
  43    const XtensaConfig *config = data;
  44
  45    xcc->config = config;
  46
  47    /* Use num_core_regs to see only non-privileged registers in an unmodified
  48     * gdb. Use num_regs to see all registers. gdb modification is required
  49     * for that: reset bit 0 in the 'flags' field of the registers definitions
  50     * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
  51     */
  52    cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
  53}
  54
  55void xtensa_finalize_config(XtensaConfig *config)
  56{
  57    unsigned i, n = 0;
  58
  59    if (config->gdb_regmap.num_regs) {
  60        return;
  61    }
  62
  63    for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) {
  64        n += (config->gdb_regmap.reg[i].type != 6);
  65    }
  66    config->gdb_regmap.num_regs = n;
  67}
  68
  69void xtensa_register_core(XtensaConfigList *node)
  70{
  71    TypeInfo type = {
  72        .parent = TYPE_XTENSA_CPU,
  73        .class_init = xtensa_core_class_init,
  74        .class_data = (void *)node->config,
  75    };
  76
  77    node->next = xtensa_cores;
  78    xtensa_cores = node;
  79    type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
  80    type_register(&type);
  81    g_free((gpointer)type.name);
  82}
  83
  84static uint32_t check_hw_breakpoints(CPUXtensaState *env)
  85{
  86    unsigned i;
  87
  88    for (i = 0; i < env->config->ndbreak; ++i) {
  89        if (env->cpu_watchpoint[i] &&
  90                env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) {
  91            return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT);
  92        }
  93    }
  94    return 0;
  95}
  96
  97void xtensa_breakpoint_handler(CPUState *cs)
  98{
  99    XtensaCPU *cpu = XTENSA_CPU(cs);
 100    CPUXtensaState *env = &cpu->env;
 101
 102    if (cs->watchpoint_hit) {
 103        if (cs->watchpoint_hit->flags & BP_CPU) {
 104            uint32_t cause;
 105
 106            cs->watchpoint_hit = NULL;
 107            cause = check_hw_breakpoints(env);
 108            if (cause) {
 109                debug_exception_env(env, cause);
 110            }
 111            cpu_loop_exit_noexc(cs);
 112        }
 113    }
 114}
 115
 116void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 117{
 118    XtensaConfigList *core = xtensa_cores;
 119    cpu_fprintf(f, "Available CPUs:\n");
 120    for (; core; core = core->next) {
 121        cpu_fprintf(f, "  %s\n", core->config->name);
 122    }
 123}
 124
 125hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 126{
 127    XtensaCPU *cpu = XTENSA_CPU(cs);
 128    uint32_t paddr;
 129    uint32_t page_size;
 130    unsigned access;
 131
 132    if (xtensa_get_physical_addr(&cpu->env, false, addr, 0, 0,
 133                &paddr, &page_size, &access) == 0) {
 134        return paddr;
 135    }
 136    if (xtensa_get_physical_addr(&cpu->env, false, addr, 2, 0,
 137                &paddr, &page_size, &access) == 0) {
 138        return paddr;
 139    }
 140    return ~0;
 141}
 142
 143static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
 144{
 145    if (xtensa_option_enabled(env->config,
 146                XTENSA_OPTION_RELOCATABLE_VECTOR)) {
 147        return vector - env->config->vecbase + env->sregs[VECBASE];
 148    } else {
 149        return vector;
 150    }
 151}
 152
 153/*!
 154 * Handle penging IRQ.
 155 * For the high priority interrupt jump to the corresponding interrupt vector.
 156 * For the level-1 interrupt convert it to either user, kernel or double
 157 * exception with the 'level-1 interrupt' exception cause.
 158 */
 159static void handle_interrupt(CPUXtensaState *env)
 160{
 161    int level = env->pending_irq_level;
 162
 163    if (level > xtensa_get_cintlevel(env) &&
 164            level <= env->config->nlevel &&
 165            (env->config->level_mask[level] &
 166             env->sregs[INTSET] &
 167             env->sregs[INTENABLE])) {
 168        CPUState *cs = CPU(xtensa_env_get_cpu(env));
 169
 170        if (level > 1) {
 171            env->sregs[EPC1 + level - 1] = env->pc;
 172            env->sregs[EPS2 + level - 2] = env->sregs[PS];
 173            env->sregs[PS] =
 174                (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM;
 175            env->pc = relocated_vector(env,
 176                    env->config->interrupt_vector[level]);
 177        } else {
 178            env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE;
 179
 180            if (env->sregs[PS] & PS_EXCM) {
 181                if (env->config->ndepc) {
 182                    env->sregs[DEPC] = env->pc;
 183                } else {
 184                    env->sregs[EPC1] = env->pc;
 185                }
 186                cs->exception_index = EXC_DOUBLE;
 187            } else {
 188                env->sregs[EPC1] = env->pc;
 189                cs->exception_index =
 190                    (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL;
 191            }
 192            env->sregs[PS] |= PS_EXCM;
 193        }
 194        env->exception_taken = 1;
 195    }
 196}
 197
 198/* Called from cpu_handle_interrupt with BQL held */
 199void xtensa_cpu_do_interrupt(CPUState *cs)
 200{
 201    XtensaCPU *cpu = XTENSA_CPU(cs);
 202    CPUXtensaState *env = &cpu->env;
 203
 204    if (cs->exception_index == EXC_IRQ) {
 205        qemu_log_mask(CPU_LOG_INT,
 206                "%s(EXC_IRQ) level = %d, cintlevel = %d, "
 207                "pc = %08x, a0 = %08x, ps = %08x, "
 208                "intset = %08x, intenable = %08x, "
 209                "ccount = %08x\n",
 210                __func__, env->pending_irq_level, xtensa_get_cintlevel(env),
 211                env->pc, env->regs[0], env->sregs[PS],
 212                env->sregs[INTSET], env->sregs[INTENABLE],
 213                env->sregs[CCOUNT]);
 214        handle_interrupt(env);
 215    }
 216
 217    switch (cs->exception_index) {
 218    case EXC_WINDOW_OVERFLOW4:
 219    case EXC_WINDOW_UNDERFLOW4:
 220    case EXC_WINDOW_OVERFLOW8:
 221    case EXC_WINDOW_UNDERFLOW8:
 222    case EXC_WINDOW_OVERFLOW12:
 223    case EXC_WINDOW_UNDERFLOW12:
 224    case EXC_KERNEL:
 225    case EXC_USER:
 226    case EXC_DOUBLE:
 227    case EXC_DEBUG:
 228        qemu_log_mask(CPU_LOG_INT, "%s(%d) "
 229                "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
 230                __func__, cs->exception_index,
 231                env->pc, env->regs[0], env->sregs[PS], env->sregs[CCOUNT]);
 232        if (env->config->exception_vector[cs->exception_index]) {
 233            env->pc = relocated_vector(env,
 234                    env->config->exception_vector[cs->exception_index]);
 235            env->exception_taken = 1;
 236        } else {
 237            qemu_log_mask(CPU_LOG_INT, "%s(pc = %08x) bad exception_index: %d\n",
 238                          __func__, env->pc, cs->exception_index);
 239        }
 240        break;
 241
 242    case EXC_IRQ:
 243        break;
 244
 245    default:
 246        qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
 247                __func__, env->pc, cs->exception_index);
 248        break;
 249    }
 250    check_interrupts(env);
 251}
 252
 253bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 254{
 255    if (interrupt_request & CPU_INTERRUPT_HARD) {
 256        cs->exception_index = EXC_IRQ;
 257        xtensa_cpu_do_interrupt(cs);
 258        return true;
 259    }
 260    return false;
 261}
 262
 263static void reset_tlb_mmu_all_ways(CPUXtensaState *env,
 264        const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
 265{
 266    unsigned wi, ei;
 267
 268    for (wi = 0; wi < tlb->nways; ++wi) {
 269        for (ei = 0; ei < tlb->way_size[wi]; ++ei) {
 270            entry[wi][ei].asid = 0;
 271            entry[wi][ei].variable = true;
 272        }
 273    }
 274}
 275
 276static void reset_tlb_mmu_ways56(CPUXtensaState *env,
 277        const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
 278{
 279    if (!tlb->varway56) {
 280        static const xtensa_tlb_entry way5[] = {
 281            {
 282                .vaddr = 0xd0000000,
 283                .paddr = 0,
 284                .asid = 1,
 285                .attr = 7,
 286                .variable = false,
 287            }, {
 288                .vaddr = 0xd8000000,
 289                .paddr = 0,
 290                .asid = 1,
 291                .attr = 3,
 292                .variable = false,
 293            }
 294        };
 295        static const xtensa_tlb_entry way6[] = {
 296            {
 297                .vaddr = 0xe0000000,
 298                .paddr = 0xf0000000,
 299                .asid = 1,
 300                .attr = 7,
 301                .variable = false,
 302            }, {
 303                .vaddr = 0xf0000000,
 304                .paddr = 0xf0000000,
 305                .asid = 1,
 306                .attr = 3,
 307                .variable = false,
 308            }
 309        };
 310        memcpy(entry[5], way5, sizeof(way5));
 311        memcpy(entry[6], way6, sizeof(way6));
 312    } else {
 313        uint32_t ei;
 314        for (ei = 0; ei < 8; ++ei) {
 315            entry[6][ei].vaddr = ei << 29;
 316            entry[6][ei].paddr = ei << 29;
 317            entry[6][ei].asid = 1;
 318            entry[6][ei].attr = 3;
 319        }
 320    }
 321}
 322
 323static void reset_tlb_region_way0(CPUXtensaState *env,
 324        xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
 325{
 326    unsigned ei;
 327
 328    for (ei = 0; ei < 8; ++ei) {
 329        entry[0][ei].vaddr = ei << 29;
 330        entry[0][ei].paddr = ei << 29;
 331        entry[0][ei].asid = 1;
 332        entry[0][ei].attr = 2;
 333        entry[0][ei].variable = true;
 334    }
 335}
 336
 337void reset_mmu(CPUXtensaState *env)
 338{
 339    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
 340        env->sregs[RASID] = 0x04030201;
 341        env->sregs[ITLBCFG] = 0;
 342        env->sregs[DTLBCFG] = 0;
 343        env->autorefill_idx = 0;
 344        reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb);
 345        reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb);
 346        reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb);
 347        reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb);
 348    } else {
 349        reset_tlb_region_way0(env, env->itlb);
 350        reset_tlb_region_way0(env, env->dtlb);
 351    }
 352}
 353
 354static unsigned get_ring(const CPUXtensaState *env, uint8_t asid)
 355{
 356    unsigned i;
 357    for (i = 0; i < 4; ++i) {
 358        if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) {
 359            return i;
 360        }
 361    }
 362    return 0xff;
 363}
 364
 365/*!
 366 * Lookup xtensa TLB for the given virtual address.
 367 * See ISA, 4.6.2.2
 368 *
 369 * \param pwi: [out] way index
 370 * \param pei: [out] entry index
 371 * \param pring: [out] access ring
 372 * \return 0 if ok, exception cause code otherwise
 373 */
 374int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb,
 375        uint32_t *pwi, uint32_t *pei, uint8_t *pring)
 376{
 377    const xtensa_tlb *tlb = dtlb ?
 378        &env->config->dtlb : &env->config->itlb;
 379    const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ?
 380        env->dtlb : env->itlb;
 381
 382    int nhits = 0;
 383    unsigned wi;
 384
 385    for (wi = 0; wi < tlb->nways; ++wi) {
 386        uint32_t vpn;
 387        uint32_t ei;
 388        split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei);
 389        if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) {
 390            unsigned ring = get_ring(env, entry[wi][ei].asid);
 391            if (ring < 4) {
 392                if (++nhits > 1) {
 393                    return dtlb ?
 394                        LOAD_STORE_TLB_MULTI_HIT_CAUSE :
 395                        INST_TLB_MULTI_HIT_CAUSE;
 396                }
 397                *pwi = wi;
 398                *pei = ei;
 399                *pring = ring;
 400            }
 401        }
 402    }
 403    return nhits ? 0 :
 404        (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE);
 405}
 406
 407/*!
 408 * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask.
 409 * See ISA, 4.6.5.10
 410 */
 411static unsigned mmu_attr_to_access(uint32_t attr)
 412{
 413    unsigned access = 0;
 414
 415    if (attr < 12) {
 416        access |= PAGE_READ;
 417        if (attr & 0x1) {
 418            access |= PAGE_EXEC;
 419        }
 420        if (attr & 0x2) {
 421            access |= PAGE_WRITE;
 422        }
 423
 424        switch (attr & 0xc) {
 425        case 0:
 426            access |= PAGE_CACHE_BYPASS;
 427            break;
 428
 429        case 4:
 430            access |= PAGE_CACHE_WB;
 431            break;
 432
 433        case 8:
 434            access |= PAGE_CACHE_WT;
 435            break;
 436        }
 437    } else if (attr == 13) {
 438        access |= PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE;
 439    }
 440    return access;
 441}
 442
 443/*!
 444 * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask.
 445 * See ISA, 4.6.3.3
 446 */
 447static unsigned region_attr_to_access(uint32_t attr)
 448{
 449    static const unsigned access[16] = {
 450         [0] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_WT,
 451         [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT,
 452         [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS,
 453         [3] =                          PAGE_EXEC | PAGE_CACHE_WB,
 454         [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
 455         [5] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
 456        [14] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_ISOLATE,
 457    };
 458
 459    return access[attr & 0xf];
 460}
 461
 462/*!
 463 * Convert cacheattr to PAGE_{READ,WRITE,EXEC} mask.
 464 * See ISA, A.2.14 The Cache Attribute Register
 465 */
 466static unsigned cacheattr_attr_to_access(uint32_t attr)
 467{
 468    static const unsigned access[16] = {
 469         [0] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_WT,
 470         [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT,
 471         [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS,
 472         [3] =                          PAGE_EXEC | PAGE_CACHE_WB,
 473         [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB,
 474        [14] = PAGE_READ | PAGE_WRITE             | PAGE_CACHE_ISOLATE,
 475    };
 476
 477    return access[attr & 0xf];
 478}
 479
 480static bool is_access_granted(unsigned access, int is_write)
 481{
 482    switch (is_write) {
 483    case 0:
 484        return access & PAGE_READ;
 485
 486    case 1:
 487        return access & PAGE_WRITE;
 488
 489    case 2:
 490        return access & PAGE_EXEC;
 491
 492    default:
 493        return 0;
 494    }
 495}
 496
 497static int get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte);
 498
 499static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb,
 500        uint32_t vaddr, int is_write, int mmu_idx,
 501        uint32_t *paddr, uint32_t *page_size, unsigned *access,
 502        bool may_lookup_pt)
 503{
 504    bool dtlb = is_write != 2;
 505    uint32_t wi;
 506    uint32_t ei;
 507    uint8_t ring;
 508    uint32_t vpn;
 509    uint32_t pte;
 510    const xtensa_tlb_entry *entry = NULL;
 511    xtensa_tlb_entry tmp_entry;
 512    int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring);
 513
 514    if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) &&
 515            may_lookup_pt && get_pte(env, vaddr, &pte) == 0) {
 516        ring = (pte >> 4) & 0x3;
 517        wi = 0;
 518        split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, wi, &ei);
 519
 520        if (update_tlb) {
 521            wi = ++env->autorefill_idx & 0x3;
 522            xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte);
 523            env->sregs[EXCVADDR] = vaddr;
 524            qemu_log_mask(CPU_LOG_MMU, "%s: autorefill(%08x): %08x -> %08x\n",
 525                          __func__, vaddr, vpn, pte);
 526        } else {
 527            xtensa_tlb_set_entry_mmu(env, &tmp_entry, dtlb, wi, ei, vpn, pte);
 528            entry = &tmp_entry;
 529        }
 530        ret = 0;
 531    }
 532    if (ret != 0) {
 533        return ret;
 534    }
 535
 536    if (entry == NULL) {
 537        entry = xtensa_tlb_get_entry(env, dtlb, wi, ei);
 538    }
 539
 540    if (ring < mmu_idx) {
 541        return dtlb ?
 542            LOAD_STORE_PRIVILEGE_CAUSE :
 543            INST_FETCH_PRIVILEGE_CAUSE;
 544    }
 545
 546    *access = mmu_attr_to_access(entry->attr) &
 547        ~(dtlb ? PAGE_EXEC : PAGE_READ | PAGE_WRITE);
 548    if (!is_access_granted(*access, is_write)) {
 549        return dtlb ?
 550            (is_write ?
 551             STORE_PROHIBITED_CAUSE :
 552             LOAD_PROHIBITED_CAUSE) :
 553            INST_FETCH_PROHIBITED_CAUSE;
 554    }
 555
 556    *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, wi));
 557    *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1;
 558
 559    return 0;
 560}
 561
 562static int get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte)
 563{
 564    CPUState *cs = CPU(xtensa_env_get_cpu(env));
 565    uint32_t paddr;
 566    uint32_t page_size;
 567    unsigned access;
 568    uint32_t pt_vaddr =
 569        (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc;
 570    int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0,
 571            &paddr, &page_size, &access, false);
 572
 573    qemu_log_mask(CPU_LOG_MMU, "%s: trying autorefill(%08x) -> %08x\n",
 574                  __func__, vaddr, ret ? ~0 : paddr);
 575
 576    if (ret == 0) {
 577        *pte = ldl_phys(cs->as, paddr);
 578    }
 579    return ret;
 580}
 581
 582static int get_physical_addr_region(CPUXtensaState *env,
 583        uint32_t vaddr, int is_write, int mmu_idx,
 584        uint32_t *paddr, uint32_t *page_size, unsigned *access)
 585{
 586    bool dtlb = is_write != 2;
 587    uint32_t wi = 0;
 588    uint32_t ei = (vaddr >> 29) & 0x7;
 589    const xtensa_tlb_entry *entry =
 590        xtensa_tlb_get_entry(env, dtlb, wi, ei);
 591
 592    *access = region_attr_to_access(entry->attr);
 593    if (!is_access_granted(*access, is_write)) {
 594        return dtlb ?
 595            (is_write ?
 596             STORE_PROHIBITED_CAUSE :
 597             LOAD_PROHIBITED_CAUSE) :
 598            INST_FETCH_PROHIBITED_CAUSE;
 599    }
 600
 601    *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK);
 602    *page_size = ~REGION_PAGE_MASK + 1;
 603
 604    return 0;
 605}
 606
 607/*!
 608 * Convert virtual address to physical addr.
 609 * MMU may issue pagewalk and change xtensa autorefill TLB way entry.
 610 *
 611 * \return 0 if ok, exception cause code otherwise
 612 */
 613int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb,
 614        uint32_t vaddr, int is_write, int mmu_idx,
 615        uint32_t *paddr, uint32_t *page_size, unsigned *access)
 616{
 617    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
 618        return get_physical_addr_mmu(env, update_tlb,
 619                vaddr, is_write, mmu_idx, paddr, page_size, access, true);
 620    } else if (xtensa_option_bits_enabled(env->config,
 621                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
 622                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) {
 623        return get_physical_addr_region(env, vaddr, is_write, mmu_idx,
 624                paddr, page_size, access);
 625    } else {
 626        *paddr = vaddr;
 627        *page_size = TARGET_PAGE_SIZE;
 628        *access = cacheattr_attr_to_access(
 629                env->sregs[CACHEATTR] >> ((vaddr & 0xe0000000) >> 27));
 630        return 0;
 631    }
 632}
 633
 634static void dump_tlb(FILE *f, fprintf_function cpu_fprintf,
 635        CPUXtensaState *env, bool dtlb)
 636{
 637    unsigned wi, ei;
 638    const xtensa_tlb *conf =
 639        dtlb ? &env->config->dtlb : &env->config->itlb;
 640    unsigned (*attr_to_access)(uint32_t) =
 641        xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ?
 642        mmu_attr_to_access : region_attr_to_access;
 643
 644    for (wi = 0; wi < conf->nways; ++wi) {
 645        uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1;
 646        const char *sz_text;
 647        bool print_header = true;
 648
 649        if (sz >= 0x100000) {
 650            sz >>= 20;
 651            sz_text = "MB";
 652        } else {
 653            sz >>= 10;
 654            sz_text = "KB";
 655        }
 656
 657        for (ei = 0; ei < conf->way_size[wi]; ++ei) {
 658            const xtensa_tlb_entry *entry =
 659                xtensa_tlb_get_entry(env, dtlb, wi, ei);
 660
 661            if (entry->asid) {
 662                static const char * const cache_text[8] = {
 663                    [PAGE_CACHE_BYPASS >> PAGE_CACHE_SHIFT] = "Bypass",
 664                    [PAGE_CACHE_WT >> PAGE_CACHE_SHIFT] = "WT",
 665                    [PAGE_CACHE_WB >> PAGE_CACHE_SHIFT] = "WB",
 666                    [PAGE_CACHE_ISOLATE >> PAGE_CACHE_SHIFT] = "Isolate",
 667                };
 668                unsigned access = attr_to_access(entry->attr);
 669                unsigned cache_idx = (access & PAGE_CACHE_MASK) >>
 670                    PAGE_CACHE_SHIFT;
 671
 672                if (print_header) {
 673                    print_header = false;
 674                    cpu_fprintf(f, "Way %u (%d %s)\n", wi, sz, sz_text);
 675                    cpu_fprintf(f,
 676                            "\tVaddr       Paddr       ASID  Attr RWX Cache\n"
 677                            "\t----------  ----------  ----  ---- --- -------\n");
 678                }
 679                cpu_fprintf(f,
 680                        "\t0x%08x  0x%08x  0x%02x  0x%02x %c%c%c %-7s\n",
 681                        entry->vaddr,
 682                        entry->paddr,
 683                        entry->asid,
 684                        entry->attr,
 685                        (access & PAGE_READ) ? 'R' : '-',
 686                        (access & PAGE_WRITE) ? 'W' : '-',
 687                        (access & PAGE_EXEC) ? 'X' : '-',
 688                        cache_text[cache_idx] ? cache_text[cache_idx] :
 689                            "Invalid");
 690            }
 691        }
 692    }
 693}
 694
 695void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env)
 696{
 697    if (xtensa_option_bits_enabled(env->config,
 698                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
 699                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) |
 700                XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) {
 701
 702        cpu_fprintf(f, "ITLB:\n");
 703        dump_tlb(f, cpu_fprintf, env, false);
 704        cpu_fprintf(f, "\nDTLB:\n");
 705        dump_tlb(f, cpu_fprintf, env, true);
 706    } else {
 707        cpu_fprintf(f, "No TLB for this CPU core\n");
 708    }
 709}
 710
 711void xtensa_runstall(CPUXtensaState *env, bool runstall)
 712{
 713    CPUState *cpu = CPU(xtensa_env_get_cpu(env));
 714
 715    env->runstall = runstall;
 716    cpu->halted = runstall;
 717    if (runstall) {
 718        cpu_interrupt(cpu, CPU_INTERRUPT_HALT);
 719    } else {
 720        cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
 721    }
 722}
 723