helper.c 30.2 KB
Newer Older
B
bellard 已提交
1 2 3 4 5 6 7
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cpu.h"
#include "exec-all.h"

8 9 10 11 12 13 14 15 16 17 18 19
static inline void set_feature(CPUARMState *env, int feature)
{
    env->features |= 1u << feature;
}

static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
{
    env->cp15.c0_cpuid = id;
    switch (id) {
    case ARM_CPUID_ARM926:
        set_feature(env, ARM_FEATURE_VFP);
        env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
20
        env->cp15.c0_cachetype = 0x1dd20d2;
21
        env->cp15.c1_sys = 0x00090078;
22
        break;
P
pbrook 已提交
23 24 25
    case ARM_CPUID_ARM946:
        set_feature(env, ARM_FEATURE_MPU);
        env->cp15.c0_cachetype = 0x0f004006;
26
        env->cp15.c1_sys = 0x00000078;
P
pbrook 已提交
27
        break;
28 29 30 31
    case ARM_CPUID_ARM1026:
        set_feature(env, ARM_FEATURE_VFP);
        set_feature(env, ARM_FEATURE_AUXCR);
        env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
32
        env->cp15.c0_cachetype = 0x1dd20d2;
33
        env->cp15.c1_sys = 0x00090078;
34
        break;
35 36 37 38 39 40 41 42 43
    case ARM_CPUID_TI915T:
    case ARM_CPUID_TI925T:
        set_feature(env, ARM_FEATURE_OMAPCP);
        env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
        env->cp15.c0_cachetype = 0x5109149;
        env->cp15.c1_sys = 0x00000070;
        env->cp15.c15_i_max = 0x000;
        env->cp15.c15_i_min = 0xff0;
        break;
44 45 46 47 48 49 50 51
    case ARM_CPUID_PXA250:
    case ARM_CPUID_PXA255:
    case ARM_CPUID_PXA260:
    case ARM_CPUID_PXA261:
    case ARM_CPUID_PXA262:
        set_feature(env, ARM_FEATURE_XSCALE);
        /* JTAG_ID is ((id << 28) | 0x09265013) */
        env->cp15.c0_cachetype = 0xd172172;
52
        env->cp15.c1_sys = 0x00000078;
53 54 55 56 57 58 59 60 61
        break;
    case ARM_CPUID_PXA270_A0:
    case ARM_CPUID_PXA270_A1:
    case ARM_CPUID_PXA270_B0:
    case ARM_CPUID_PXA270_B1:
    case ARM_CPUID_PXA270_C0:
    case ARM_CPUID_PXA270_C5:
        set_feature(env, ARM_FEATURE_XSCALE);
        /* JTAG_ID is ((id << 28) | 0x09265013) */
62 63
        set_feature(env, ARM_FEATURE_IWMMXT);
        env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q';
64
        env->cp15.c0_cachetype = 0xd172172;
65
        env->cp15.c1_sys = 0x00000078;
66 67 68 69 70 71 72
        break;
    default:
        cpu_abort(env, "Bad CPU ID: %x\n", id);
        break;
    }
}

P
pbrook 已提交
73 74
void cpu_reset(CPUARMState *env)
{
75 76 77 78 79
    uint32_t id;
    id = env->cp15.c0_cpuid;
    memset(env, 0, offsetof(CPUARMState, breakpoints));
    if (id)
        cpu_reset_model_id(env, id);
P
pbrook 已提交
80 81 82 83 84 85 86 87 88
#if defined (CONFIG_USER_ONLY)
    env->uncached_cpsr = ARM_CPU_MODE_USR;
    env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30;
#else
    /* SVC mode with interrupts disabled.  */
    env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
    env->vfp.xregs[ARM_VFP_FPEXC] = 0;
#endif
    env->regs[15] = 0;
89
    tlb_flush(env, 1);
P
pbrook 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102 103
}

CPUARMState *cpu_arm_init(void)
{
    CPUARMState *env;

    env = qemu_mallocz(sizeof(CPUARMState));
    if (!env)
        return NULL;
    cpu_exec_init(env);
    cpu_reset(env);
    return env;
}

P
pbrook 已提交
104 105 106 107 108 109 110
struct arm_cpu_t {
    uint32_t id;
    const char *name;
};

