qemu/target/unicore32/softmmu.c
<<
>>
Prefs
   1/*
   2 * Softmmu related functions
   3 *
   4 * Copyright (C) 2010-2012 Guan Xuetao
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation, or any later version.
   9 * See the COPYING file in the top-level directory.
  10 */
  11#ifdef CONFIG_USER_ONLY
  12#error This file only exist under softmmu circumstance
  13#endif
  14
  15#include "qemu/osdep.h"
  16#include "cpu.h"
  17#include "exec/exec-all.h"
  18#include "qemu/error-report.h"
  19
  20#undef DEBUG_UC32
  21
  22#ifdef DEBUG_UC32
  23#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__)
  24#else
  25#define DPRINTF(fmt, ...) do {} while (0)
  26#endif
  27
  28#define SUPERPAGE_SIZE             (1 << 22)
  29#define UC32_PAGETABLE_READ        (1 << 8)
  30#define UC32_PAGETABLE_WRITE       (1 << 7)
  31#define UC32_PAGETABLE_EXEC        (1 << 6)
  32#define UC32_PAGETABLE_EXIST       (1 << 2)
  33#define PAGETABLE_TYPE(x)          ((x) & 3)
  34
  35
  36/* Map CPU modes onto saved register banks.  */
  37static inline int bank_number(CPUUniCore32State *env, int mode)
  38{
  39    UniCore32CPU *cpu = uc32_env_get_cpu(env);
  40
  41    switch (mode) {
  42    case ASR_MODE_USER:
  43    case ASR_MODE_SUSR:
  44        return 0;
  45    case ASR_MODE_PRIV:
  46        return 1;
  47    case ASR_MODE_TRAP:
  48        return 2;
  49    case ASR_MODE_EXTN:
  50        return 3;
  51    case ASR_MODE_INTR:
  52        return 4;
  53    }
  54    cpu_abort(CPU(cpu), "Bad mode %x\n", mode);
  55    return -1;
  56}
  57
  58void switch_mode(CPUUniCore32State *env, int mode)
  59{
  60    int old_mode;
  61    int i;
  62
  63    old_mode = env->uncached_asr & ASR_M;
  64    if (mode == old_mode) {
  65        return;
  66    }
  67
  68    i = bank_number(env, old_mode);
  69    env->banked_r29[i] = env->regs[29];
  70    env->banked_r30[i] = env->regs[30];
  71    env->banked_bsr[i] = env->bsr;
  72
  73    i = bank_number(env, mode);
  74    env->regs[29] = env->banked_r29[i];
  75    env->regs[30] = env->banked_r30[i];
  76    env->bsr = env->banked_bsr[i];
  77}
  78
  79/* Handle a CPU exception.  */
  80void uc32_cpu_do_interrupt(CPUState *cs)
  81{
  82    UniCore32CPU *cpu = UNICORE32_CPU(cs);
  83    CPUUniCore32State *env = &cpu->env;
  84    uint32_t addr;
  85    int new_mode;
  86
  87    switch (cs->exception_index) {
  88    case UC32_EXCP_PRIV:
  89        new_mode = ASR_MODE_PRIV;
  90        addr = 0x08;
  91        break;
  92    case UC32_EXCP_ITRAP:
  93        DPRINTF("itrap happened at %x\n", env->regs[31]);
  94        new_mode = ASR_MODE_TRAP;
  95        addr = 0x0c;
  96        break;
  97    case UC32_EXCP_DTRAP:
  98        DPRINTF("dtrap happened at %x\n", env->regs[31]);
  99        new_mode = ASR_MODE_TRAP;
 100        addr = 0x10;
 101        break;
 102    case UC32_EXCP_INTR:
 103        new_mode = ASR_MODE_INTR;
 104        addr = 0x18;
 105        break;
 106    default:
 107        cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
 108        return;
 109    }
 110    /* High vectors.  */
 111    if (env->cp0.c1_sys & (1 << 13)) {
 112        addr += 0xffff0000;
 113    }
 114
 115    switch_mode(env, new_mode);
 116    env->bsr = cpu_asr_read(env);
 117    env->uncached_asr = (env->uncached_asr & ~ASR_M) | new_mode;
 118    env->uncached_asr |= ASR_I;
 119    /* The PC already points to the proper instruction.  */
 120    env->regs[30] = env->regs[31];
 121    env->regs[31] = addr;
 122    cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
 123}
 124
 125static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address,
 126        int access_type, int is_user, uint32_t *phys_ptr, int *prot,
 127        target_ulong *page_size)
 128{
 129    UniCore32CPU *cpu = uc32_env_get_cpu(env);
 130    CPUState *cs = CPU(cpu);
 131    int code;
 132    uint32_t table;
 133    uint32_t desc;
 134    uint32_t phys_addr;
 135
 136    /* Pagetable walk.  */
 137    /* Lookup l1 descriptor.  */
 138    table = env->cp0.c2_base & 0xfffff000;
 139    table |= (address >> 20) & 0xffc;
 140    desc = ldl_phys(cs->as, table);
 141    code = 0;
 142    switch (PAGETABLE_TYPE(desc)) {
 143    case 3:
 144        /* Superpage  */
 145        if (!(desc & UC32_PAGETABLE_EXIST)) {
 146            code = 0x0b; /* superpage miss */
 147            goto do_fault;
 148        }
 149        phys_addr = (desc & 0xffc00000) | (address & 0x003fffff);
 150        *page_size = SUPERPAGE_SIZE;
 151        break;
 152    case 0:
 153        /* Lookup l2 entry.  */
 154        if (is_user) {
 155            DPRINTF("PGD address %x, desc %x\n", table, desc);
 156        }
 157        if (!(desc & UC32_PAGETABLE_EXIST)) {
 158            code = 0x05; /* second pagetable miss */
 159            goto do_fault;
 160        }
 161        table = (desc & 0xfffff000) | ((address >> 10) & 0xffc);
 162        desc = ldl_phys(cs->as, table);
 163        /* 4k page.  */
 164        if (is_user) {
 165            DPRINTF("PTE address %x, desc %x\n", table, desc);
 166        }
 167        if (!(desc & UC32_PAGETABLE_EXIST)) {
 168            code = 0x08; /* page miss */
 169            goto do_fault;
 170        }
 171        switch (PAGETABLE_TYPE(desc)) {
 172        case 0:
 173            phys_addr = (desc & 0xfffff000) | (address & 0xfff);
 174            *page_size = TARGET_PAGE_SIZE;
 175            break;
 176        default:
 177            cpu_abort(CPU(cpu), "wrong page type!");
 178        }
 179        break;
 180    default:
 181        cpu_abort(CPU(cpu), "wrong page type!");
 182    }
 183
 184    *phys_ptr = phys_addr;
 185    *prot = 0;
 186    /* Check access permissions.  */
 187    if (desc & UC32_PAGETABLE_READ) {
 188        *prot |= PAGE_READ;
 189    } else {
 190        if (is_user && (access_type == 0)) {
 191            code = 0x11; /* access unreadable area */
 192            goto do_fault;
 193        }
 194    }
 195
 196    if (desc & UC32_PAGETABLE_WRITE) {
 197        *prot |= PAGE_WRITE;
 198    } else {
 199        if (is_user && (access_type == 1)) {
 200            code = 0x12; /* access unwritable area */
 201            goto do_fault;
 202        }
 203    }
 204
 205    if (desc & UC32_PAGETABLE_EXEC) {
 206        *prot |= PAGE_EXEC;
 207    } else {
 208        if (is_user && (access_type == 2)) {
 209            code = 0x13; /* access unexecutable area */
 210            goto do_fault;
 211        }
 212    }
 213
 214do_fault:
 215    return code;
 216}
 217
 218int uc32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
 219                              int access_type, int mmu_idx)
 220{
 221    UniCore32CPU *cpu = UNICORE32_CPU(cs);
 222    CPUUniCore32State *env = &cpu->env;
 223    uint32_t phys_addr;
 224    target_ulong page_size;
 225    int prot;
 226    int ret, is_user;
 227
 228    ret = 1;
 229    is_user = mmu_idx == MMU_USER_IDX;
 230
 231    if ((env->cp0.c1_sys & 1) == 0) {
 232        /* MMU disabled.  */
 233        phys_addr = address;
 234        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 235        page_size = TARGET_PAGE_SIZE;
 236        ret = 0;
 237    } else {
 238        if ((address & (1 << 31)) || (is_user)) {
 239            ret = get_phys_addr_ucv2(env, address, access_type, is_user,
 240                                    &phys_addr, &prot, &page_size);
 241            if (is_user) {
 242                DPRINTF("user space access: ret %x, address %" VADDR_PRIx ", "
 243                        "access_type %x, phys_addr %x, prot %x\n",
 244                        ret, address, access_type, phys_addr, prot);
 245            }
 246        } else {
 247            /*IO memory */
 248            phys_addr = address | (1 << 31);
 249            prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 250            page_size = TARGET_PAGE_SIZE;
 251            ret = 0;
 252        }
 253    }
 254
 255    if (ret == 0) {
 256        /* Map a single page.  */
 257        phys_addr &= TARGET_PAGE_MASK;
 258        address &= TARGET_PAGE_MASK;
 259        tlb_set_page(cs, address, phys_addr, prot, mmu_idx, page_size);
 260        return 0;
 261    }
 262
 263    env->cp0.c3_faultstatus = ret;
 264    env->cp0.c4_faultaddr = address;
 265    if (access_type == 2) {
 266        cs->exception_index = UC32_EXCP_ITRAP;
 267    } else {
 268        cs->exception_index = UC32_EXCP_DTRAP;
 269    }
 270    return ret;
 271}
 272
 273hwaddr uc32_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 274{
 275    error_report("function uc32_cpu_get_phys_page_debug not "
 276                    "implemented, aborting");
 277    return -1;
 278}
 279