static const struct arm_cpu_t arm_cpu_names[] = {
    { ARM_CPUID_ARM926, "arm926"},
P
pbrook 已提交
111
    { ARM_CPUID_ARM946, "arm946"},
P
pbrook 已提交
112
    { ARM_CPUID_ARM1026, "arm1026"},
113
    { ARM_CPUID_TI925T, "ti925t" },
114 115 116 117 118 119 120 121 122 123 124 125
    { ARM_CPUID_PXA250, "pxa250" },
    { ARM_CPUID_PXA255, "pxa255" },
    { ARM_CPUID_PXA260, "pxa260" },
    { ARM_CPUID_PXA261, "pxa261" },
    { ARM_CPUID_PXA262, "pxa262" },
    { ARM_CPUID_PXA270, "pxa270" },
    { ARM_CPUID_PXA270_A0, "pxa270-a0" },
    { ARM_CPUID_PXA270_A1, "pxa270-a1" },
    { ARM_CPUID_PXA270_B0, "pxa270-b0" },
    { ARM_CPUID_PXA270_B1, "pxa270-b1" },
    { ARM_CPUID_PXA270_C0, "pxa270-c0" },
    { ARM_CPUID_PXA270_C5, "pxa270-c5" },
P
pbrook 已提交
126 127 128
    { 0, NULL}
};

J
j_mayer 已提交
129
void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
P
pbrook 已提交
130 131 132
{
    int i;

J
j_mayer 已提交
133
    (*cpu_fprintf)(f, "Available CPUs:\n");
P
pbrook 已提交
134
    for (i = 0; arm_cpu_names[i].name; i++) {
J
j_mayer 已提交
135
        (*cpu_fprintf)(f, "  %s\n", arm_cpu_names[i].name);
P
pbrook 已提交
136 137 138
    }
}

P
pbrook 已提交
139
void cpu_arm_set_model(CPUARMState *env, const char *name)
P
pbrook 已提交
140
{
P
pbrook 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    int i;
    uint32_t id;

    id = 0;
    i = 0;
    for (i = 0; arm_cpu_names[i].name; i++) {
        if (strcmp(name, arm_cpu_names[i].name) == 0) {
            id = arm_cpu_names[i].id;
            break;
        }
    }
    if (!id) {
        cpu_abort(env, "Unknown CPU '%s'", name);
        return;
    }
156
    cpu_reset_model_id(env, id);
P
pbrook 已提交
157 158 159 160 161 162 163
}

void cpu_arm_close(CPUARMState *env)
{
    free(env);
}

164
#if defined(CONFIG_USER_ONLY)
B
bellard 已提交
165 166 167 168 169 170 171

void do_interrupt (CPUState *env)
{
    env->exception_index = -1;
}

int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
172
                              int mmu_idx, int is_softmmu)
B
bellard 已提交
173 174 175 176 177 178 179 180 181 182 183
{
    if (rw == 2) {
        env->exception_index = EXCP_PREFETCH_ABORT;
        env->cp15.c6_insn = address;
    } else {
        env->exception_index = EXCP_DATA_ABORT;
        env->cp15.c6_data = address;
    }
    return 1;
}

184
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
B
bellard 已提交
185 186 187 188 189
{
    return addr;
}

/* These should probably raise undefined insn exceptions.  */
190 191 192 193 194 195 196 197 198 199 200 201 202 203
void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val)
{
    int op1 = (insn >> 8) & 0xf;
    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
    return;
}

uint32_t helper_get_cp(CPUState *env, uint32_t insn)
{
    int op1 = (insn >> 8) & 0xf;
    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
    return 0;
}

B
bellard 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
{
    cpu_abort(env, "cp15 insn %08x\n", insn);
}

uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
{
    cpu_abort(env, "cp15 insn %08x\n", insn);
    return 0;
}

void switch_mode(CPUState *env, int mode)
{
    if (mode != ARM_CPU_MODE_USR)
        cpu_abort(env, "Tried to switch out of user mode\n");
}

#else

223 224
extern int semihosting_enabled;

B
bellard 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
/* Map CPU modes onto saved register banks.  */
static inline int bank_number (int mode)
{
    switch (mode) {
    case ARM_CPU_MODE_USR:
    case ARM_CPU_MODE_SYS:
        return 0;
    case ARM_CPU_MODE_SVC:
        return 1;
    case ARM_CPU_MODE_ABT:
        return 2;
    case ARM_CPU_MODE_UND:
        return 3;
    case ARM_CPU_MODE_IRQ:
        return 4;
    case ARM_CPU_MODE_FIQ:
        return 5;
    }
    cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
    return -1;
}

void switch_mode(CPUState *env, int mode)
{
    int old_mode;
    int i;

    old_mode = env->uncached_cpsr & CPSR_M;
    if (mode == old_mode)
        return;

    if (old_mode == ARM_CPU_MODE_FIQ) {
        memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
P
pbrook 已提交
258
        memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
B
bellard 已提交
259 260
    } else if (mode == ARM_CPU_MODE_FIQ) {
        memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
P
pbrook 已提交
261
        memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
B
bellard 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    }

    i = bank_number(old_mode);
    env->banked_r13[i] = env->regs[13];
    env->banked_r14[i] = env->regs[14];
    env->banked_spsr[i] = env->spsr;

    i = bank_number(mode);
    env->regs[13] = env->banked_r13[i];
    env->regs[14] = env->banked_r14[i];
    env->spsr = env->banked_spsr[i];
}

/* Handle a CPU exception.  */
void do_interrupt(CPUARMState *env)
{
    uint32_t addr;
    uint32_t mask;
    int new_mode;
    uint32_t offset;

    /* TODO: Vectored interrupt controller.  */
    switch (env->exception_index) {
    case EXCP_UDEF:
        new_mode = ARM_CPU_MODE_UND;
        addr = 0x04;
        mask = CPSR_I;
        if (env->thumb)
            offset = 2;
        else
            offset = 4;
        break;
    case EXCP_SWI:
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
        if (semihosting_enabled) {
            /* Check for semihosting interrupt.  */
            if (env->thumb) {
                mask = lduw_code(env->regs[15] - 2) & 0xff;
            } else {
                mask = ldl_code(env->regs[15] - 4) & 0xffffff;
            }
            /* Only intercept calls from privileged modes, to provide some
               semblance of security.  */
            if (((mask == 0x123456 && !env->thumb)
                    || (mask == 0xab && env->thumb))
                  && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
                env->regs[0] = do_arm_semihosting(env);
                return;
            }
        }
B
bellard 已提交
311 312 313 314 315 316 317
        new_mode = ARM_CPU_MODE_SVC;
        addr = 0x08;
        mask = CPSR_I;
        /* The PC already points to the next instructon.  */
        offset = 0;
        break;
    case EXCP_PREFETCH_ABORT:
P
pbrook 已提交
318
    case EXCP_BKPT:
B
bellard 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
        new_mode = ARM_CPU_MODE_ABT;
        addr = 0x0c;
        mask = CPSR_A | CPSR_I;
        offset = 4;
        break;
    case EXCP_DATA_ABORT:
        new_mode = ARM_CPU_MODE_ABT;
        addr = 0x10;
        mask = CPSR_A | CPSR_I;
        offset = 8;
        break;
    case EXCP_IRQ:
        new_mode = ARM_CPU_MODE_IRQ;
        addr = 0x18;
        /* Disable IRQ and imprecise data aborts.  */
        mask = CPSR_A | CPSR_I;
        offset = 4;
        break;
    case EXCP_FIQ:
        new_mode = ARM_CPU_MODE_FIQ;
        addr = 0x1c;
        /* Disable FIQ, IRQ and imprecise data aborts.  */
        mask = CPSR_A | CPSR_I | CPSR_F;
        offset = 4;
        break;
    default:
        cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
        return; /* Never happens.  Keep compiler happy.  */
    }
    /* High vectors.  */
    if (env->cp15.c1_sys & (1 << 13)) {
        addr += 0xffff0000;
    }
    switch_mode (env, new_mode);
    env->spsr = cpsr_read(env);
354
    /* Switch to the new mode, and switch to Arm mode.  */
B
bellard 已提交
355
    /* ??? Thumb interrupt handlers not implemented.  */
356
    env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
B
bellard 已提交
357
    env->uncached_cpsr |= mask;
358
    env->thumb = 0;
B
bellard 已提交
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
    env->regs[14] = env->regs[15] + offset;
    env->regs[15] = addr;
    env->interrupt_request |= CPU_INTERRUPT_EXITTB;
}

/* Check section/page access permissions.
   Returns the page protection flags, or zero if the access is not
   permitted.  */
static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
                           int is_user)
{
  if (domain == 3)
    return PAGE_READ | PAGE_WRITE;

  switch (ap) {
  case 0:
P
pbrook 已提交
375
      if (access_type == 1)
B
bellard 已提交
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
          return 0;
      switch ((env->cp15.c1_sys >> 8) & 3) {
      case 1:
          return is_user ? 0 : PAGE_READ;
      case 2:
          return PAGE_READ;
      default:
          return 0;
      }
  case 1:
      return is_user ? 0 : PAGE_READ | PAGE_WRITE;
  case 2:
      if (is_user)
          return (access_type == 1) ? 0 : PAGE_READ;
      else
          return PAGE_READ | PAGE_WRITE;
  case 3:
      return PAGE_READ | PAGE_WRITE;
  default:
      abort();
  }
}

static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
                         int is_user, uint32_t *phys_ptr, int *prot)
{
    int code;
    uint32_t table;
    uint32_t desc;
    int type;
    int ap;
    int domain;
    uint32_t phys_addr;

    /* Fast Context Switch Extension.  */
    if (address < 0x02000000)
        address += env->cp15.c13_fcse;

    if ((env->cp15.c1_sys & 1) == 0) {
P
pbrook 已提交
415
        /* MMU/MPU disabled.  */
B
bellard 已提交
416 417
        *phys_ptr = address;
        *prot = PAGE_READ | PAGE_WRITE;
P
pbrook 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    } else if (arm_feature(env, ARM_FEATURE_MPU)) {
        int n;
        uint32_t mask;
        uint32_t base;

        *phys_ptr = address;
        for (n = 7; n >= 0; n--) {
            base = env->cp15.c6_region[n];
            if ((base & 1) == 0)
                continue;
            mask = 1 << ((base >> 1) & 0x1f);
            /* Keep this shift separate from the above to avoid an
               (undefined) << 32.  */
            mask = (mask << 1) - 1;
            if (((base ^ address) & ~mask) == 0)
                break;
        }
        if (n < 0)
            return 2;

        if (access_type == 2) {
            mask = env->cp15.c5_insn;
        } else {
            mask = env->cp15.c5_data;
        }
        mask = (mask >> (n * 4)) & 0xf;
        switch (mask) {
        case 0:
            return 1;
        case 1:
            if (is_user)
              return 1;
            *prot = PAGE_READ | PAGE_WRITE;
            break;
        case 2:
            *prot = PAGE_READ;
            if (!is_user)
                *prot |= PAGE_WRITE;
            break;
        case 3:
            *prot = PAGE_READ | PAGE_WRITE;
            break;
        case 5:
            if (is_user)
                return 1;
            *prot = PAGE_READ;
            break;
        case 6:
            *prot = PAGE_READ;
            break;
        default:
            /* Bad permission.  */
            return 1;
        }
B
bellard 已提交
472 473 474
    } else {
        /* Pagetable walk.  */
        /* Lookup l1 descriptor.  */
P
pbrook 已提交
475
        table = (env->cp15.c2_base & 0xffffc000) | ((address >> 18) & 0x3ffc);
B
bellard 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
        desc = ldl_phys(table);
        type = (desc & 3);
        domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
        if (type == 0) {
            /* Secton translation fault.  */
            code = 5;
            goto do_fault;
        }
        if (domain == 0 || domain == 2) {
            if (type == 2)
                code = 9; /* Section domain fault.  */
            else
                code = 11; /* Page domain fault.  */
            goto do_fault;
        }
        if (type == 2) {
            /* 1Mb section.  */
            phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
            ap = (desc >> 10) & 3;
            code = 13;
        } else {
            /* Lookup l2 entry.  */
P
pbrook 已提交
498 499 500 501 502 503 504
            if (type == 1) {
                /* Coarse pagetable.  */
                table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
            } else {
                /* Fine pagetable.  */
                table = (desc & 0xfffff000) | ((address >> 8) & 0xffc);
            }
B
bellard 已提交
505 506 507 508 509 510 511 512 513 514 515 516 517 518
            desc = ldl_phys(table);
            switch (desc & 3) {
            case 0: /* Page translation fault.  */
                code = 7;
                goto do_fault;
            case 1: /* 64k page.  */
                phys_addr = (desc & 0xffff0000) | (address & 0xffff);
                ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
                break;
            case 2: /* 4k page.  */
                phys_addr = (desc & 0xfffff000) | (address & 0xfff);
                ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
                break;
            case 3: /* 1k page.  */
B
balrog 已提交
519 520 521 522
                if (type == 1) {
                    if (arm_feature(env, ARM_FEATURE_XSCALE))
                        phys_addr = (desc & 0xfffff000) | (address & 0xfff);
                    else {
523 524 525 526
                        /* Page translation fault.  */
                        code = 7;
                        goto do_fault;
                    }
B
balrog 已提交
527
                } else
528
                    phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
B
bellard 已提交
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
                ap = (desc >> 4) & 3;
                break;
            default:
                /* Never happens, but compiler isn't smart enough to tell.  */
                abort();
            }
            code = 15;
        }
        *prot = check_ap(env, ap, domain, access_type, is_user);
        if (!*prot) {
            /* Access permission fault.  */
            goto do_fault;
        }
        *phys_ptr = phys_addr;
    }
    return 0;
do_fault:
    return code | (domain << 4);
}

int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
550
                              int access_type, int mmu_idx, int is_softmmu)
B
bellard 已提交
551 552 553
{
    uint32_t phys_addr;
    int prot;
554
    int ret, is_user;
B
bellard 已提交
555

556
    is_user = mmu_idx == MMU_USER_IDX;
B
bellard 已提交
557 558 559 560 561
    ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
    if (ret == 0) {
        /* Map a single [sub]page.  */
        phys_addr &= ~(uint32_t)0x3ff;
        address &= ~(uint32_t)0x3ff;
562
        return tlb_set_page (env, address, phys_addr, prot, mmu_idx,
B
bellard 已提交
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
                             is_softmmu);
    }

    if (access_type == 2) {
        env->cp15.c5_insn = ret;
        env->cp15.c6_insn = address;
        env->exception_index = EXCP_PREFETCH_ABORT;
    } else {
        env->cp15.c5_data = ret;
        env->cp15.c6_data = address;
        env->exception_index = EXCP_DATA_ABORT;
    }
    return 1;
}

578
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
B
bellard 已提交
579 580 581 582 583 584 585 586 587 588 589 590 591
{
    uint32_t phys_addr;
    int prot;
    int ret;

    ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);

    if (ret != 0)
        return -1;

    return phys_addr;
}

592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val)
{
    int cp_num = (insn >> 8) & 0xf;
    int cp_info = (insn >> 5) & 7;
    int src = (insn >> 16) & 0xf;
    int operand = insn & 0xf;

    if (env->cp[cp_num].cp_write)
        env->cp[cp_num].cp_write(env->cp[cp_num].opaque,
                                 cp_info, src, operand, val);
}

uint32_t helper_get_cp(CPUState *env, uint32_t insn)
{
    int cp_num = (insn >> 8) & 0xf;
    int cp_info = (insn >> 5) & 7;
    int dest = (insn >> 16) & 0xf;
    int operand = insn & 0xf;

    if (env->cp[cp_num].cp_read)
        return env->cp[cp_num].cp_read(env->cp[cp_num].opaque,
                                       cp_info, dest, operand);
    return 0;
}

P
pbrook 已提交
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
/* Return basic MPU access permission bits.  */
static uint32_t simple_mpu_ap_bits(uint32_t val)
{
    uint32_t ret;
    uint32_t mask;
    int i;
    ret = 0;
    mask = 3;
    for (i = 0; i < 16; i += 2) {
        ret |= (val >> i) & mask;
        mask <<= 2;
    }
    return ret;
}

/* Pad basic MPU access permission bits to extended format.  */
static uint32_t extended_mpu_ap_bits(uint32_t val)
{
    uint32_t ret;
    uint32_t mask;
    int i;
    ret = 0;
    mask = 3;
    for (i = 0; i < 16; i += 2) {
        ret |= (val & mask) << i;
        mask <<= 2;
    }
    return ret;
}

B
bellard 已提交
647 648 649
void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
{
    uint32_t op2;
P
pbrook 已提交
650
    uint32_t crm;
B
bellard 已提交
651 652

    op2 = (insn >> 5) & 7;
P
pbrook 已提交
653
    crm = insn & 0xf;
B
bellard 已提交
654 655
    switch ((insn >> 16) & 0xf) {
    case 0: /* ID codes.  */
656 657
        if (arm_feature(env, ARM_FEATURE_XSCALE))
            break;
658 659
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            break;
B
bellard 已提交
660 661
        goto bad_reg;
    case 1: /* System configuration.  */
662 663
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            op2 = 0;
B
bellard 已提交
664 665
        switch (op2) {
        case 0:
P
pbrook 已提交
666
            if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
667
                env->cp15.c1_sys = val;
B
bellard 已提交
668 669 670 671
            /* ??? Lots of these bits are not implemented.  */
            /* This may enable/disable the MMU, so do a TLB flush.  */
            tlb_flush(env, 1);
            break;
672
        case 1:
673 674
            if (arm_feature(env, ARM_FEATURE_XSCALE)) {
                env->cp15.c1_xscaleauxcr = val;
675
                break;
676
            }
677
            goto bad_reg;
B
bellard 已提交
678
        case 2:
679 680
            if (arm_feature(env, ARM_FEATURE_XSCALE))
                goto bad_reg;
B
bellard 已提交
681 682 683
            env->cp15.c1_coproc = val;
            /* ??? Is this safe when called from within a TB?  */
            tb_flush(env);
684
            break;
B
bellard 已提交
685 686 687 688
        default:
            goto bad_reg;
        }
        break;
P
pbrook 已提交
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    case 2: /* MMU Page table control / MPU cache control.  */
        if (arm_feature(env, ARM_FEATURE_MPU)) {
            switch (op2) {
            case 0:
                env->cp15.c2_data = val;
                break;
            case 1:
                env->cp15.c2_insn = val;
                break;
            default:
                goto bad_reg;
            }
        } else {
            env->cp15.c2_base = val;
        }
B
bellard 已提交
704
        break;
P
pbrook 已提交
705
    case 3: /* MMU Domain access control / MPU write buffer control.  */
B
bellard 已提交
706
        env->cp15.c3 = val;
707
        tlb_flush(env, 1); /* Flush TLB as domain not tracked in TLB */
B
bellard 已提交
708 709 710
        break;
    case 4: /* Reserved.  */
        goto bad_reg;
P
pbrook 已提交
711
    case 5: /* MMU Fault status / MPU access permission.  */
712 713
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            op2 = 0;
B
bellard 已提交
714 715
        switch (op2) {
        case 0:
P
pbrook 已提交
716 717
            if (arm_feature(env, ARM_FEATURE_MPU))
                val = extended_mpu_ap_bits(val);
B
bellard 已提交
718 719 720
            env->cp15.c5_data = val;
            break;
        case 1:
P
pbrook 已提交
721 722
            if (arm_feature(env, ARM_FEATURE_MPU))
                val = extended_mpu_ap_bits(val);
B
bellard 已提交
723 724
            env->cp15.c5_insn = val;
            break;
P
pbrook 已提交
725 726 727 728
        case 2:
            if (!arm_feature(env, ARM_FEATURE_MPU))
                goto bad_reg;
            env->cp15.c5_data = val;
B
bellard 已提交
729
            break;
P
pbrook 已提交
730 731 732 733
        case 3:
            if (!arm_feature(env, ARM_FEATURE_MPU))
                goto bad_reg;
            env->cp15.c5_insn = val;
B
bellard 已提交
734 735 736 737 738
            break;
        default:
            goto bad_reg;
        }
        break;
P
pbrook 已提交
739 740 741 742 743 744
    case 6: /* MMU Fault address / MPU base/size.  */
        if (arm_feature(env, ARM_FEATURE_MPU)) {
            if (crm >= 8)
                goto bad_reg;
            env->cp15.c6_region[crm] = val;
        } else {
745 746
            if (arm_feature(env, ARM_FEATURE_OMAPCP))
                op2 = 0;
P
pbrook 已提交
747 748 749 750 751 752 753 754 755 756 757 758
            switch (op2) {
            case 0:
                env->cp15.c6_data = val;
                break;
            case 1:
                env->cp15.c6_insn = val;
                break;
            default:
                goto bad_reg;
            }
        }
        break;
B
bellard 已提交
759
    case 7: /* Cache control.  */
760 761
        env->cp15.c15_i_max = 0x000;
        env->cp15.c15_i_min = 0xff0;
B
bellard 已提交
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
        /* No cache, so nothing to do.  */
        break;
    case 8: /* MMU TLB control.  */
        switch (op2) {
        case 0: /* Invalidate all.  */
            tlb_flush(env, 0);
            break;
        case 1: /* Invalidate single TLB entry.  */
#if 0
            /* ??? This is wrong for large pages and sections.  */
            /* As an ugly hack to make linux work we always flush a 4K
               pages.  */
            val &= 0xfffff000;
            tlb_flush_page(env, val);
            tlb_flush_page(env, val + 0x400);
            tlb_flush_page(env, val + 0x800);
            tlb_flush_page(env, val + 0xc00);
#else
            tlb_flush(env, 1);
#endif
            break;
        default:
            goto bad_reg;
        }
        break;
P
pbrook 已提交
787
    case 9:
788 789
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            break;
P
pbrook 已提交
790 791 792 793 794 795 796 797 798 799 800 801
        switch (crm) {
        case 0: /* Cache lockdown.  */
            switch (op2) {
            case 0:
                env->cp15.c9_data = val;
                break;
            case 1:
                env->cp15.c9_insn = val;
                break;
            default:
                goto bad_reg;
            }
B
bellard 已提交
802
            break;
P
pbrook 已提交
803 804 805
        case 1: /* TCM memory region registers.  */
            /* Not implemented.  */
            goto bad_reg;
B
bellard 已提交
806 807 808 809 810 811 812 813 814 815 816 817
        default:
            goto bad_reg;
        }
        break;
    case 10: /* MMU TLB lockdown.  */
        /* ??? TLB lockdown not implemented.  */
        break;
    case 12: /* Reserved.  */
        goto bad_reg;
    case 13: /* Process ID.  */
        switch (op2) {
        case 0:
818 819 820 821 822 823
            /* Unlike real hardware the qemu TLB uses virtual addresses,
               not modified virtual addresses, so this causes a TLB flush.
             */
            if (env->cp15.c13_fcse != val)
              tlb_flush(env, 1);
            env->cp15.c13_fcse = val;
B
bellard 已提交
824 825
            break;
        case 1:
826
            /* This changes the ASID, so do a TLB flush.  */
P
pbrook 已提交
827 828
            if (env->cp15.c13_context != val
                && !arm_feature(env, ARM_FEATURE_MPU))
829 830
              tlb_flush(env, 0);
            env->cp15.c13_context = val;
B
bellard 已提交
831 832 833 834 835 836 837 838
            break;
        default:
            goto bad_reg;
        }
        break;
    case 14: /* Reserved.  */
        goto bad_reg;
    case 15: /* Implementation specific.  */
839
        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
P
pbrook 已提交
840
            if (op2 == 0 && crm == 1) {
841 842 843 844 845
                if (env->cp15.c15_cpar != (val & 0x3fff)) {
                    /* Changes cp0 to cp13 behavior, so needs a TB flush.  */
                    tb_flush(env);
                    env->cp15.c15_cpar = val & 0x3fff;
                }
846 847 848 849
                break;
            }
            goto bad_reg;
        }
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
            switch (crm) {
            case 0:
                break;
            case 1: /* Set TI925T configuration.  */
                env->cp15.c15_ticonfig = val & 0xe7;
                env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */
                        ARM_CPUID_TI915T : ARM_CPUID_TI925T;
                break;
            case 2: /* Set I_max.  */
                env->cp15.c15_i_max = val;
                break;
            case 3: /* Set I_min.  */
                env->cp15.c15_i_min = val;
                break;
            case 4: /* Set thread-ID.  */
                env->cp15.c15_threadid = val & 0xffff;
                break;
            case 8: /* Wait-for-interrupt (deprecated).  */
                cpu_interrupt(env, CPU_INTERRUPT_HALT);
                break;
            default:
                goto bad_reg;
            }
        }
B
bellard 已提交
875 876 877 878 879
        break;
    }
    return;
bad_reg:
    /* ??? For debugging only.  Should raise illegal instruction exception.  */
880
    cpu_abort(env, "Unimplemented cp15 register write\n");
B
bellard 已提交
881 882 883 884 885
}

uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
{
    uint32_t op2;
886
    uint32_t crm;
B
bellard 已提交
887 888

    op2 = (insn >> 5) & 7;
889
    crm = insn & 0xf;
B
bellard 已提交
890 891 892 893
    switch ((insn >> 16) & 0xf) {
    case 0: /* ID codes.  */
        switch (op2) {
        default: /* Device ID.  */
P
pbrook 已提交
894
            return env->cp15.c0_cpuid;
B
bellard 已提交
895
        case 1: /* Cache Type.  */
896
            return env->cp15.c0_cachetype;
B
bellard 已提交
897
        case 2: /* TCM status.  */
898 899
            if (arm_feature(env, ARM_FEATURE_XSCALE))
                goto bad_reg;
B
bellard 已提交
900 901 902
            return 0;
        }
    case 1: /* System configuration.  */
903 904
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            op2 = 0;
B
bellard 已提交
905 906 907 908
        switch (op2) {
        case 0: /* Control register.  */
            return env->cp15.c1_sys;
        case 1: /* Auxiliary control register.  */
P
pbrook 已提交
909 910
            if (arm_feature(env, ARM_FEATURE_AUXCR))
                return 1;
911
            if (arm_feature(env, ARM_FEATURE_XSCALE))
912
                return env->cp15.c1_xscaleauxcr;
P
pbrook 已提交
913
            goto bad_reg;
B
bellard 已提交
914
        case 2: /* Coprocessor access register.  */
915 916
            if (arm_feature(env, ARM_FEATURE_XSCALE))
                goto bad_reg;
B
bellard 已提交
917 918 919 920
            return env->cp15.c1_coproc;
        default:
            goto bad_reg;
        }
P
pbrook 已提交
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
    case 2: /* MMU Page table control / MPU cache control.  */
        if (arm_feature(env, ARM_FEATURE_MPU)) {
            switch (op2) {
            case 0:
                return env->cp15.c2_data;
                break;
            case 1:
                return env->cp15.c2_insn;
                break;
            default:
                goto bad_reg;
            }
        } else {
            return env->cp15.c2_base;
        }
    case 3: /* MMU Domain access control / MPU write buffer control.  */
B
bellard 已提交
937 938 939
        return env->cp15.c3;
    case 4: /* Reserved.  */
        goto bad_reg;
P
pbrook 已提交
940
    case 5: /* MMU Fault status / MPU access permission.  */
941 942
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            op2 = 0;
B
bellard 已提交
943 944
        switch (op2) {
        case 0:
P
pbrook 已提交
945 946
            if (arm_feature(env, ARM_FEATURE_MPU))
                return simple_mpu_ap_bits(env->cp15.c5_data);
B
bellard 已提交
947 948
            return env->cp15.c5_data;
        case 1:
P
pbrook 已提交
949 950 951 952 953 954 955 956 957 958
            if (arm_feature(env, ARM_FEATURE_MPU))
                return simple_mpu_ap_bits(env->cp15.c5_data);
            return env->cp15.c5_insn;
        case 2:
            if (!arm_feature(env, ARM_FEATURE_MPU))
                goto bad_reg;
            return env->cp15.c5_data;
        case 3:
            if (!arm_feature(env, ARM_FEATURE_MPU))
                goto bad_reg;
B
bellard 已提交
959 960 961 962
            return env->cp15.c5_insn;
        default:
            goto bad_reg;
        }
P
pbrook 已提交
963 964 965 966 967 968 969 970
    case 6: /* MMU Fault address / MPU base/size.  */
        if (arm_feature(env, ARM_FEATURE_MPU)) {
            int n;
            n = (insn & 0xf);
            if (n >= 8)
                goto bad_reg;
            return env->cp15.c6_region[n];
        } else {
971 972
            if (arm_feature(env, ARM_FEATURE_OMAPCP))
                op2 = 0;
P
pbrook 已提交
973 974 975 976 977 978 979 980 981 982
            switch (op2) {
            case 0:
                return env->cp15.c6_data;
            case 1:
                /* Arm9 doesn't have an IFAR, but implementing it anyway
                   shouldn't do any harm.  */
                return env->cp15.c6_insn;
            default:
                goto bad_reg;
            }
B
bellard 已提交
983 984 985
        }
    case 7: /* Cache control.  */
        /* ??? This is for test, clean and invaidate operations that set the
986
           Z flag.  We can't represent N = Z = 1, so it also clears
B
bellard 已提交
987 988 989 990 991 992
           the N flag.  Oh well.  */
        env->NZF = 0;
        return 0;
    case 8: /* MMU TLB control.  */
        goto bad_reg;
    case 9: /* Cache lockdown.  */
993 994
        if (arm_feature(env, ARM_FEATURE_OMAPCP))
            return 0;
B
bellard 已提交
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
        switch (op2) {
        case 0:
            return env->cp15.c9_data;
        case 1:
            return env->cp15.c9_insn;
        default:
            goto bad_reg;
        }
    case 10: /* MMU TLB lockdown.  */
        /* ??? TLB lockdown not implemented.  */
        return 0;
    case 11: /* TCM DMA control.  */
    case 12: /* Reserved.  */
        goto bad_reg;
    case 13: /* Process ID.  */
        switch (op2) {
        case 0:
            return env->cp15.c13_fcse;
        case 1:
            return env->cp15.c13_context;
        default:
            goto bad_reg;
        }
    case 14: /* Reserved.  */
        goto bad_reg;
    case 15: /* Implementation specific.  */
1021
        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
1022
            if (op2 == 0 && crm == 1)
1023 1024 1025 1026
                return env->cp15.c15_cpar;

            goto bad_reg;
        }
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
            switch (crm) {
            case 0:
                return 0;
            case 1: /* Read TI925T configuration.  */
                return env->cp15.c15_ticonfig;
            case 2: /* Read I_max.  */
                return env->cp15.c15_i_max;
            case 3: /* Read I_min.  */
                return env->cp15.c15_i_min;
            case 4: /* Read thread-ID.  */
                return env->cp15.c15_threadid;
            case 8: /* TI925T_status */
                return 0;
            }
            goto bad_reg;
        }
B
bellard 已提交
1044 1045 1046 1047 1048 1049 1050 1051
        return 0;
    }
bad_reg:
    /* ??? For debugging only.  Should raise illegal instruction exception.  */
    cpu_abort(env, "Unimplemented cp15 register read\n");
    return 0;
}

1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
                ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
                void *opaque)
{
    if (cpnum < 0 || cpnum > 14) {
        cpu_abort(env, "Bad coprocessor number: %i\n", cpnum);
        return;
    }

    env->cp[cpnum].cp_read = cp_read;
    env->cp[cpnum].cp_write = cp_write;
    env->cp[cpnum].opaque = opaque;
}

B
bellard 已提交
1066
#endif