Commit b5ff1b3127119aa430a6fd309591d584803b7b6e
1 parent
0e43e99c
ARM system emulation (Paul Brook)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1661 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
20 changed files
with
2539 additions
and
248 deletions
Changelog
| 1 | 1 | version 0.7.3: |
| 2 | 2 | |
| 3 | + - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s | |
| 4 | + cpu (Paul Brook) | |
| 5 | + - SMP support | |
| 3 | 6 | - Mac OS X cocoa improvements (Mike Kronenberg) |
| 4 | 7 | - Mac OS X CoreAudio driver (Mike Kronenberg) |
| 5 | 8 | - DirectSound driver (malc) |
| ... | ... | @@ -10,7 +13,6 @@ version 0.7.3: |
| 10 | 13 | - Linux host serial port access |
| 11 | 14 | - Linux host low level parallel port access |
| 12 | 15 | - New network emulation code supporting VLANs. |
| 13 | - - SMP support | |
| 14 | 16 | |
| 15 | 17 | version 0.7.2: |
| 16 | 18 | ... | ... |
Makefile.target
| ... | ... | @@ -211,7 +211,7 @@ LIBOBJS+= op_helper.o helper.o |
| 211 | 211 | endif |
| 212 | 212 | |
| 213 | 213 | ifeq ($(TARGET_BASE_ARCH), arm) |
| 214 | -LIBOBJS+= op_helper.o | |
| 214 | +LIBOBJS+= op_helper.o helper.o | |
| 215 | 215 | endif |
| 216 | 216 | |
| 217 | 217 | # NOTE: the disassembler code is only needed for debugging |
| ... | ... | @@ -324,6 +324,9 @@ VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o magic-load.o slavio_intctl.o |
| 324 | 324 | VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o |
| 325 | 325 | endif |
| 326 | 326 | endif |
| 327 | +ifeq ($(TARGET_BASE_ARCH), arm) | |
| 328 | +VL_OBJS+= integratorcp.o ps2.o | |
| 329 | +endif | |
| 327 | 330 | ifdef CONFIG_GDBSTUB |
| 328 | 331 | VL_OBJS+=gdbstub.o |
| 329 | 332 | endif | ... | ... |
configure
| ... | ... | @@ -227,7 +227,7 @@ fi |
| 227 | 227 | |
| 228 | 228 | if test -z "$target_list" ; then |
| 229 | 229 | # these targets are portable |
| 230 | - target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu" | |
| 230 | + target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu arm-softmmu" | |
| 231 | 231 | # the following are Linux specific |
| 232 | 232 | if [ "$linux" = "yes" ] ; then |
| 233 | 233 | target_list="i386-user arm-user armeb-user sparc-user ppc-user $target_list" | ... | ... |
cpu-exec.c
| ... | ... | @@ -172,7 +172,9 @@ static inline TranslationBlock *tb_find_fast(void) |
| 172 | 172 | pc = cs_base + env->eip; |
| 173 | 173 | #elif defined(TARGET_ARM) |
| 174 | 174 | flags = env->thumb | (env->vfp.vec_len << 1) |
| 175 | - | (env->vfp.vec_stride << 4); | |
| 175 | + | (env->vfp.vec_stride << 4); | |
| 176 | + if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) | |
| 177 | + flags |= (1 << 6); | |
| 176 | 178 | cs_base = 0; |
| 177 | 179 | pc = env->regs[15]; |
| 178 | 180 | #elif defined(TARGET_SPARC) |
| ... | ... | @@ -322,15 +324,6 @@ int cpu_exec(CPUState *env1) |
| 322 | 324 | CC_OP = CC_OP_EFLAGS; |
| 323 | 325 | env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); |
| 324 | 326 | #elif defined(TARGET_ARM) |
| 325 | - { | |
| 326 | - unsigned int psr; | |
| 327 | - psr = env->cpsr; | |
| 328 | - env->CF = (psr >> 29) & 1; | |
| 329 | - env->NZF = (psr & 0xc0000000) ^ 0x40000000; | |
| 330 | - env->VF = (psr << 3) & 0x80000000; | |
| 331 | - env->QF = (psr >> 27) & 1; | |
| 332 | - env->cpsr = psr & ~CACHED_CPSR_BITS; | |
| 333 | - } | |
| 334 | 327 | #elif defined(TARGET_SPARC) |
| 335 | 328 | #if defined(reg_REGWPTR) |
| 336 | 329 | saved_regwptr = REGWPTR; |
| ... | ... | @@ -379,6 +372,8 @@ int cpu_exec(CPUState *env1) |
| 379 | 372 | do_interrupt(env); |
| 380 | 373 | #elif defined(TARGET_SPARC) |
| 381 | 374 | do_interrupt(env->exception_index); |
| 375 | +#elif defined(TARGET_ARM) | |
| 376 | + do_interrupt(env); | |
| 382 | 377 | #endif |
| 383 | 378 | } |
| 384 | 379 | env->exception_index = -1; |
| ... | ... | @@ -508,8 +503,19 @@ int cpu_exec(CPUState *env1) |
| 508 | 503 | //do_interrupt(0, 0, 0, 0, 0); |
| 509 | 504 | env->interrupt_request &= ~CPU_INTERRUPT_TIMER; |
| 510 | 505 | } |
| 506 | +#elif defined(TARGET_ARM) | |
| 507 | + if (interrupt_request & CPU_INTERRUPT_FIQ | |
| 508 | + && !(env->uncached_cpsr & CPSR_F)) { | |
| 509 | + env->exception_index = EXCP_FIQ; | |
| 510 | + do_interrupt(env); | |
| 511 | + } | |
| 512 | + if (interrupt_request & CPU_INTERRUPT_HARD | |
| 513 | + && !(env->uncached_cpsr & CPSR_I)) { | |
| 514 | + env->exception_index = EXCP_IRQ; | |
| 515 | + do_interrupt(env); | |
| 516 | + } | |
| 511 | 517 | #endif |
| 512 | - if (interrupt_request & CPU_INTERRUPT_EXITTB) { | |
| 518 | + if (env->interrupt_request & CPU_INTERRUPT_EXITTB) { | |
| 513 | 519 | env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; |
| 514 | 520 | /* ensure that no TB jump will be modified as |
| 515 | 521 | the program flow was changed */ |
| ... | ... | @@ -526,7 +532,7 @@ int cpu_exec(CPUState *env1) |
| 526 | 532 | } |
| 527 | 533 | } |
| 528 | 534 | #ifdef DEBUG_EXEC |
| 529 | - if ((loglevel & CPU_LOG_EXEC)) { | |
| 535 | + if ((loglevel & CPU_LOG_TB_CPU)) { | |
| 530 | 536 | #if defined(TARGET_I386) |
| 531 | 537 | /* restore flags in standard format */ |
| 532 | 538 | #ifdef reg_EAX |
| ... | ... | @@ -557,9 +563,7 @@ int cpu_exec(CPUState *env1) |
| 557 | 563 | cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); |
| 558 | 564 | env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); |
| 559 | 565 | #elif defined(TARGET_ARM) |
| 560 | - env->cpsr = compute_cpsr(); | |
| 561 | 566 | cpu_dump_state(env, logfile, fprintf, 0); |
| 562 | - env->cpsr &= ~CACHED_CPSR_BITS; | |
| 563 | 567 | #elif defined(TARGET_SPARC) |
| 564 | 568 | REGWPTR = env->regbase + (env->cwp * 16); |
| 565 | 569 | env->regwptr = REGWPTR; |
| ... | ... | @@ -760,7 +764,6 @@ int cpu_exec(CPUState *env1) |
| 760 | 764 | EDI = saved_EDI; |
| 761 | 765 | #endif |
| 762 | 766 | #elif defined(TARGET_ARM) |
| 763 | - env->cpsr = compute_cpsr(); | |
| 764 | 767 | /* XXX: Save/restore host fpu exception state?. */ |
| 765 | 768 | #elif defined(TARGET_SPARC) |
| 766 | 769 | #if defined(reg_REGWPTR) | ... | ... |
exec-all.h
| ... | ... | @@ -549,8 +549,10 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr) |
| 549 | 549 | is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM); |
| 550 | 550 | #elif defined (TARGET_SPARC) |
| 551 | 551 | is_user = (env->psrs == 0); |
| 552 | +#elif defined (TARGET_ARM) | |
| 553 | + is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR); | |
| 552 | 554 | #else |
| 553 | -#error "Unimplemented !" | |
| 555 | +#error unimplemented CPU | |
| 554 | 556 | #endif |
| 555 | 557 | if (__builtin_expect(env->tlb_read[is_user][index].address != |
| 556 | 558 | (addr & TARGET_PAGE_MASK), 0)) { | ... | ... |
exec.c
| ... | ... | @@ -1868,14 +1868,14 @@ int cpu_register_io_memory(int io_index, |
| 1868 | 1868 | int i; |
| 1869 | 1869 | |
| 1870 | 1870 | if (io_index <= 0) { |
| 1871 | - if (io_index >= IO_MEM_NB_ENTRIES) | |
| 1871 | + if (io_mem_nb >= IO_MEM_NB_ENTRIES) | |
| 1872 | 1872 | return -1; |
| 1873 | 1873 | io_index = io_mem_nb++; |
| 1874 | 1874 | } else { |
| 1875 | 1875 | if (io_index >= IO_MEM_NB_ENTRIES) |
| 1876 | 1876 | return -1; |
| 1877 | 1877 | } |
| 1878 | - | |
| 1878 | + | |
| 1879 | 1879 | for(i = 0;i < 3; i++) { |
| 1880 | 1880 | io_mem_read[io_index][i] = mem_read[i]; |
| 1881 | 1881 | io_mem_write[io_index][i] = mem_write[i]; | ... | ... |
gdbstub.c
| ... | ... | @@ -399,7 +399,7 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) |
| 399 | 399 | memset (ptr, 0, 8 * 12 + 4); |
| 400 | 400 | ptr += 8 * 12 + 4; |
| 401 | 401 | /* CPSR (4 bytes). */ |
| 402 | - *(uint32_t *)ptr = tswapl (env->cpsr); | |
| 402 | + *(uint32_t *)ptr = tswapl (cpsr_read(env)); | |
| 403 | 403 | ptr += 4; |
| 404 | 404 | |
| 405 | 405 | return ptr - mem_buf; |
| ... | ... | @@ -419,7 +419,7 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) |
| 419 | 419 | } |
| 420 | 420 | /* Ignore FPA regs and scr. */ |
| 421 | 421 | ptr += 8 * 12 + 4; |
| 422 | - env->cpsr = tswapl(*(uint32_t *)ptr); | |
| 422 | + cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff); | |
| 423 | 423 | } |
| 424 | 424 | #else |
| 425 | 425 | static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) |
| ... | ... | @@ -463,6 +463,8 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) |
| 463 | 463 | #elif defined (TARGET_SPARC) |
| 464 | 464 | env->pc = addr; |
| 465 | 465 | env->npc = addr + 4; |
| 466 | +#elif defined (TARGET_ARM) | |
| 467 | + env->regs[15] = addr; | |
| 466 | 468 | #endif |
| 467 | 469 | } |
| 468 | 470 | #ifdef CONFIG_USER_ONLY |
| ... | ... | @@ -481,6 +483,8 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) |
| 481 | 483 | #elif defined (TARGET_SPARC) |
| 482 | 484 | env->pc = addr; |
| 483 | 485 | env->npc = addr + 4; |
| 486 | +#elif defined (TARGET_ARM) | |
| 487 | + env->regs[15] = addr; | |
| 484 | 488 | #endif |
| 485 | 489 | } |
| 486 | 490 | cpu_single_step(env, 1); | ... | ... |
hw/integratorcp.c
0 โ 100644
| 1 | +/* | |
| 2 | + * ARM Integrator CP System emulation. | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 CodeSourcery, LLC. | |
| 5 | + * Written by Paul Brook | |
| 6 | + * | |
| 7 | + * This code is licenced under the GPL | |
| 8 | + */ | |
| 9 | + | |
| 10 | +#include <vl.h> | |
| 11 | + | |
| 12 | +#define KERNEL_ARGS_ADDR 0x100 | |
| 13 | +#define KERNEL_LOAD_ADDR 0x00010000 | |
| 14 | +#define INITRD_LOAD_ADDR 0x00800000 | |
| 15 | + | |
| 16 | +/* Stub functions for hardware that doesn't exist. */ | |
| 17 | +void pic_set_irq(int irq, int level) | |
| 18 | +{ | |
| 19 | + cpu_abort (cpu_single_env, "pic_set_irq"); | |
| 20 | +} | |
| 21 | + | |
| 22 | +void pic_info(void) | |
| 23 | +{ | |
| 24 | +} | |
| 25 | + | |
| 26 | +void irq_info(void) | |
| 27 | +{ | |
| 28 | +} | |
| 29 | + | |
| 30 | +void vga_update_display(void) | |
| 31 | +{ | |
| 32 | +} | |
| 33 | + | |
| 34 | +void vga_screen_dump(const char *filename) | |
| 35 | +{ | |
| 36 | +} | |
| 37 | + | |
| 38 | +void vga_invalidate_display(void) | |
| 39 | +{ | |
| 40 | +} | |
| 41 | + | |
| 42 | +void DMA_run (void) | |
| 43 | +{ | |
| 44 | +} | |
| 45 | + | |
| 46 | +typedef struct { | |
| 47 | + uint32_t flash_offset; | |
| 48 | + uint32_t cm_osc; | |
| 49 | + uint32_t cm_ctrl; | |
| 50 | + uint32_t cm_lock; | |
| 51 | + uint32_t cm_auxosc; | |
| 52 | + uint32_t cm_sdram; | |
| 53 | + uint32_t cm_init; | |
| 54 | + uint32_t cm_flags; | |
| 55 | + uint32_t cm_nvflags; | |
| 56 | + uint32_t int_level; | |
| 57 | + uint32_t irq_enabled; | |
| 58 | + uint32_t fiq_enabled; | |
| 59 | +} integratorcm_state; | |
| 60 | + | |
| 61 | +static uint8_t integrator_spd[128] = { | |
| 62 | + 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1, | |
| 63 | + 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40 | |
| 64 | +}; | |
| 65 | + | |
| 66 | +static uint32_t integratorcm_read(void *opaque, target_phys_addr_t offset) | |
| 67 | +{ | |
| 68 | + integratorcm_state *s = (integratorcm_state *)opaque; | |
| 69 | + offset -= 0x10000000; | |
| 70 | + if (offset >= 0x100 && offset < 0x200) { | |
| 71 | + /* CM_SPD */ | |
| 72 | + if (offset >= 0x180) | |
| 73 | + return 0; | |
| 74 | + return integrator_spd[offset >> 2]; | |
| 75 | + } | |
| 76 | + switch (offset >> 2) { | |
| 77 | + case 0: /* CM_ID */ | |
| 78 | + return 0x411a3001; | |
| 79 | + case 1: /* CM_PROC */ | |
| 80 | + return 0; | |
| 81 | + case 2: /* CM_OSC */ | |
| 82 | + return s->cm_osc; | |
| 83 | + case 3: /* CM_CTRL */ | |
| 84 | + return s->cm_ctrl; | |
| 85 | + case 4: /* CM_STAT */ | |
| 86 | + return 0x00100000; | |
| 87 | + case 5: /* CM_LOCK */ | |
| 88 | + if (s->cm_lock == 0xa05f) { | |
| 89 | + return 0x1a05f; | |
| 90 | + } else { | |
| 91 | + return s->cm_lock; | |
| 92 | + } | |
| 93 | + case 6: /* CM_LMBUSCNT */ | |
| 94 | + /* ??? High frequency timer. */ | |
| 95 | + cpu_abort(cpu_single_env, "integratorcm_read: CM_LMBUSCNT"); | |
| 96 | + case 7: /* CM_AUXOSC */ | |
| 97 | + return s->cm_auxosc; | |
| 98 | + case 8: /* CM_SDRAM */ | |
| 99 | + return s->cm_sdram; | |
| 100 | + case 9: /* CM_INIT */ | |
| 101 | + return s->cm_init; | |
| 102 | + case 10: /* CM_REFCT */ | |
| 103 | + /* ??? High frequency timer. */ | |
| 104 | + cpu_abort(cpu_single_env, "integratorcm_read: CM_REFCT"); | |
| 105 | + case 12: /* CM_FLAGS */ | |
| 106 | + return s->cm_flags; | |
| 107 | + case 14: /* CM_NVFLAGS */ | |
| 108 | + return s->cm_nvflags; | |
| 109 | + case 16: /* CM_IRQ_STAT */ | |
| 110 | + return s->int_level & s->irq_enabled; | |
| 111 | + case 17: /* CM_IRQ_RSTAT */ | |
| 112 | + return s->int_level; | |
| 113 | + case 18: /* CM_IRQ_ENSET */ | |
| 114 | + return s->irq_enabled; | |
| 115 | + case 20: /* CM_SOFT_INTSET */ | |
| 116 | + return s->int_level & 1; | |
| 117 | + case 24: /* CM_FIQ_STAT */ | |
| 118 | + return s->int_level & s->fiq_enabled; | |
| 119 | + case 25: /* CM_FIQ_RSTAT */ | |
| 120 | + return s->int_level; | |
| 121 | + case 26: /* CM_FIQ_ENSET */ | |
| 122 | + return s->fiq_enabled; | |
| 123 | + case 32: /* CM_VOLTAGE_CTL0 */ | |
| 124 | + case 33: /* CM_VOLTAGE_CTL1 */ | |
| 125 | + case 34: /* CM_VOLTAGE_CTL2 */ | |
| 126 | + case 35: /* CM_VOLTAGE_CTL3 */ | |
| 127 | + /* ??? Voltage control unimplemented. */ | |
| 128 | + return 0; | |
| 129 | + default: | |
| 130 | + cpu_abort (cpu_single_env, | |
| 131 | + "integratorcm_read: Unimplemented offset 0x%x\n", offset); | |
| 132 | + return 0; | |
| 133 | + } | |
| 134 | +} | |
| 135 | + | |
| 136 | +static void integratorcm_do_remap(integratorcm_state *s, int flash) | |
| 137 | +{ | |
| 138 | + if (flash) { | |
| 139 | + cpu_register_physical_memory(0, 0x100000, IO_MEM_RAM); | |
| 140 | + } else { | |
| 141 | + cpu_register_physical_memory(0, 0x100000, s->flash_offset | IO_MEM_RAM); | |
| 142 | + } | |
| 143 | + //??? tlb_flush (cpu_single_env, 1); | |
| 144 | +} | |
| 145 | + | |
| 146 | +static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value) | |
| 147 | +{ | |
| 148 | + if (value & 8) { | |
| 149 | + cpu_abort(cpu_single_env, "Board reset\n"); | |
| 150 | + } | |
| 151 | + if ((s->cm_init ^ value) & 4) { | |
| 152 | + integratorcm_do_remap(s, (value & 4) == 0); | |
| 153 | + } | |
| 154 | + if ((s->cm_init ^ value) & 1) { | |
| 155 | + printf("Green LED %s\n", (value & 1) ? "on" : "off"); | |
| 156 | + } | |
| 157 | + s->cm_init = (s->cm_init & ~ 5) | (value ^ 5); | |
| 158 | +} | |
| 159 | + | |
| 160 | +static void integratorcm_update(integratorcm_state *s) | |
| 161 | +{ | |
| 162 | + /* ??? The CPU irq/fiq is raised when either the core module or base PIC | |
| 163 | + are active. */ | |
| 164 | + if (s->int_level & (s->irq_enabled | s->fiq_enabled)) | |
| 165 | + cpu_abort(cpu_single_env, "Core module interrupt\n"); | |
| 166 | +} | |
| 167 | + | |
| 168 | +static void integratorcm_write(void *opaque, target_phys_addr_t offset, | |
| 169 | + uint32_t value) | |
| 170 | +{ | |
| 171 | + integratorcm_state *s = (integratorcm_state *)opaque; | |
| 172 | + offset -= 0x10000000; | |
| 173 | + switch (offset >> 2) { | |
| 174 | + case 2: /* CM_OSC */ | |
| 175 | + if (s->cm_lock == 0xa05f) | |
| 176 | + s->cm_osc = value; | |
| 177 | + break; | |
| 178 | + case 3: /* CM_CTRL */ | |
| 179 | + integratorcm_set_ctrl(s, value); | |
| 180 | + break; | |
| 181 | + case 5: /* CM_LOCK */ | |
| 182 | + s->cm_lock = value & 0xffff; | |
| 183 | + break; | |
| 184 | + case 7: /* CM_AUXOSC */ | |
| 185 | + if (s->cm_lock == 0xa05f) | |
| 186 | + s->cm_auxosc = value; | |
| 187 | + break; | |
| 188 | + case 8: /* CM_SDRAM */ | |
| 189 | + s->cm_sdram = value; | |
| 190 | + break; | |
| 191 | + case 9: /* CM_INIT */ | |
| 192 | + /* ??? This can change the memory bus frequency. */ | |
| 193 | + s->cm_init = value; | |
| 194 | + break; | |
| 195 | + case 12: /* CM_FLAGSS */ | |
| 196 | + s->cm_flags |= value; | |
| 197 | + break; | |
| 198 | + case 13: /* CM_FLAGSC */ | |
| 199 | + s->cm_flags &= ~value; | |
| 200 | + break; | |
| 201 | + case 14: /* CM_NVFLAGSS */ | |
| 202 | + s->cm_nvflags |= value; | |
| 203 | + break; | |
| 204 | + case 15: /* CM_NVFLAGSS */ | |
| 205 | + s->cm_nvflags &= ~value; | |
| 206 | + break; | |
| 207 | + case 18: /* CM_IRQ_ENSET */ | |
| 208 | + s->irq_enabled |= value; | |
| 209 | + integratorcm_update(s); | |
| 210 | + break; | |
| 211 | + case 19: /* CM_IRQ_ENCLR */ | |
| 212 | + s->irq_enabled &= ~value; | |
| 213 | + integratorcm_update(s); | |
| 214 | + break; | |
| 215 | + case 20: /* CM_SOFT_INTSET */ | |
| 216 | + s->int_level |= (value & 1); | |
| 217 | + integratorcm_update(s); | |
| 218 | + break; | |
| 219 | + case 21: /* CM_SOFT_INTCLR */ | |
| 220 | + s->int_level &= ~(value & 1); | |
| 221 | + integratorcm_update(s); | |
| 222 | + break; | |
| 223 | + case 26: /* CM_FIQ_ENSET */ | |
| 224 | + s->fiq_enabled |= value; | |
| 225 | + integratorcm_update(s); | |
| 226 | + break; | |
| 227 | + case 27: /* CM_FIQ_ENCLR */ | |
| 228 | + s->fiq_enabled &= ~value; | |
| 229 | + integratorcm_update(s); | |
| 230 | + break; | |
| 231 | + case 32: /* CM_VOLTAGE_CTL0 */ | |
| 232 | + case 33: /* CM_VOLTAGE_CTL1 */ | |
| 233 | + case 34: /* CM_VOLTAGE_CTL2 */ | |
| 234 | + case 35: /* CM_VOLTAGE_CTL3 */ | |
| 235 | + /* ??? Voltage control unimplemented. */ | |
| 236 | + break; | |
| 237 | + default: | |
| 238 | + cpu_abort (cpu_single_env, | |
| 239 | + "integratorcm_write: Unimplemented offset 0x%x\n", offset); | |
| 240 | + break; | |
| 241 | + } | |
| 242 | +} | |
| 243 | + | |
| 244 | +/* Integrator/CM control registers. */ | |
| 245 | + | |
| 246 | +static CPUReadMemoryFunc *integratorcm_readfn[] = { | |
| 247 | + integratorcm_read, | |
| 248 | + integratorcm_read, | |
| 249 | + integratorcm_read | |
| 250 | +}; | |
| 251 | + | |
| 252 | +static CPUWriteMemoryFunc *integratorcm_writefn[] = { | |
| 253 | + integratorcm_write, | |
| 254 | + integratorcm_write, | |
| 255 | + integratorcm_write | |
| 256 | +}; | |
| 257 | + | |
| 258 | +static void integratorcm_init(int memsz, uint32_t flash_offset) | |
| 259 | +{ | |
| 260 | + int iomemtype; | |
| 261 | + integratorcm_state *s; | |
| 262 | + | |
| 263 | + s = (integratorcm_state *)qemu_mallocz(sizeof(integratorcm_state)); | |
| 264 | + s->cm_osc = 0x01000048; | |
| 265 | + /* ??? What should the high bits of this value be? */ | |
| 266 | + s->cm_auxosc = 0x0007feff; | |
| 267 | + s->cm_sdram = 0x00011122; | |
| 268 | + if (memsz >= 256) { | |
| 269 | + integrator_spd[31] = 64; | |
| 270 | + s->cm_sdram |= 0x10; | |
| 271 | + } else if (memsz >= 128) { | |
| 272 | + integrator_spd[31] = 32; | |
| 273 | + s->cm_sdram |= 0x0c; | |
| 274 | + } else if (memsz >= 64) { | |
| 275 | + integrator_spd[31] = 16; | |
| 276 | + s->cm_sdram |= 0x08; | |
| 277 | + } else if (memsz >= 32) { | |
| 278 | + integrator_spd[31] = 4; | |
| 279 | + s->cm_sdram |= 0x04; | |
| 280 | + } else { | |
| 281 | + integrator_spd[31] = 2; | |
| 282 | + } | |
| 283 | + memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); | |
| 284 | + s->cm_init = 0x00000112; | |
| 285 | + s->flash_offset = flash_offset; | |
| 286 | + | |
| 287 | + iomemtype = cpu_register_io_memory(0, integratorcm_readfn, | |
| 288 | + integratorcm_writefn, s); | |
| 289 | + cpu_register_physical_memory(0x10000000, 0x007fffff, iomemtype); | |
| 290 | + integratorcm_do_remap(s, 1); | |
| 291 | + /* ??? Save/restore. */ | |
| 292 | +} | |
| 293 | + | |
| 294 | +/* Integrator/CP hardware emulation. */ | |
| 295 | +/* Primary interrupt controller. */ | |
| 296 | + | |
| 297 | +typedef struct icp_pic_state | |
| 298 | +{ | |
| 299 | + uint32_t base; | |
| 300 | + uint32_t level; | |
| 301 | + uint32_t irq_enabled; | |
| 302 | + uint32_t fiq_enabled; | |
| 303 | + void *parent; | |
| 304 | + /* -1 if parent is a cpu, otherwise IRQ number on parent PIC. */ | |
| 305 | + int parent_irq; | |
| 306 | +} icp_pic_state; | |
| 307 | + | |
| 308 | +static void icp_pic_set_level(icp_pic_state *, int, int); | |
| 309 | + | |
| 310 | +static void icp_pic_update(icp_pic_state *s) | |
| 311 | +{ | |
| 312 | + CPUState *env; | |
| 313 | + if (s->parent_irq != -1) { | |
| 314 | + uint32_t flags; | |
| 315 | + | |
| 316 | + flags = (s->level & s->irq_enabled); | |
| 317 | + icp_pic_set_level((icp_pic_state *)s->parent, s->parent_irq, | |
| 318 | + flags != 0); | |
| 319 | + return; | |
| 320 | + } | |
| 321 | + /* Raise CPU interrupt. */ | |
| 322 | + env = (CPUState *)s->parent; | |
| 323 | + if (s->level & s->fiq_enabled) { | |
| 324 | + cpu_interrupt (env, CPU_INTERRUPT_FIQ); | |
| 325 | + } else { | |
| 326 | + cpu_reset_interrupt (env, CPU_INTERRUPT_FIQ); | |
| 327 | + } | |
| 328 | + if (s->level & s->irq_enabled) { | |
| 329 | + cpu_interrupt (env, CPU_INTERRUPT_HARD); | |
| 330 | + } else { | |
| 331 | + cpu_reset_interrupt (env, CPU_INTERRUPT_HARD); | |
| 332 | + } | |
| 333 | +} | |
| 334 | + | |
| 335 | +static void icp_pic_set_level(icp_pic_state *s, int n, int level) | |
| 336 | +{ | |
| 337 | + if (level) | |
| 338 | + s->level |= 1 << n; | |
| 339 | + else | |
| 340 | + s->level &= ~(1 << n); | |
| 341 | + icp_pic_update(s); | |
| 342 | +} | |
| 343 | + | |
| 344 | +static uint32_t icp_pic_read(void *opaque, target_phys_addr_t offset) | |
| 345 | +{ | |
| 346 | + icp_pic_state *s = (icp_pic_state *)opaque; | |
| 347 | + | |
| 348 | + offset -= s->base; | |
| 349 | + switch (offset >> 2) { | |
| 350 | + case 0: /* IRQ_STATUS */ | |
| 351 | + return s->level & s->irq_enabled; | |
| 352 | + case 1: /* IRQ_RAWSTAT */ | |
| 353 | + return s->level; | |
| 354 | + case 2: /* IRQ_ENABLESET */ | |
| 355 | + return s->irq_enabled; | |
| 356 | + case 4: /* INT_SOFTSET */ | |
| 357 | + return s->level & 1; | |
| 358 | + case 8: /* FRQ_STATUS */ | |
| 359 | + return s->level & s->fiq_enabled; | |
| 360 | + case 9: /* FRQ_RAWSTAT */ | |
| 361 | + return s->level; | |
| 362 | + case 10: /* FRQ_ENABLESET */ | |
| 363 | + return s->fiq_enabled; | |
| 364 | + case 3: /* IRQ_ENABLECLR */ | |
| 365 | + case 5: /* INT_SOFTCLR */ | |
| 366 | + case 11: /* FRQ_ENABLECLR */ | |
| 367 | + default: | |
| 368 | + printf ("icp_pic_read: Bad register offset 0x%x\n", offset); | |
| 369 | + return 0; | |
| 370 | + } | |
| 371 | +} | |
| 372 | + | |
| 373 | +static void icp_pic_write(void *opaque, target_phys_addr_t offset, | |
| 374 | + uint32_t value) | |
| 375 | +{ | |
| 376 | + icp_pic_state *s = (icp_pic_state *)opaque; | |
| 377 | + offset -= s->base; | |
| 378 | + | |
| 379 | + switch (offset >> 2) { | |
| 380 | + case 2: /* IRQ_ENABLESET */ | |
| 381 | + s->irq_enabled |= value; | |
| 382 | + break; | |
| 383 | + case 3: /* IRQ_ENABLECLR */ | |
| 384 | + s->irq_enabled &= ~value; | |
| 385 | + break; | |
| 386 | + case 4: /* INT_SOFTSET */ | |
| 387 | + if (value & 1) | |
| 388 | + icp_pic_set_level(s, 0, 1); | |
| 389 | + break; | |
| 390 | + case 5: /* INT_SOFTCLR */ | |
| 391 | + if (value & 1) | |
| 392 | + icp_pic_set_level(s, 0, 0); | |
| 393 | + break; | |
| 394 | + case 10: /* FRQ_ENABLESET */ | |
| 395 | + s->fiq_enabled |= value; | |
| 396 | + break; | |
| 397 | + case 11: /* FRQ_ENABLECLR */ | |
| 398 | + s->fiq_enabled &= ~value; | |
| 399 | + break; | |
| 400 | + case 0: /* IRQ_STATUS */ | |
| 401 | + case 1: /* IRQ_RAWSTAT */ | |
| 402 | + case 8: /* FRQ_STATUS */ | |
| 403 | + case 9: /* FRQ_RAWSTAT */ | |
| 404 | + default: | |
| 405 | + printf ("icp_pic_write: Bad register offset 0x%x\n", offset); | |
| 406 | + return; | |
| 407 | + } | |
| 408 | + icp_pic_update(s); | |
| 409 | +} | |
| 410 | + | |
| 411 | +static CPUReadMemoryFunc *icp_pic_readfn[] = { | |
| 412 | + icp_pic_read, | |
| 413 | + icp_pic_read, | |
| 414 | + icp_pic_read | |
| 415 | +}; | |
| 416 | + | |
| 417 | +static CPUWriteMemoryFunc *icp_pic_writefn[] = { | |
| 418 | + icp_pic_write, | |
| 419 | + icp_pic_write, | |
| 420 | + icp_pic_write | |
| 421 | +}; | |
| 422 | + | |
| 423 | +static icp_pic_state *icp_pic_init(uint32_t base, void *parent, | |
| 424 | + int parent_irq) | |
| 425 | +{ | |
| 426 | + icp_pic_state *s; | |
| 427 | + int iomemtype; | |
| 428 | + | |
| 429 | + s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state)); | |
| 430 | + if (!s) | |
| 431 | + return NULL; | |
| 432 | + | |
| 433 | + s->base = base; | |
| 434 | + s->parent = parent; | |
| 435 | + s->parent_irq = parent_irq; | |
| 436 | + iomemtype = cpu_register_io_memory(0, icp_pic_readfn, | |
| 437 | + icp_pic_writefn, s); | |
| 438 | + cpu_register_physical_memory(base, 0x007fffff, iomemtype); | |
| 439 | + /* ??? Save/restore. */ | |
| 440 | + return s; | |
| 441 | +} | |
| 442 | + | |
| 443 | +/* Timers. */ | |
| 444 | + | |
| 445 | +/* System bus clock speed (40MHz) for timer 0. Not sure about this value. */ | |
| 446 | +#define ICP_BUS_FREQ 40000000 | |
| 447 | + | |
| 448 | +typedef struct { | |
| 449 | + int64_t next_time; | |
| 450 | + int64_t expires[3]; | |
| 451 | + int64_t loaded[3]; | |
| 452 | + QEMUTimer *timer; | |
| 453 | + icp_pic_state *pic; | |
| 454 | + uint32_t base; | |
| 455 | + uint32_t control[3]; | |
| 456 | + uint32_t count[3]; | |
| 457 | + uint32_t limit[3]; | |
| 458 | + int freq[3]; | |
| 459 | + int int_level[3]; | |
| 460 | +} icp_pit_state; | |
| 461 | + | |
| 462 | +/* Calculate the new expiry time of the given timer. */ | |
| 463 | + | |
| 464 | +static void icp_pit_reload(icp_pit_state *s, int n) | |
| 465 | +{ | |
| 466 | + int64_t delay; | |
| 467 | + | |
| 468 | + s->loaded[n] = s->expires[n]; | |
| 469 | + delay = muldiv64(s->count[n], ticks_per_sec, s->freq[n]); | |
| 470 | + if (delay == 0) | |
| 471 | + delay = 1; | |
| 472 | + s->expires[n] += delay; | |
| 473 | +} | |
| 474 | + | |
| 475 | +/* Check all active timers, and schedule the next timer interrupt. */ | |
| 476 | + | |
| 477 | +static void icp_pit_update(icp_pit_state *s, int64_t now) | |
| 478 | +{ | |
| 479 | + int n; | |
| 480 | + int64_t next; | |
| 481 | + | |
| 482 | + next = now; | |
| 483 | + for (n = 0; n < 3; n++) { | |
| 484 | + /* Ignore disabled timers. */ | |
| 485 | + if ((s->control[n] & 0x80) == 0) | |
| 486 | + continue; | |
| 487 | + /* Ignore expired one-shot timers. */ | |
| 488 | + if (s->count[n] == 0 && s->control[n] & 1) | |
| 489 | + continue; | |
| 490 | + if (s->expires[n] - now <= 0) { | |
| 491 | + /* Timer has expired. */ | |
| 492 | + s->int_level[n] = 1; | |
| 493 | + if (s->control[n] & 1) { | |
| 494 | + /* One-shot. */ | |
| 495 | + s->count[n] = 0; | |
| 496 | + } else { | |
| 497 | + if ((s->control[n] & 0x40) == 0) { | |
| 498 | + /* Free running. */ | |
| 499 | + if (s->control[n] & 2) | |
| 500 | + s->count[n] = 0xffffffff; | |
| 501 | + else | |
| 502 | + s->count[n] = 0xffff; | |
| 503 | + } else { | |
| 504 | + /* Periodic. */ | |
| 505 | + s->count[n] = s->limit[n]; | |
| 506 | + } | |
| 507 | + } | |
| 508 | + } | |
| 509 | + while (s->expires[n] - now <= 0) { | |
| 510 | + icp_pit_reload(s, n); | |
| 511 | + } | |
| 512 | + } | |
| 513 | + /* Update interrupts. */ | |
| 514 | + for (n = 0; n < 3; n++) { | |
| 515 | + if (s->int_level[n] && (s->control[n] & 0x20)) { | |
| 516 | + icp_pic_set_level(s->pic, 5 + n, 1); | |
| 517 | + } else { | |
| 518 | + icp_pic_set_level(s->pic, 5 + n, 0); | |
| 519 | + } | |
| 520 | + if (next - s->expires[n] < 0) | |
| 521 | + next = s->expires[n]; | |
| 522 | + } | |
| 523 | + /* Schedule the next timer interrupt. */ | |
| 524 | + if (next == now) { | |
| 525 | + qemu_del_timer(s->timer); | |
| 526 | + s->next_time = 0; | |
| 527 | + } else if (next != s->next_time) { | |
| 528 | + qemu_mod_timer(s->timer, next); | |
| 529 | + s->next_time = next; | |
| 530 | + } | |
| 531 | +} | |
| 532 | + | |
| 533 | +/* Return the current value of the timer. */ | |
| 534 | +static uint32_t icp_pit_getcount(icp_pit_state *s, int n, int64_t now) | |
| 535 | +{ | |
| 536 | + int64_t elapsed; | |
| 537 | + int64_t period; | |
| 538 | + | |
| 539 | + if (s->count[n] == 0) | |
| 540 | + return 0; | |
| 541 | + if ((s->control[n] & 0x80) == 0) | |
| 542 | + return s->count[n]; | |
| 543 | + elapsed = now - s->loaded[n]; | |
| 544 | + period = s->expires[n] - s->loaded[n]; | |
| 545 | + /* If the timer should have expired then return 0. This can happen | |
| 546 | + when the host timer signal doesnt occur immediately. It's better to | |
| 547 | + have a timer appear to sit at zero for a while than have it wrap | |
| 548 | + around before the guest interrupt is raised. */ | |
| 549 | + /* ??? Could we trigger the interrupt here? */ | |
| 550 | + if (elapsed > period) | |
| 551 | + return 0; | |
| 552 | + /* We need to calculate count * elapsed / period without overfowing. | |
| 553 | + Scale both elapsed and period so they fit in a 32-bit int. */ | |
| 554 | + while (period != (int32_t)period) { | |
| 555 | + period >>= 1; | |
| 556 | + elapsed >>= 1; | |
| 557 | + } | |
| 558 | + return ((uint64_t)s->count[n] * (uint64_t)(int32_t)elapsed) | |
| 559 | + / (int32_t)period; | |
| 560 | +} | |
| 561 | + | |
| 562 | +static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) | |
| 563 | +{ | |
| 564 | + int n; | |
| 565 | + icp_pit_state *s = (icp_pit_state *)opaque; | |
| 566 | + | |
| 567 | + offset -= s->base; | |
| 568 | + n = offset >> 8; | |
| 569 | + if (n > 2) | |
| 570 | + cpu_abort (cpu_single_env, "icp_pit_read: Bad timer %x\n", offset); | |
| 571 | + switch ((offset & 0xff) >> 2) { | |
| 572 | + case 0: /* TimerLoad */ | |
| 573 | + case 6: /* TimerBGLoad */ | |
| 574 | + return s->limit[n]; | |
| 575 | + case 1: /* TimerValue */ | |
| 576 | + return icp_pit_getcount(s, n, qemu_get_clock(vm_clock)); | |
| 577 | + case 2: /* TimerControl */ | |
| 578 | + return s->control[n]; | |
| 579 | + case 4: /* TimerRIS */ | |
| 580 | + return s->int_level[n]; | |
| 581 | + case 5: /* TimerMIS */ | |
| 582 | + if ((s->control[n] & 0x20) == 0) | |
| 583 | + return 0; | |
| 584 | + return s->int_level[n]; | |
| 585 | + default: | |
| 586 | + cpu_abort (cpu_single_env, "icp_pit_read: Bad offset %x\n", offset); | |
| 587 | + return 0; | |
| 588 | + } | |
| 589 | +} | |
| 590 | + | |
| 591 | +static void icp_pit_write(void *opaque, target_phys_addr_t offset, | |
| 592 | + uint32_t value) | |
| 593 | +{ | |
| 594 | + icp_pit_state *s = (icp_pit_state *)opaque; | |
| 595 | + int n; | |
| 596 | + int64_t now; | |
| 597 | + | |
| 598 | + now = qemu_get_clock(vm_clock); | |
| 599 | + offset -= s->base; | |
| 600 | + n = offset >> 8; | |
| 601 | + if (n > 2) | |
| 602 | + cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); | |
| 603 | + | |
| 604 | + switch ((offset & 0xff) >> 2) { | |
| 605 | + case 0: /* TimerLoad */ | |
| 606 | + s->limit[n] = value; | |
| 607 | + s->count[n] = value; | |
| 608 | + s->expires[n] = now; | |
| 609 | + icp_pit_reload(s, n); | |
| 610 | + break; | |
| 611 | + case 1: /* TimerValue */ | |
| 612 | + /* ??? Linux seems to want to write to this readonly register. | |
| 613 | + Ignore it. */ | |
| 614 | + break; | |
| 615 | + case 2: /* TimerControl */ | |
| 616 | + if (s->control[n] & 0x80) { | |
| 617 | + /* Pause the timer if it is running. This may cause some | |
| 618 | + inaccuracy dure to rounding, but avoids a whole lot of other | |
| 619 | + messyness. */ | |
| 620 | + s->count[n] = icp_pit_getcount(s, n, now); | |
| 621 | + } | |
| 622 | + s->control[n] = value; | |
| 623 | + if (n == 0) | |
| 624 | + s->freq[n] = ICP_BUS_FREQ; | |
| 625 | + else | |
| 626 | + s->freq[n] = 1000000; | |
| 627 | + /* ??? Need to recalculate expiry time after changing divisor. */ | |
| 628 | + switch ((value >> 2) & 3) { | |
| 629 | + case 1: s->freq[n] >>= 4; break; | |
| 630 | + case 2: s->freq[n] >>= 8; break; | |
| 631 | + } | |
| 632 | + if (s->control[n] & 0x80) { | |
| 633 | + /* Restart the timer if still enabled. */ | |
| 634 | + s->expires[n] = now; | |
| 635 | + icp_pit_reload(s, n); | |
| 636 | + } | |
| 637 | + break; | |
| 638 | + case 3: /* TimerIntClr */ | |
| 639 | + s->int_level[n] = 0; | |
| 640 | + break; | |
| 641 | + case 6: /* TimerBGLoad */ | |
| 642 | + s->limit[n] = value; | |
| 643 | + break; | |
| 644 | + default: | |
| 645 | + cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); | |
| 646 | + } | |
| 647 | + icp_pit_update(s, now); | |
| 648 | +} | |
| 649 | + | |
| 650 | +static void icp_pit_tick(void *opaque) | |
| 651 | +{ | |
| 652 | + int64_t now; | |
| 653 | + | |
| 654 | + now = qemu_get_clock(vm_clock); | |
| 655 | + icp_pit_update((icp_pit_state *)opaque, now); | |
| 656 | +} | |
| 657 | + | |
| 658 | +static CPUReadMemoryFunc *icp_pit_readfn[] = { | |
| 659 | + icp_pit_read, | |
| 660 | + icp_pit_read, | |
| 661 | + icp_pit_read | |
| 662 | +}; | |
| 663 | + | |
| 664 | +static CPUWriteMemoryFunc *icp_pit_writefn[] = { | |
| 665 | + icp_pit_write, | |
| 666 | + icp_pit_write, | |
| 667 | + icp_pit_write | |
| 668 | +}; | |
| 669 | + | |
| 670 | +static void icp_pit_init(uint32_t base, icp_pic_state *pic) | |
| 671 | +{ | |
| 672 | + int iomemtype; | |
| 673 | + icp_pit_state *s; | |
| 674 | + int n; | |
| 675 | + | |
| 676 | + s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); | |
| 677 | + s->base = base; | |
| 678 | + s->pic = pic; | |
| 679 | + s->freq[0] = ICP_BUS_FREQ; | |
| 680 | + s->freq[1] = 1000000; | |
| 681 | + s->freq[2] = 1000000; | |
| 682 | + for (n = 0; n < 3; n++) { | |
| 683 | + s->control[n] = 0x20; | |
| 684 | + s->count[n] = 0xffffffff; | |
| 685 | + } | |
| 686 | + | |
| 687 | + iomemtype = cpu_register_io_memory(0, icp_pit_readfn, | |
| 688 | + icp_pit_writefn, s); | |
| 689 | + cpu_register_physical_memory(base, 0x007fffff, iomemtype); | |
| 690 | + s->timer = qemu_new_timer(vm_clock, icp_pit_tick, s); | |
| 691 | + /* ??? Save/restore. */ | |
| 692 | +} | |
| 693 | + | |
| 694 | +/* ARM PrimeCell PL011 UART */ | |
| 695 | + | |
| 696 | +typedef struct { | |
| 697 | + uint32_t base; | |
| 698 | + uint32_t readbuff; | |
| 699 | + uint32_t flags; | |
| 700 | + uint32_t lcr; | |
| 701 | + uint32_t cr; | |
| 702 | + uint32_t dmacr; | |
| 703 | + uint32_t int_enabled; | |
| 704 | + uint32_t int_level; | |
| 705 | + uint32_t read_fifo[16]; | |
| 706 | + uint32_t ilpr; | |
| 707 | + uint32_t ibrd; | |
| 708 | + uint32_t fbrd; | |
| 709 | + uint32_t ifl; | |
| 710 | + int read_pos; | |
| 711 | + int read_count; | |
| 712 | + int read_trigger; | |
| 713 | + CharDriverState *chr; | |
| 714 | + icp_pic_state *pic; | |
| 715 | + int irq; | |
| 716 | +} pl011_state; | |
| 717 | + | |
| 718 | +#define PL011_INT_TX 0x20 | |
| 719 | +#define PL011_INT_RX 0x10 | |
| 720 | + | |
| 721 | +#define PL011_FLAG_TXFE 0x80 | |
| 722 | +#define PL011_FLAG_RXFF 0x40 | |
| 723 | +#define PL011_FLAG_TXFF 0x20 | |
| 724 | +#define PL011_FLAG_RXFE 0x10 | |
| 725 | + | |
| 726 | +static const unsigned char pl011_id[] = | |
| 727 | +{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | |
| 728 | + | |
| 729 | +static void pl011_update(pl011_state *s) | |
| 730 | +{ | |
| 731 | + uint32_t flags; | |
| 732 | + | |
| 733 | + flags = s->int_level & s->int_enabled; | |
| 734 | + icp_pic_set_level(s->pic, s->irq, flags != 0); | |
| 735 | +} | |
| 736 | + | |
| 737 | +static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) | |
| 738 | +{ | |
| 739 | + pl011_state *s = (pl011_state *)opaque; | |
| 740 | + uint32_t c; | |
| 741 | + | |
| 742 | + offset -= s->base; | |
| 743 | + if (offset >= 0xfe0 && offset < 0x1000) { | |
| 744 | + return pl011_id[(offset - 0xfe0) >> 2]; | |
| 745 | + } | |
| 746 | + switch (offset >> 2) { | |
| 747 | + case 0: /* UARTDR */ | |
| 748 | + s->flags &= ~PL011_FLAG_RXFF; | |
| 749 | + c = s->read_fifo[s->read_pos]; | |
| 750 | + if (s->read_count > 0) { | |
| 751 | + s->read_count--; | |
| 752 | + if (++s->read_pos == 16) | |
| 753 | + s->read_pos = 0; | |
| 754 | + } | |
| 755 | + if (s->read_count == 0) { | |
| 756 | + s->flags |= PL011_FLAG_RXFE; | |
| 757 | + } | |
| 758 | + if (s->read_count == s->read_trigger - 1) | |
| 759 | + s->int_level &= ~ PL011_INT_RX; | |
| 760 | + pl011_update(s); | |
| 761 | + return c; | |
| 762 | + case 1: /* UARTCR */ | |
| 763 | + return 0; | |
| 764 | + case 6: /* UARTFR */ | |
| 765 | + return s->flags; | |
| 766 | + case 8: /* UARTILPR */ | |
| 767 | + return s->ilpr; | |
| 768 | + case 9: /* UARTIBRD */ | |
| 769 | + return s->ibrd; | |
| 770 | + case 10: /* UARTFBRD */ | |
| 771 | + return s->fbrd; | |
| 772 | + case 11: /* UARTLCR_H */ | |
| 773 | + return s->lcr; | |
| 774 | + case 12: /* UARTCR */ | |
| 775 | + return s->cr; | |
| 776 | + case 13: /* UARTIFLS */ | |
| 777 | + return s->ifl; | |
| 778 | + case 14: /* UARTIMSC */ | |
| 779 | + return s->int_enabled; | |
| 780 | + case 15: /* UARTRIS */ | |
| 781 | + return s->int_level; | |
| 782 | + case 16: /* UARTMIS */ | |
| 783 | + return s->int_level & s->int_enabled; | |
| 784 | + case 18: /* UARTDMACR */ | |
| 785 | + return s->dmacr; | |
| 786 | + default: | |
| 787 | + cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); | |
| 788 | + return 0; | |
| 789 | + } | |
| 790 | +} | |
| 791 | + | |
| 792 | +static void pl011_set_read_trigger(pl011_state *s) | |
| 793 | +{ | |
| 794 | +#if 0 | |
| 795 | + /* The docs say the RX interrupt is triggered when the FIFO exceeds | |
| 796 | + the threshold. However linux only reads the FIFO in response to an | |
| 797 | + interrupt. Triggering the interrupt when the FIFO is non-empty seems | |
| 798 | + to make things work. */ | |
| 799 | + if (s->lcr & 0x10) | |
| 800 | + s->read_trigger = (s->ifl >> 1) & 0x1c; | |
| 801 | + else | |
| 802 | +#endif | |
| 803 | + s->read_trigger = 1; | |
| 804 | +} | |
| 805 | + | |
| 806 | +static void pl011_write(void *opaque, target_phys_addr_t offset, | |
| 807 | + uint32_t value) | |
| 808 | +{ | |
| 809 | + pl011_state *s = (pl011_state *)opaque; | |
| 810 | + unsigned char ch; | |
| 811 | + | |
| 812 | + offset -= s->base; | |
| 813 | + switch (offset >> 2) { | |
| 814 | + case 0: /* UARTDR */ | |
| 815 | + /* ??? Check if transmitter is enabled. */ | |
| 816 | + ch = value; | |
| 817 | + if (s->chr) | |
| 818 | + qemu_chr_write(s->chr, &ch, 1); | |
| 819 | + s->int_level |= PL011_INT_TX; | |
| 820 | + pl011_update(s); | |
| 821 | + break; | |
| 822 | + case 1: /* UARTCR */ | |
| 823 | + s->cr = value; | |
| 824 | + break; | |
| 825 | + case 8: /* UARTUARTILPR */ | |
| 826 | + s->ilpr = value; | |
| 827 | + break; | |
| 828 | + case 9: /* UARTIBRD */ | |
| 829 | + s->ibrd = value; | |
| 830 | + break; | |
| 831 | + case 10: /* UARTFBRD */ | |
| 832 | + s->fbrd = value; | |
| 833 | + break; | |
| 834 | + case 11: /* UARTLCR_H */ | |
| 835 | + s->lcr = value; | |
| 836 | + pl011_set_read_trigger(s); | |
| 837 | + break; | |
| 838 | + case 12: /* UARTCR */ | |
| 839 | + /* ??? Need to implement the enable and loopback bits. */ | |
| 840 | + s->cr = value; | |
| 841 | + break; | |
| 842 | + case 13: /* UARTIFS */ | |
| 843 | + s->ifl = value; | |
| 844 | + pl011_set_read_trigger(s); | |
| 845 | + break; | |
| 846 | + case 14: /* UARTIMSC */ | |
| 847 | + s->int_enabled = value; | |
| 848 | + pl011_update(s); | |
| 849 | + break; | |
| 850 | + case 17: /* UARTICR */ | |
| 851 | + s->int_level &= ~value; | |
| 852 | + pl011_update(s); | |
| 853 | + break; | |
| 854 | + case 18: /* UARTDMACR */ | |
| 855 | + s->dmacr = value; | |
| 856 | + if (value & 3) | |
| 857 | + cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); | |
| 858 | + break; | |
| 859 | + default: | |
| 860 | + cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); | |
| 861 | + } | |
| 862 | +} | |
| 863 | + | |
| 864 | +static int pl011_can_recieve(void *opaque) | |
| 865 | +{ | |
| 866 | + pl011_state *s = (pl011_state *)opaque; | |
| 867 | + | |
| 868 | + if (s->lcr & 0x10) | |
| 869 | + return s->read_count < 16; | |
| 870 | + else | |
| 871 | + return s->read_count < 1; | |
| 872 | +} | |
| 873 | + | |
| 874 | +static void pl011_recieve(void *opaque, const uint8_t *buf, int size) | |
| 875 | +{ | |
| 876 | + pl011_state *s = (pl011_state *)opaque; | |
| 877 | + int slot; | |
| 878 | + | |
| 879 | + slot = s->read_pos + s->read_count; | |
| 880 | + if (slot >= 16) | |
| 881 | + slot -= 16; | |
| 882 | + s->read_fifo[slot] = *buf; | |
| 883 | + s->read_count++; | |
| 884 | + s->flags &= ~PL011_FLAG_RXFE; | |
| 885 | + if (s->cr & 0x10 || s->read_count == 16) { | |
| 886 | + s->flags |= PL011_FLAG_RXFF; | |
| 887 | + } | |
| 888 | + if (s->read_count == s->read_trigger) { | |
| 889 | + s->int_level |= PL011_INT_RX; | |
| 890 | + pl011_update(s); | |
| 891 | + } | |
| 892 | +} | |
| 893 | + | |
| 894 | +static void pl011_event(void *opaque, int event) | |
| 895 | +{ | |
| 896 | + /* ??? Should probably implement break. */ | |
| 897 | +} | |
| 898 | + | |
| 899 | +static CPUReadMemoryFunc *pl011_readfn[] = { | |
| 900 | + pl011_read, | |
| 901 | + pl011_read, | |
| 902 | + pl011_read | |
| 903 | +}; | |
| 904 | + | |
| 905 | +static CPUWriteMemoryFunc *pl011_writefn[] = { | |
| 906 | + pl011_write, | |
| 907 | + pl011_write, | |
| 908 | + pl011_write | |
| 909 | +}; | |
| 910 | + | |
| 911 | +static void pl011_init(uint32_t base, icp_pic_state *pic, int irq, | |
| 912 | + CharDriverState *chr) | |
| 913 | +{ | |
| 914 | + int iomemtype; | |
| 915 | + pl011_state *s; | |
| 916 | + | |
| 917 | + s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); | |
| 918 | + iomemtype = cpu_register_io_memory(0, pl011_readfn, | |
| 919 | + pl011_writefn, s); | |
| 920 | + cpu_register_physical_memory(base, 0x007fffff, iomemtype); | |
| 921 | + s->base = base; | |
| 922 | + s->pic = pic; | |
| 923 | + s->irq = irq; | |
| 924 | + s->chr = chr; | |
| 925 | + s->read_trigger = 1; | |
| 926 | + s->ifl = 0x12; | |
| 927 | + s->cr = 0x300; | |
| 928 | + s->flags = 0x90; | |
| 929 | + if (chr){ | |
| 930 | + qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); | |
| 931 | + qemu_chr_add_event_handler(chr, pl011_event); | |
| 932 | + } | |
| 933 | + /* ??? Save/restore. */ | |
| 934 | +} | |
| 935 | + | |
| 936 | +/* CP control registers. */ | |
| 937 | +typedef struct { | |
| 938 | + uint32_t base; | |
| 939 | +} icp_control_state; | |
| 940 | + | |
| 941 | +static uint32_t icp_control_read(void *opaque, target_phys_addr_t offset) | |
| 942 | +{ | |
| 943 | + icp_control_state *s = (icp_control_state *)opaque; | |
| 944 | + offset -= s->base; | |
| 945 | + switch (offset >> 2) { | |
| 946 | + case 0: /* CP_IDFIELD */ | |
| 947 | + return 0x41034003; | |
| 948 | + case 1: /* CP_FLASHPROG */ | |
| 949 | + return 0; | |
| 950 | + case 2: /* CP_INTREG */ | |
| 951 | + return 0; | |
| 952 | + case 3: /* CP_DECODE */ | |
| 953 | + return 0x11; | |
| 954 | + default: | |
| 955 | + cpu_abort (cpu_single_env, "icp_control_read: Bad offset %x\n", offset); | |
| 956 | + return 0; | |
| 957 | + } | |
| 958 | +} | |
| 959 | + | |
| 960 | +static void icp_control_write(void *opaque, target_phys_addr_t offset, | |
| 961 | + uint32_t value) | |
| 962 | +{ | |
| 963 | + icp_control_state *s = (icp_control_state *)opaque; | |
| 964 | + offset -= s->base; | |
| 965 | + switch (offset >> 2) { | |
| 966 | + case 1: /* CP_FLASHPROG */ | |
| 967 | + case 2: /* CP_INTREG */ | |
| 968 | + case 3: /* CP_DECODE */ | |
| 969 | + /* Nothing interesting implemented yet. */ | |
| 970 | + break; | |
| 971 | + default: | |
| 972 | + cpu_abort (cpu_single_env, "icp_control_write: Bad offset %x\n", offset); | |
| 973 | + } | |
| 974 | +} | |
| 975 | +static CPUReadMemoryFunc *icp_control_readfn[] = { | |
| 976 | + icp_control_read, | |
| 977 | + icp_control_read, | |
| 978 | + icp_control_read | |
| 979 | +}; | |
| 980 | + | |
| 981 | +static CPUWriteMemoryFunc *icp_control_writefn[] = { | |
| 982 | + icp_control_write, | |
| 983 | + icp_control_write, | |
| 984 | + icp_control_write | |
| 985 | +}; | |
| 986 | + | |
| 987 | +static void icp_control_init(uint32_t base) | |
| 988 | +{ | |
| 989 | + int iomemtype; | |
| 990 | + icp_control_state *s; | |
| 991 | + | |
| 992 | + s = (icp_control_state *)qemu_mallocz(sizeof(icp_control_state)); | |
| 993 | + iomemtype = cpu_register_io_memory(0, icp_control_readfn, | |
| 994 | + icp_control_writefn, s); | |
| 995 | + cpu_register_physical_memory(base, 0x007fffff, iomemtype); | |
| 996 | + s->base = base; | |
| 997 | + /* ??? Save/restore. */ | |
| 998 | +} | |
| 999 | + | |
| 1000 | + | |
| 1001 | +/* Keyboard/Mouse Interface. */ | |
| 1002 | + | |
| 1003 | +typedef struct { | |
| 1004 | + void *dev; | |
| 1005 | + uint32_t base; | |
| 1006 | + uint32_t cr; | |
| 1007 | + uint32_t clk; | |
| 1008 | + uint32_t last; | |
| 1009 | + icp_pic_state *pic; | |
| 1010 | + int pending; | |
| 1011 | + int irq; | |
| 1012 | + int is_mouse; | |
| 1013 | +} icp_kmi_state; | |
| 1014 | + | |
| 1015 | +static void icp_kmi_update(void *opaque, int level) | |
| 1016 | +{ | |
| 1017 | + icp_kmi_state *s = (icp_kmi_state *)opaque; | |
| 1018 | + int raise; | |
| 1019 | + | |
| 1020 | + s->pending = level; | |
| 1021 | + raise = (s->pending && (s->cr & 0x10) != 0) | |
| 1022 | + || (s->cr & 0x08) != 0; | |
| 1023 | + icp_pic_set_level(s->pic, s->irq, raise); | |
| 1024 | +} | |
| 1025 | + | |
| 1026 | +static uint32_t icp_kmi_read(void *opaque, target_phys_addr_t offset) | |
| 1027 | +{ | |
| 1028 | + icp_kmi_state *s = (icp_kmi_state *)opaque; | |
| 1029 | + offset -= s->base; | |
| 1030 | + if (offset >= 0xfe0 && offset < 0x1000) | |
| 1031 | + return 0; | |
| 1032 | + | |
| 1033 | + switch (offset >> 2) { | |
| 1034 | + case 0: /* KMICR */ | |
| 1035 | + return s->cr; | |
| 1036 | + case 1: /* KMISTAT */ | |
| 1037 | + /* KMIC and KMID bits not implemented. */ | |
| 1038 | + if (s->pending) { | |
| 1039 | + return 0x10; | |
| 1040 | + } else { | |
| 1041 | + return 0; | |
| 1042 | + } | |
| 1043 | + case 2: /* KMIDATA */ | |
| 1044 | + if (s->pending) | |
| 1045 | + s->last = ps2_read_data(s->dev); | |
| 1046 | + return s->last; | |
| 1047 | + case 3: /* KMICLKDIV */ | |
| 1048 | + return s->clk; | |
| 1049 | + case 4: /* KMIIR */ | |
| 1050 | + return s->pending | 2; | |
| 1051 | + default: | |
| 1052 | + cpu_abort (cpu_single_env, "icp_kmi_read: Bad offset %x\n", offset); | |
| 1053 | + return 0; | |
| 1054 | + } | |
| 1055 | +} | |
| 1056 | + | |
| 1057 | +static void icp_kmi_write(void *opaque, target_phys_addr_t offset, | |
| 1058 | + uint32_t value) | |
| 1059 | +{ | |
| 1060 | + icp_kmi_state *s = (icp_kmi_state *)opaque; | |
| 1061 | + offset -= s->base; | |
| 1062 | + switch (offset >> 2) { | |
| 1063 | + case 0: /* KMICR */ | |
| 1064 | + s->cr = value; | |
| 1065 | + icp_kmi_update(s, s->pending); | |
| 1066 | + /* ??? Need to implement the enable/disable bit. */ | |
| 1067 | + break; | |
| 1068 | + case 2: /* KMIDATA */ | |
| 1069 | + /* ??? This should toggle the TX interrupt line. */ | |
| 1070 | + /* ??? This means kbd/mouse can block each other. */ | |
| 1071 | + if (s->is_mouse) { | |
| 1072 | + ps2_write_mouse(s->dev, value); | |
| 1073 | + } else { | |
| 1074 | + ps2_write_keyboard(s->dev, value); | |
| 1075 | + } | |
| 1076 | + break; | |
| 1077 | + case 3: /* KMICLKDIV */ | |
| 1078 | + s->clk = value; | |
| 1079 | + return; | |
| 1080 | + default: | |
| 1081 | + cpu_abort (cpu_single_env, "icp_kmi_write: Bad offset %x\n", offset); | |
| 1082 | + } | |
| 1083 | +} | |
| 1084 | +static CPUReadMemoryFunc *icp_kmi_readfn[] = { | |
| 1085 | + icp_kmi_read, | |
| 1086 | + icp_kmi_read, | |
| 1087 | + icp_kmi_read | |
| 1088 | +}; | |
| 1089 | + | |
| 1090 | +static CPUWriteMemoryFunc *icp_kmi_writefn[] = { | |
| 1091 | + icp_kmi_write, | |
| 1092 | + icp_kmi_write, | |
| 1093 | + icp_kmi_write | |
| 1094 | +}; | |
| 1095 | + | |
| 1096 | +static void icp_kmi_init(uint32_t base, icp_pic_state * pic, int irq, | |
| 1097 | + int is_mouse) | |
| 1098 | +{ | |
| 1099 | + int iomemtype; | |
| 1100 | + icp_kmi_state *s; | |
| 1101 | + | |
| 1102 | + s = (icp_kmi_state *)qemu_mallocz(sizeof(icp_kmi_state)); | |
| 1103 | + iomemtype = cpu_register_io_memory(0, icp_kmi_readfn, | |
| 1104 | + icp_kmi_writefn, s); | |
| 1105 | + cpu_register_physical_memory(base, 0x007fffff, iomemtype); | |
| 1106 | + s->base = base; | |
| 1107 | + s->pic = pic; | |
| 1108 | + s->irq = irq; | |
| 1109 | + s->is_mouse = is_mouse; | |
| 1110 | + if (is_mouse) | |
| 1111 | + s->dev = ps2_mouse_init(icp_kmi_update, s); | |
| 1112 | + else | |
| 1113 | + s->dev = ps2_kbd_init(icp_kmi_update, s); | |
| 1114 | + /* ??? Save/restore. */ | |
| 1115 | +} | |
| 1116 | + | |
| 1117 | +/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ | |
| 1118 | +static uint32_t bootloader[] = { | |
| 1119 | + 0xe3a00000, /* mov r0, #0 */ | |
| 1120 | + 0xe3a01013, /* mov r1, #0x13 */ | |
| 1121 | + 0xe3811c01, /* orr r1, r1, #0x100 */ | |
| 1122 | + 0xe59f2000, /* ldr r2, [pc, #0] */ | |
| 1123 | + 0xe59ff000, /* ldr pc, [pc, #0] */ | |
| 1124 | + 0, /* Address of kernel args. Set by integratorcp_init. */ | |
| 1125 | + 0 /* Kernel entry point. Set by integratorcp_init. */ | |
| 1126 | +}; | |
| 1127 | + | |
| 1128 | +static void set_kernel_args(uint32_t ram_size, int initrd_size, | |
| 1129 | + const char *kernel_cmdline) | |
| 1130 | +{ | |
| 1131 | + uint32_t *p; | |
| 1132 | + | |
| 1133 | + p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR); | |
| 1134 | + /* ATAG_CORE */ | |
| 1135 | + *(p++) = 5; | |
| 1136 | + *(p++) = 0x54410001; | |
| 1137 | + *(p++) = 1; | |
| 1138 | + *(p++) = 0x1000; | |
| 1139 | + *(p++) = 0; | |
| 1140 | + /* ATAG_MEM */ | |
| 1141 | + *(p++) = 4; | |
| 1142 | + *(p++) = 0x54410002; | |
| 1143 | + *(p++) = ram_size; | |
| 1144 | + *(p++) = 0; | |
| 1145 | + if (initrd_size) { | |
| 1146 | + /* ATAG_INITRD2 */ | |
| 1147 | + *(p++) = 4; | |
| 1148 | + *(p++) = 0x54420005; | |
| 1149 | + *(p++) = INITRD_LOAD_ADDR; | |
| 1150 | + *(p++) = initrd_size; | |
| 1151 | + } | |
| 1152 | + if (kernel_cmdline && *kernel_cmdline) { | |
| 1153 | + /* ATAG_CMDLINE */ | |
| 1154 | + int cmdline_size; | |
| 1155 | + | |
| 1156 | + cmdline_size = strlen(kernel_cmdline); | |
| 1157 | + memcpy (p + 2, kernel_cmdline, cmdline_size + 1); | |
| 1158 | + cmdline_size = (cmdline_size >> 2) + 1; | |
| 1159 | + *(p++) = cmdline_size + 2; | |
| 1160 | + *(p++) = 0x54410009; | |
| 1161 | + p += cmdline_size; | |
| 1162 | + } | |
| 1163 | + /* ATAG_END */ | |
| 1164 | + *(p++) = 0; | |
| 1165 | + *(p++) = 0; | |
| 1166 | +} | |
| 1167 | + | |
| 1168 | +/* Board init. */ | |
| 1169 | + | |
| 1170 | +static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, | |
| 1171 | + DisplayState *ds, const char **fd_filename, int snapshot, | |
| 1172 | + const char *kernel_filename, const char *kernel_cmdline, | |
| 1173 | + const char *initrd_filename) | |
| 1174 | +{ | |
| 1175 | + CPUState *env; | |
| 1176 | + uint32_t bios_offset; | |
| 1177 | + icp_pic_state *pic; | |
| 1178 | + int kernel_size; | |
| 1179 | + int initrd_size; | |
| 1180 | + | |
| 1181 | + env = cpu_init(); | |
| 1182 | + bios_offset = ram_size + vga_ram_size; | |
| 1183 | + /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */ | |
| 1184 | + /* ??? RAM shoud repeat to fill physical memory space. */ | |
| 1185 | + /* SDRAM at address zero*/ | |
| 1186 | + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); | |
| 1187 | + /* And again at address 0x80000000 */ | |
| 1188 | + cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM); | |
| 1189 | + | |
| 1190 | + integratorcm_init(ram_size >> 20, bios_offset); | |
| 1191 | + pic = icp_pic_init(0x14000000, env, -1); | |
| 1192 | + icp_pic_init(0xca000000, pic, 26); | |
| 1193 | + icp_pit_init(0x13000000, pic); | |
| 1194 | + pl011_init(0x16000000, pic, 1, serial_hds[0]); | |
| 1195 | + pl011_init(0x17000000, pic, 2, serial_hds[1]); | |
| 1196 | + icp_control_init(0xcb000000); | |
| 1197 | + icp_kmi_init(0x18000000, pic, 3, 0); | |
| 1198 | + icp_kmi_init(0x19000000, pic, 4, 1); | |
| 1199 | + | |
| 1200 | + /* Load the kernel. */ | |
| 1201 | + if (!kernel_filename) { | |
| 1202 | + fprintf(stderr, "Kernel image must be specified\n"); | |
| 1203 | + exit(1); | |
| 1204 | + } | |
| 1205 | + kernel_size = load_image(kernel_filename, | |
| 1206 | + phys_ram_base + KERNEL_LOAD_ADDR); | |
| 1207 | + if (kernel_size < 0) { | |
| 1208 | + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); | |
| 1209 | + exit(1); | |
| 1210 | + } | |
| 1211 | + if (initrd_filename) { | |
| 1212 | + initrd_size = load_image(initrd_filename, | |
| 1213 | + phys_ram_base + INITRD_LOAD_ADDR); | |
| 1214 | + if (initrd_size < 0) { | |
| 1215 | + fprintf(stderr, "qemu: could not load initrd '%s'\n", | |
| 1216 | + initrd_filename); | |
| 1217 | + exit(1); | |
| 1218 | + } | |
| 1219 | + } else { | |
| 1220 | + initrd_size = 0; | |
| 1221 | + } | |
| 1222 | + bootloader[5] = KERNEL_ARGS_ADDR; | |
| 1223 | + bootloader[6] = KERNEL_LOAD_ADDR; | |
| 1224 | + memcpy(phys_ram_base, bootloader, sizeof(bootloader)); | |
| 1225 | + set_kernel_args(ram_size, initrd_size, kernel_cmdline); | |
| 1226 | +} | |
| 1227 | + | |
| 1228 | +QEMUMachine integratorcp_machine = { | |
| 1229 | + "integratorcp", | |
| 1230 | + "ARM Integrator/CP", | |
| 1231 | + integratorcp_init, | |
| 1232 | +}; | ... | ... |
linux-user/main.c
| ... | ... | @@ -331,6 +331,7 @@ void cpu_loop(CPUARMState *env) |
| 331 | 331 | int trapnr; |
| 332 | 332 | unsigned int n, insn; |
| 333 | 333 | target_siginfo_t info; |
| 334 | + uint32_t addr; | |
| 334 | 335 | |
| 335 | 336 | for(;;) { |
| 336 | 337 | trapnr = cpu_arm_exec(env); |
| ... | ... | @@ -397,13 +398,18 @@ void cpu_loop(CPUARMState *env) |
| 397 | 398 | /* just indicate that signals should be handled asap */ |
| 398 | 399 | break; |
| 399 | 400 | case EXCP_PREFETCH_ABORT: |
| 401 | + addr = env->cp15.c6_data; | |
| 402 | + goto do_segv; | |
| 400 | 403 | case EXCP_DATA_ABORT: |
| 404 | + addr = env->cp15.c6_insn; | |
| 405 | + goto do_segv; | |
| 406 | + do_segv: | |
| 401 | 407 | { |
| 402 | 408 | info.si_signo = SIGSEGV; |
| 403 | 409 | info.si_errno = 0; |
| 404 | 410 | /* XXX: check env->error_code */ |
| 405 | 411 | info.si_code = TARGET_SEGV_MAPERR; |
| 406 | - info._sifields._sigfault._addr = env->cp15_6; | |
| 412 | + info._sifields._sigfault._addr = addr; | |
| 407 | 413 | queue_signal(info.si_signo, &info); |
| 408 | 414 | } |
| 409 | 415 | break; |
| ... | ... | @@ -1190,10 +1196,10 @@ int main(int argc, char **argv) |
| 1190 | 1196 | #elif defined(TARGET_ARM) |
| 1191 | 1197 | { |
| 1192 | 1198 | int i; |
| 1199 | + cpsr_write(env, regs->uregs[16], 0xffffffff); | |
| 1193 | 1200 | for(i = 0; i < 16; i++) { |
| 1194 | 1201 | env->regs[i] = regs->uregs[i]; |
| 1195 | 1202 | } |
| 1196 | - env->cpsr = regs->uregs[16]; | |
| 1197 | 1203 | ts->stack_base = info->start_stack; |
| 1198 | 1204 | ts->heap_base = info->brk; |
| 1199 | 1205 | /* This will be filled in on the first SYS_HEAPINFO call. */ | ... | ... |
linux-user/signal.c
| ... | ... | @@ -1003,7 +1003,7 @@ setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ |
| 1003 | 1003 | __put_user_error(env->regs[14], &sc->arm_lr, err); |
| 1004 | 1004 | __put_user_error(env->regs[15], &sc->arm_pc, err); |
| 1005 | 1005 | #ifdef TARGET_CONFIG_CPU_32 |
| 1006 | - __put_user_error(env->cpsr, &sc->arm_cpsr, err); | |
| 1006 | + __put_user_error(cpsr_read(env), &sc->arm_cpsr, err); | |
| 1007 | 1007 | #endif |
| 1008 | 1008 | |
| 1009 | 1009 | __put_user_error(/* current->thread.trap_no */ 0, &sc->trap_no, err); |
| ... | ... | @@ -1040,9 +1040,9 @@ setup_return(CPUState *env, struct emulated_sigaction *ka, |
| 1040 | 1040 | target_ulong retcode; |
| 1041 | 1041 | int thumb = 0; |
| 1042 | 1042 | #if defined(TARGET_CONFIG_CPU_32) |
| 1043 | +#if 0 | |
| 1043 | 1044 | target_ulong cpsr = env->cpsr; |
| 1044 | 1045 | |
| 1045 | -#if 0 | |
| 1046 | 1046 | /* |
| 1047 | 1047 | * Maybe we need to deliver a 32-bit signal to a 26-bit task. |
| 1048 | 1048 | */ |
| ... | ... | @@ -1088,9 +1088,11 @@ setup_return(CPUState *env, struct emulated_sigaction *ka, |
| 1088 | 1088 | env->regs[14] = retcode; |
| 1089 | 1089 | env->regs[15] = handler & (thumb ? ~1 : ~3); |
| 1090 | 1090 | |
| 1091 | +#if 0 | |
| 1091 | 1092 | #ifdef TARGET_CONFIG_CPU_32 |
| 1092 | 1093 | env->cpsr = cpsr; |
| 1093 | 1094 | #endif |
| 1095 | +#endif | |
| 1094 | 1096 | |
| 1095 | 1097 | return 0; |
| 1096 | 1098 | } |
| ... | ... | @@ -1157,6 +1159,7 @@ static int |
| 1157 | 1159 | restore_sigcontext(CPUState *env, struct target_sigcontext *sc) |
| 1158 | 1160 | { |
| 1159 | 1161 | int err = 0; |
| 1162 | + uint32_t cpsr; | |
| 1160 | 1163 | |
| 1161 | 1164 | __get_user_error(env->regs[0], &sc->arm_r0, err); |
| 1162 | 1165 | __get_user_error(env->regs[1], &sc->arm_r1, err); |
| ... | ... | @@ -1175,7 +1178,8 @@ restore_sigcontext(CPUState *env, struct target_sigcontext *sc) |
| 1175 | 1178 | __get_user_error(env->regs[14], &sc->arm_lr, err); |
| 1176 | 1179 | __get_user_error(env->regs[15], &sc->arm_pc, err); |
| 1177 | 1180 | #ifdef TARGET_CONFIG_CPU_32 |
| 1178 | - __get_user_error(env->cpsr, &sc->arm_cpsr, err); | |
| 1181 | + __get_user_error(cpsr, &sc->arm_cpsr, err); | |
| 1182 | + cpsr_write(env, cpsr, 0xffffffff); | |
| 1179 | 1183 | #endif |
| 1180 | 1184 | |
| 1181 | 1185 | err |= !valid_user_regs(env); | ... | ... |
softmmu_header.h
| ... | ... | @@ -59,6 +59,10 @@ |
| 59 | 59 | #define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) |
| 60 | 60 | #elif defined (TARGET_SPARC) |
| 61 | 61 | #define CPU_MEM_INDEX ((env->psrs) == 0) |
| 62 | +#elif defined (TARGET_ARM) | |
| 63 | +#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) | |
| 64 | +#else | |
| 65 | +#error unsupported CPU | |
| 62 | 66 | #endif |
| 63 | 67 | #define MMUSUFFIX _mmu |
| 64 | 68 | |
| ... | ... | @@ -72,6 +76,10 @@ |
| 72 | 76 | #define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) |
| 73 | 77 | #elif defined (TARGET_SPARC) |
| 74 | 78 | #define CPU_MEM_INDEX ((env->psrs) == 0) |
| 79 | +#elif defined (TARGET_ARM) | |
| 80 | +#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) | |
| 81 | +#else | |
| 82 | +#error unsupported CPU | |
| 75 | 83 | #endif |
| 76 | 84 | #define MMUSUFFIX _cmmu |
| 77 | 85 | ... | ... |
target-arm/cpu.h
| ... | ... | @@ -32,6 +32,8 @@ |
| 32 | 32 | #define EXCP_SWI 2 /* software interrupt */ |
| 33 | 33 | #define EXCP_PREFETCH_ABORT 3 |
| 34 | 34 | #define EXCP_DATA_ABORT 4 |
| 35 | +#define EXCP_IRQ 5 | |
| 36 | +#define EXCP_FIQ 6 | |
| 35 | 37 | |
| 36 | 38 | /* We currently assume float and double are IEEE single and double |
| 37 | 39 | precision respectively. |
| ... | ... | @@ -42,8 +44,22 @@ |
| 42 | 44 | */ |
| 43 | 45 | |
| 44 | 46 | typedef struct CPUARMState { |
| 47 | + /* Regs for current mode. */ | |
| 45 | 48 | uint32_t regs[16]; |
| 46 | - uint32_t cpsr; | |
| 49 | + /* Frequently accessed CPSR bits are stored separately for efficiently. | |
| 50 | + This contains all the other bits. Use cpsr_{read,write} to accless | |
| 51 | + the whole CPSR. */ | |
| 52 | + uint32_t uncached_cpsr; | |
| 53 | + uint32_t spsr; | |
| 54 | + | |
| 55 | + /* Banked registers. */ | |
| 56 | + uint32_t banked_spsr[6]; | |
| 57 | + uint32_t banked_r13[6]; | |
| 58 | + uint32_t banked_r14[6]; | |
| 59 | + | |
| 60 | + /* These hold r8-r12. */ | |
| 61 | + uint32_t usr_regs[5]; | |
| 62 | + uint32_t fiq_regs[5]; | |
| 47 | 63 | |
| 48 | 64 | /* cpsr flag cache for faster execution */ |
| 49 | 65 | uint32_t CF; /* 0 or 1 */ |
| ... | ... | @@ -53,8 +69,21 @@ typedef struct CPUARMState { |
| 53 | 69 | |
| 54 | 70 | int thumb; /* 0 = arm mode, 1 = thumb mode */ |
| 55 | 71 | |
| 56 | - /* coprocessor 15 (MMU) status */ | |
| 57 | - uint32_t cp15_6; | |
| 72 | + /* System control coprocessor (cp15) */ | |
| 73 | + struct { | |
| 74 | + uint32_t c1_sys; /* System control register. */ | |
| 75 | + uint32_t c1_coproc; /* Coprocessor access register. */ | |
| 76 | + uint32_t c2; /* MMU translation table base. */ | |
| 77 | + uint32_t c3; /* MMU domain access control register. */ | |
| 78 | + uint32_t c5_insn; /* Fault status registers. */ | |
| 79 | + uint32_t c5_data; | |
| 80 | + uint32_t c6_insn; /* Fault address registers. */ | |
| 81 | + uint32_t c6_data; | |
| 82 | + uint32_t c9_insn; /* Cache lockdown registers. */ | |
| 83 | + uint32_t c9_data; | |
| 84 | + uint32_t c13_fcse; /* FCSE PID. */ | |
| 85 | + uint32_t c13_context; /* Context ID. */ | |
| 86 | + } cp15; | |
| 58 | 87 | |
| 59 | 88 | /* exception/interrupt handling */ |
| 60 | 89 | jmp_buf jmp_env; |
| ... | ... | @@ -87,6 +116,9 @@ typedef struct CPUARMState { |
| 87 | 116 | CPUARMState *cpu_arm_init(void); |
| 88 | 117 | int cpu_arm_exec(CPUARMState *s); |
| 89 | 118 | void cpu_arm_close(CPUARMState *s); |
| 119 | +void do_interrupt(CPUARMState *); | |
| 120 | +void switch_mode(CPUARMState *, int); | |
| 121 | + | |
| 90 | 122 | /* you can call this signal handler from your SIGBUS and SIGSEGV |
| 91 | 123 | signal handlers to inform the virtual CPU of exceptions. non zero |
| 92 | 124 | is returned if the signal was handled by the virtual CPU. */ |
| ... | ... | @@ -94,7 +126,69 @@ struct siginfo; |
| 94 | 126 | int cpu_arm_signal_handler(int host_signum, struct siginfo *info, |
| 95 | 127 | void *puc); |
| 96 | 128 | |
| 129 | +#define CPSR_M (0x1f) | |
| 130 | +#define CPSR_T (1 << 5) | |
| 131 | +#define CPSR_F (1 << 6) | |
| 132 | +#define CPSR_I (1 << 7) | |
| 133 | +#define CPSR_A (1 << 8) | |
| 134 | +#define CPSR_E (1 << 9) | |
| 135 | +#define CPSR_IT_2_7 (0xfc00) | |
| 136 | +/* Bits 20-23 reserved. */ | |
| 137 | +#define CPSR_J (1 << 24) | |
| 138 | +#define CPSR_IT_0_1 (3 << 25) | |
| 139 | +#define CPSR_Q (1 << 27) | |
| 140 | +#define CPSR_NZCV (0xf << 28) | |
| 141 | + | |
| 142 | +#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV) | |
| 143 | +/* Return the current CPSR value. */ | |
| 144 | +static inline uint32_t cpsr_read(CPUARMState *env) | |
| 145 | +{ | |
| 146 | + int ZF; | |
| 147 | + ZF = (env->NZF == 0); | |
| 148 | + return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) | | |
| 149 | + (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) | |
| 150 | + | (env->thumb << 5); | |
| 151 | +} | |
| 152 | + | |
| 153 | +/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */ | |
| 154 | +static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) | |
| 155 | +{ | |
| 156 | + /* NOTE: N = 1 and Z = 1 cannot be stored currently */ | |
| 157 | + if (mask & CPSR_NZCV) { | |
| 158 | + env->NZF = (val & 0xc0000000) ^ 0x40000000; | |
| 159 | + env->CF = (val >> 29) & 1; | |
| 160 | + env->VF = (val << 3) & 0x80000000; | |
| 161 | + } | |
| 162 | + if (mask & CPSR_Q) | |
| 163 | + env->QF = ((val & CPSR_Q) != 0); | |
| 164 | + if (mask & CPSR_T) | |
| 165 | + env->thumb = ((val & CPSR_T) != 0); | |
| 166 | + | |
| 167 | + if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { | |
| 168 | + switch_mode(env, val & CPSR_M); | |
| 169 | + } | |
| 170 | + mask &= ~CACHED_CPSR_BITS; | |
| 171 | + env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); | |
| 172 | +} | |
| 173 | + | |
| 174 | +enum arm_cpu_mode { | |
| 175 | + ARM_CPU_MODE_USR = 0x10, | |
| 176 | + ARM_CPU_MODE_FIQ = 0x11, | |
| 177 | + ARM_CPU_MODE_IRQ = 0x12, | |
| 178 | + ARM_CPU_MODE_SVC = 0x13, | |
| 179 | + ARM_CPU_MODE_ABT = 0x17, | |
| 180 | + ARM_CPU_MODE_UND = 0x1b, | |
| 181 | + ARM_CPU_MODE_SYS = 0x1f | |
| 182 | +}; | |
| 183 | + | |
| 184 | +#if defined(CONFIG_USER_ONLY) | |
| 97 | 185 | #define TARGET_PAGE_BITS 12 |
| 186 | +#else | |
| 187 | +/* The ARM MMU allows 1k pages. */ | |
| 188 | +/* ??? Linux doesn't actually use these, and they're deprecated in recent | |
| 189 | + architecture revisions. Maybe an a configure option to disable them. */ | |
| 190 | +#define TARGET_PAGE_BITS 10 | |
| 191 | +#endif | |
| 98 | 192 | #include "cpu-all.h" |
| 99 | 193 | |
| 100 | 194 | #endif | ... | ... |
target-arm/exec.h
| ... | ... | @@ -34,16 +34,6 @@ register uint32_t T2 asm(AREG3); |
| 34 | 34 | #include "cpu.h" |
| 35 | 35 | #include "exec-all.h" |
| 36 | 36 | |
| 37 | -/* Implemented CPSR bits. */ | |
| 38 | -#define CACHED_CPSR_BITS 0xf8000000 | |
| 39 | -static inline int compute_cpsr(void) | |
| 40 | -{ | |
| 41 | - int ZF; | |
| 42 | - ZF = (env->NZF == 0); | |
| 43 | - return env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) | | |
| 44 | - (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27); | |
| 45 | -} | |
| 46 | - | |
| 47 | 37 | static inline void env_to_regs(void) |
| 48 | 38 | { |
| 49 | 39 | } |
| ... | ... | @@ -55,10 +45,17 @@ static inline void regs_to_env(void) |
| 55 | 45 | int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
| 56 | 46 | int is_user, int is_softmmu); |
| 57 | 47 | |
| 48 | +#if !defined(CONFIG_USER_ONLY) | |
| 49 | +#include "softmmu_exec.h" | |
| 50 | +#endif | |
| 51 | + | |
| 58 | 52 | /* In op_helper.c */ |
| 59 | 53 | |
| 60 | 54 | void cpu_lock(void); |
| 61 | 55 | void cpu_unlock(void); |
| 56 | +void helper_set_cp15(CPUState *, uint32_t, uint32_t); | |
| 57 | +uint32_t helper_get_cp15(CPUState *, uint32_t); | |
| 58 | + | |
| 62 | 59 | void cpu_loop_exit(void); |
| 63 | 60 | |
| 64 | 61 | void raise_exception(int); | ... | ... |
target-arm/helper.c
0 โ 100644
| 1 | +#include <stdio.h> | |
| 2 | +#include <stdlib.h> | |
| 3 | +#include <string.h> | |
| 4 | + | |
| 5 | +#include "cpu.h" | |
| 6 | +#include "exec-all.h" | |
| 7 | + | |
| 8 | +#if defined(CONFIG_USER_ONLY) | |
| 9 | + | |
| 10 | +void do_interrupt (CPUState *env) | |
| 11 | +{ | |
| 12 | + env->exception_index = -1; | |
| 13 | +} | |
| 14 | + | |
| 15 | +int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, | |
| 16 | + int is_user, int is_softmmu) | |
| 17 | +{ | |
| 18 | + if (rw == 2) { | |
| 19 | + env->exception_index = EXCP_PREFETCH_ABORT; | |
| 20 | + env->cp15.c6_insn = address; | |
| 21 | + } else { | |
| 22 | + env->exception_index = EXCP_DATA_ABORT; | |
| 23 | + env->cp15.c6_data = address; | |
| 24 | + } | |
| 25 | + return 1; | |
| 26 | +} | |
| 27 | + | |
| 28 | +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | |
| 29 | +{ | |
| 30 | + return addr; | |
| 31 | +} | |
| 32 | + | |
| 33 | +/* These should probably raise undefined insn exceptions. */ | |
| 34 | +void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) | |
| 35 | +{ | |
| 36 | + cpu_abort(env, "cp15 insn %08x\n", insn); | |
| 37 | +} | |
| 38 | + | |
| 39 | +uint32_t helper_get_cp15(CPUState *env, uint32_t insn) | |
| 40 | +{ | |
| 41 | + cpu_abort(env, "cp15 insn %08x\n", insn); | |
| 42 | + return 0; | |
| 43 | +} | |
| 44 | + | |
| 45 | +void switch_mode(CPUState *env, int mode) | |
| 46 | +{ | |
| 47 | + if (mode != ARM_CPU_MODE_USR) | |
| 48 | + cpu_abort(env, "Tried to switch out of user mode\n"); | |
| 49 | +} | |
| 50 | + | |
| 51 | +#else | |
| 52 | + | |
| 53 | +/* Map CPU modes onto saved register banks. */ | |
| 54 | +static inline int bank_number (int mode) | |
| 55 | +{ | |
| 56 | + switch (mode) { | |
| 57 | + case ARM_CPU_MODE_USR: | |
| 58 | + case ARM_CPU_MODE_SYS: | |
| 59 | + return 0; | |
| 60 | + case ARM_CPU_MODE_SVC: | |
| 61 | + return 1; | |
| 62 | + case ARM_CPU_MODE_ABT: | |
| 63 | + return 2; | |
| 64 | + case ARM_CPU_MODE_UND: | |
| 65 | + return 3; | |
| 66 | + case ARM_CPU_MODE_IRQ: | |
| 67 | + return 4; | |
| 68 | + case ARM_CPU_MODE_FIQ: | |
| 69 | + return 5; | |
| 70 | + } | |
| 71 | + cpu_abort(cpu_single_env, "Bad mode %x\n", mode); | |
| 72 | + return -1; | |
| 73 | +} | |
| 74 | + | |
| 75 | +void switch_mode(CPUState *env, int mode) | |
| 76 | +{ | |
| 77 | + int old_mode; | |
| 78 | + int i; | |
| 79 | + | |
| 80 | + old_mode = env->uncached_cpsr & CPSR_M; | |
| 81 | + if (mode == old_mode) | |
| 82 | + return; | |
| 83 | + | |
| 84 | + if (old_mode == ARM_CPU_MODE_FIQ) { | |
| 85 | + memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); | |
| 86 | + memcpy (env->regs, env->usr_regs + 8, 5 * sizeof(uint32_t)); | |
| 87 | + } else if (mode == ARM_CPU_MODE_FIQ) { | |
| 88 | + memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); | |
| 89 | + memcpy (env->regs, env->fiq_regs + 8, 5 * sizeof(uint32_t)); | |
| 90 | + } | |
| 91 | + | |
| 92 | + i = bank_number(old_mode); | |
| 93 | + env->banked_r13[i] = env->regs[13]; | |
| 94 | + env->banked_r14[i] = env->regs[14]; | |
| 95 | + env->banked_spsr[i] = env->spsr; | |
| 96 | + | |
| 97 | + i = bank_number(mode); | |
| 98 | + env->regs[13] = env->banked_r13[i]; | |
| 99 | + env->regs[14] = env->banked_r14[i]; | |
| 100 | + env->spsr = env->banked_spsr[i]; | |
| 101 | +} | |
| 102 | + | |
| 103 | +/* Handle a CPU exception. */ | |
| 104 | +void do_interrupt(CPUARMState *env) | |
| 105 | +{ | |
| 106 | + uint32_t addr; | |
| 107 | + uint32_t mask; | |
| 108 | + int new_mode; | |
| 109 | + uint32_t offset; | |
| 110 | + | |
| 111 | + /* TODO: Vectored interrupt controller. */ | |
| 112 | + switch (env->exception_index) { | |
| 113 | + case EXCP_UDEF: | |
| 114 | + new_mode = ARM_CPU_MODE_UND; | |
| 115 | + addr = 0x04; | |
| 116 | + mask = CPSR_I; | |
| 117 | + if (env->thumb) | |
| 118 | + offset = 2; | |
| 119 | + else | |
| 120 | + offset = 4; | |
| 121 | + break; | |
| 122 | + case EXCP_SWI: | |
| 123 | + new_mode = ARM_CPU_MODE_SVC; | |
| 124 | + addr = 0x08; | |
| 125 | + mask = CPSR_I; | |
| 126 | + /* The PC already points to the next instructon. */ | |
| 127 | + offset = 0; | |
| 128 | + break; | |
| 129 | + case EXCP_PREFETCH_ABORT: | |
| 130 | + new_mode = ARM_CPU_MODE_ABT; | |
| 131 | + addr = 0x0c; | |
| 132 | + mask = CPSR_A | CPSR_I; | |
| 133 | + offset = 4; | |
| 134 | + break; | |
| 135 | + case EXCP_DATA_ABORT: | |
| 136 | + new_mode = ARM_CPU_MODE_ABT; | |
| 137 | + addr = 0x10; | |
| 138 | + mask = CPSR_A | CPSR_I; | |
| 139 | + offset = 8; | |
| 140 | + break; | |
| 141 | + case EXCP_IRQ: | |
| 142 | + new_mode = ARM_CPU_MODE_IRQ; | |
| 143 | + addr = 0x18; | |
| 144 | + /* Disable IRQ and imprecise data aborts. */ | |
| 145 | + mask = CPSR_A | CPSR_I; | |
| 146 | + offset = 4; | |
| 147 | + break; | |
| 148 | + case EXCP_FIQ: | |
| 149 | + new_mode = ARM_CPU_MODE_FIQ; | |
| 150 | + addr = 0x1c; | |
| 151 | + /* Disable FIQ, IRQ and imprecise data aborts. */ | |
| 152 | + mask = CPSR_A | CPSR_I | CPSR_F; | |
| 153 | + offset = 4; | |
| 154 | + break; | |
| 155 | + default: | |
| 156 | + cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index); | |
| 157 | + return; /* Never happens. Keep compiler happy. */ | |
| 158 | + } | |
| 159 | + /* High vectors. */ | |
| 160 | + if (env->cp15.c1_sys & (1 << 13)) { | |
| 161 | + addr += 0xffff0000; | |
| 162 | + } | |
| 163 | + switch_mode (env, new_mode); | |
| 164 | + env->spsr = cpsr_read(env); | |
| 165 | + /* Switch to the new mode, and clear the thumb bit. */ | |
| 166 | + /* ??? Thumb interrupt handlers not implemented. */ | |
| 167 | + env->uncached_cpsr = (env->uncached_cpsr & ~(CPSR_M | CPSR_T)) | new_mode; | |
| 168 | + env->uncached_cpsr |= mask; | |
| 169 | + env->regs[14] = env->regs[15] + offset; | |
| 170 | + env->regs[15] = addr; | |
| 171 | + env->interrupt_request |= CPU_INTERRUPT_EXITTB; | |
| 172 | +} | |
| 173 | + | |
| 174 | +/* Check section/page access permissions. | |
| 175 | + Returns the page protection flags, or zero if the access is not | |
| 176 | + permitted. */ | |
| 177 | +static inline int check_ap(CPUState *env, int ap, int domain, int access_type, | |
| 178 | + int is_user) | |
| 179 | +{ | |
| 180 | + if (domain == 3) | |
| 181 | + return PAGE_READ | PAGE_WRITE; | |
| 182 | + | |
| 183 | + switch (ap) { | |
| 184 | + case 0: | |
| 185 | + if (access_type != 1) | |
| 186 | + return 0; | |
| 187 | + switch ((env->cp15.c1_sys >> 8) & 3) { | |
| 188 | + case 1: | |
| 189 | + return is_user ? 0 : PAGE_READ; | |
| 190 | + case 2: | |
| 191 | + return PAGE_READ; | |
| 192 | + default: | |
| 193 | + return 0; | |
| 194 | + } | |
| 195 | + case 1: | |
| 196 | + return is_user ? 0 : PAGE_READ | PAGE_WRITE; | |
| 197 | + case 2: | |
| 198 | + if (is_user) | |
| 199 | + return (access_type == 1) ? 0 : PAGE_READ; | |
| 200 | + else | |
| 201 | + return PAGE_READ | PAGE_WRITE; | |
| 202 | + case 3: | |
| 203 | + return PAGE_READ | PAGE_WRITE; | |
| 204 | + default: | |
| 205 | + abort(); | |
| 206 | + } | |
| 207 | +} | |
| 208 | + | |
| 209 | +static int get_phys_addr(CPUState *env, uint32_t address, int access_type, | |
| 210 | + int is_user, uint32_t *phys_ptr, int *prot) | |
| 211 | +{ | |
| 212 | + int code; | |
| 213 | + uint32_t table; | |
| 214 | + uint32_t desc; | |
| 215 | + int type; | |
| 216 | + int ap; | |
| 217 | + int domain; | |
| 218 | + uint32_t phys_addr; | |
| 219 | + | |
| 220 | + /* Fast Context Switch Extension. */ | |
| 221 | + if (address < 0x02000000) | |
| 222 | + address += env->cp15.c13_fcse; | |
| 223 | + | |
| 224 | + if ((env->cp15.c1_sys & 1) == 0) { | |
| 225 | + /* MMU diusabled. */ | |
| 226 | + *phys_ptr = address; | |
| 227 | + *prot = PAGE_READ | PAGE_WRITE; | |
| 228 | + } else { | |
| 229 | + /* Pagetable walk. */ | |
| 230 | + /* Lookup l1 descriptor. */ | |
| 231 | + table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc); | |
| 232 | + desc = ldl_phys(table); | |
| 233 | + type = (desc & 3); | |
| 234 | + domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3; | |
| 235 | + if (type == 0) { | |
| 236 | + /* Secton translation fault. */ | |
| 237 | + code = 5; | |
| 238 | + goto do_fault; | |
| 239 | + } | |
| 240 | + if (domain == 0 || domain == 2) { | |
| 241 | + if (type == 2) | |
| 242 | + code = 9; /* Section domain fault. */ | |
| 243 | + else | |
| 244 | + code = 11; /* Page domain fault. */ | |
| 245 | + goto do_fault; | |
| 246 | + } | |
| 247 | + if (type == 2) { | |
| 248 | + /* 1Mb section. */ | |
| 249 | + phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); | |
| 250 | + ap = (desc >> 10) & 3; | |
| 251 | + code = 13; | |
| 252 | + } else { | |
| 253 | + /* Lookup l2 entry. */ | |
| 254 | + table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); | |
| 255 | + desc = ldl_phys(table); | |
| 256 | + switch (desc & 3) { | |
| 257 | + case 0: /* Page translation fault. */ | |
| 258 | + code = 7; | |
| 259 | + goto do_fault; | |
| 260 | + case 1: /* 64k page. */ | |
| 261 | + phys_addr = (desc & 0xffff0000) | (address & 0xffff); | |
| 262 | + ap = (desc >> (4 + ((address >> 13) & 6))) & 3; | |
| 263 | + break; | |
| 264 | + case 2: /* 4k page. */ | |
| 265 | + phys_addr = (desc & 0xfffff000) | (address & 0xfff); | |
| 266 | + ap = (desc >> (4 + ((address >> 13) & 6))) & 3; | |
| 267 | + break; | |
| 268 | + case 3: /* 1k page. */ | |
| 269 | + if (type == 1) { | |
| 270 | + /* Page translation fault. */ | |
| 271 | + code = 7; | |
| 272 | + goto do_fault; | |
| 273 | + } | |
| 274 | + phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); | |
| 275 | + ap = (desc >> 4) & 3; | |
| 276 | + break; | |
| 277 | + default: | |
| 278 | + /* Never happens, but compiler isn't smart enough to tell. */ | |
| 279 | + abort(); | |
| 280 | + } | |
| 281 | + code = 15; | |
| 282 | + } | |
| 283 | + *prot = check_ap(env, ap, domain, access_type, is_user); | |
| 284 | + if (!*prot) { | |
| 285 | + /* Access permission fault. */ | |
| 286 | + goto do_fault; | |
| 287 | + } | |
| 288 | + *phys_ptr = phys_addr; | |
| 289 | + } | |
| 290 | + return 0; | |
| 291 | +do_fault: | |
| 292 | + return code | (domain << 4); | |
| 293 | +} | |
| 294 | + | |
| 295 | +int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, | |
| 296 | + int access_type, int is_user, int is_softmmu) | |
| 297 | +{ | |
| 298 | + uint32_t phys_addr; | |
| 299 | + int prot; | |
| 300 | + int ret; | |
| 301 | + | |
| 302 | + ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot); | |
| 303 | + if (ret == 0) { | |
| 304 | + /* Map a single [sub]page. */ | |
| 305 | + phys_addr &= ~(uint32_t)0x3ff; | |
| 306 | + address &= ~(uint32_t)0x3ff; | |
| 307 | + return tlb_set_page (env, address, phys_addr, prot, is_user, | |
| 308 | + is_softmmu); | |
| 309 | + } | |
| 310 | + | |
| 311 | + if (access_type == 2) { | |
| 312 | + env->cp15.c5_insn = ret; | |
| 313 | + env->cp15.c6_insn = address; | |
| 314 | + env->exception_index = EXCP_PREFETCH_ABORT; | |
| 315 | + } else { | |
| 316 | + env->cp15.c5_data = ret; | |
| 317 | + env->cp15.c6_data = address; | |
| 318 | + env->exception_index = EXCP_DATA_ABORT; | |
| 319 | + } | |
| 320 | + return 1; | |
| 321 | +} | |
| 322 | + | |
| 323 | +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | |
| 324 | +{ | |
| 325 | + uint32_t phys_addr; | |
| 326 | + int prot; | |
| 327 | + int ret; | |
| 328 | + | |
| 329 | + ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot); | |
| 330 | + | |
| 331 | + if (ret != 0) | |
| 332 | + return -1; | |
| 333 | + | |
| 334 | + return phys_addr; | |
| 335 | +} | |
| 336 | + | |
| 337 | +void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) | |
| 338 | +{ | |
| 339 | + uint32_t op2; | |
| 340 | + | |
| 341 | + op2 = (insn >> 5) & 7; | |
| 342 | + switch ((insn >> 16) & 0xf) { | |
| 343 | + case 0: /* ID codes. */ | |
| 344 | + goto bad_reg; | |
| 345 | + case 1: /* System configuration. */ | |
| 346 | + switch (op2) { | |
| 347 | + case 0: | |
| 348 | + env->cp15.c1_sys = val; | |
| 349 | + /* ??? Lots of these bits are not implemented. */ | |
| 350 | + /* This may enable/disable the MMU, so do a TLB flush. */ | |
| 351 | + tlb_flush(env, 1); | |
| 352 | + break; | |
| 353 | + case 2: | |
| 354 | + env->cp15.c1_coproc = val; | |
| 355 | + /* ??? Is this safe when called from within a TB? */ | |
| 356 | + tb_flush(env); | |
| 357 | + default: | |
| 358 | + goto bad_reg; | |
| 359 | + } | |
| 360 | + break; | |
| 361 | + case 2: /* MMU Page table control. */ | |
| 362 | + env->cp15.c2 = val; | |
| 363 | + break; | |
| 364 | + case 3: /* MMU Domain access control. */ | |
| 365 | + env->cp15.c3 = val; | |
| 366 | + break; | |
| 367 | + case 4: /* Reserved. */ | |
| 368 | + goto bad_reg; | |
| 369 | + case 5: /* MMU Fault status. */ | |
| 370 | + switch (op2) { | |
| 371 | + case 0: | |
| 372 | + env->cp15.c5_data = val; | |
| 373 | + break; | |
| 374 | + case 1: | |
| 375 | + env->cp15.c5_insn = val; | |
| 376 | + break; | |
| 377 | + default: | |
| 378 | + goto bad_reg; | |
| 379 | + } | |
| 380 | + break; | |
| 381 | + case 6: /* MMU Fault address. */ | |
| 382 | + switch (op2) { | |
| 383 | + case 0: | |
| 384 | + env->cp15.c6_data = val; | |
| 385 | + break; | |
| 386 | + case 1: | |
| 387 | + env->cp15.c6_insn = val; | |
| 388 | + break; | |
| 389 | + default: | |
| 390 | + goto bad_reg; | |
| 391 | + } | |
| 392 | + break; | |
| 393 | + case 7: /* Cache control. */ | |
| 394 | + /* No cache, so nothing to do. */ | |
| 395 | + break; | |
| 396 | + case 8: /* MMU TLB control. */ | |
| 397 | + switch (op2) { | |
| 398 | + case 0: /* Invalidate all. */ | |
| 399 | + tlb_flush(env, 0); | |
| 400 | + break; | |
| 401 | + case 1: /* Invalidate single TLB entry. */ | |
| 402 | +#if 0 | |
| 403 | + /* ??? This is wrong for large pages and sections. */ | |
| 404 | + /* As an ugly hack to make linux work we always flush a 4K | |
| 405 | + pages. */ | |
| 406 | + val &= 0xfffff000; | |
| 407 | + tlb_flush_page(env, val); | |
| 408 | + tlb_flush_page(env, val + 0x400); | |
| 409 | + tlb_flush_page(env, val + 0x800); | |
| 410 | + tlb_flush_page(env, val + 0xc00); | |
| 411 | +#else | |
| 412 | + tlb_flush(env, 1); | |
| 413 | +#endif | |
| 414 | + break; | |
| 415 | + default: | |
| 416 | + goto bad_reg; | |
| 417 | + } | |
| 418 | + break; | |
| 419 | + case 9: /* Cache lockdown. */ | |
| 420 | + switch (op2) { | |
| 421 | + case 0: | |
| 422 | + env->cp15.c9_data = val; | |
| 423 | + break; | |
| 424 | + case 1: | |
| 425 | + env->cp15.c9_insn = val; | |
| 426 | + break; | |
| 427 | + default: | |
| 428 | + goto bad_reg; | |
| 429 | + } | |
| 430 | + break; | |
| 431 | + case 10: /* MMU TLB lockdown. */ | |
| 432 | + /* ??? TLB lockdown not implemented. */ | |
| 433 | + break; | |
| 434 | + case 11: /* TCM DMA control. */ | |
| 435 | + case 12: /* Reserved. */ | |
| 436 | + goto bad_reg; | |
| 437 | + case 13: /* Process ID. */ | |
| 438 | + switch (op2) { | |
| 439 | + case 0: | |
| 440 | + env->cp15.c9_data = val; | |
| 441 | + break; | |
| 442 | + case 1: | |
| 443 | + env->cp15.c9_insn = val; | |
| 444 | + break; | |
| 445 | + default: | |
| 446 | + goto bad_reg; | |
| 447 | + } | |
| 448 | + break; | |
| 449 | + case 14: /* Reserved. */ | |
| 450 | + goto bad_reg; | |
| 451 | + case 15: /* Implementation specific. */ | |
| 452 | + /* ??? Internal registers not implemented. */ | |
| 453 | + break; | |
| 454 | + } | |
| 455 | + return; | |
| 456 | +bad_reg: | |
| 457 | + /* ??? For debugging only. Should raise illegal instruction exception. */ | |
| 458 | + cpu_abort(env, "Unimplemented cp15 register read\n"); | |
| 459 | +} | |
| 460 | + | |
| 461 | +uint32_t helper_get_cp15(CPUState *env, uint32_t insn) | |
| 462 | +{ | |
| 463 | + uint32_t op2; | |
| 464 | + | |
| 465 | + op2 = (insn >> 5) & 7; | |
| 466 | + switch ((insn >> 16) & 0xf) { | |
| 467 | + case 0: /* ID codes. */ | |
| 468 | + switch (op2) { | |
| 469 | + default: /* Device ID. */ | |
| 470 | + return 0x4106a262; | |
| 471 | + case 1: /* Cache Type. */ | |
| 472 | + return 0x1dd20d2; | |
| 473 | + case 2: /* TCM status. */ | |
| 474 | + return 0; | |
| 475 | + } | |
| 476 | + case 1: /* System configuration. */ | |
| 477 | + switch (op2) { | |
| 478 | + case 0: /* Control register. */ | |
| 479 | + return env->cp15.c1_sys; | |
| 480 | + case 1: /* Auxiliary control register. */ | |
| 481 | + return 1; | |
| 482 | + case 2: /* Coprocessor access register. */ | |
| 483 | + return env->cp15.c1_coproc; | |
| 484 | + default: | |
| 485 | + goto bad_reg; | |
| 486 | + } | |
| 487 | + case 2: /* MMU Page table control. */ | |
| 488 | + return env->cp15.c2; | |
| 489 | + case 3: /* MMU Domain access control. */ | |
| 490 | + return env->cp15.c3; | |
| 491 | + case 4: /* Reserved. */ | |
| 492 | + goto bad_reg; | |
| 493 | + case 5: /* MMU Fault status. */ | |
| 494 | + switch (op2) { | |
| 495 | + case 0: | |
| 496 | + return env->cp15.c5_data; | |
| 497 | + case 1: | |
| 498 | + return env->cp15.c5_insn; | |
| 499 | + default: | |
| 500 | + goto bad_reg; | |
| 501 | + } | |
| 502 | + case 6: /* MMU Fault address. */ | |
| 503 | + switch (op2) { | |
| 504 | + case 0: | |
| 505 | + return env->cp15.c6_data; | |
| 506 | + case 1: | |
| 507 | + return env->cp15.c6_insn; | |
| 508 | + default: | |
| 509 | + goto bad_reg; | |
| 510 | + } | |
| 511 | + case 7: /* Cache control. */ | |
| 512 | + /* ??? This is for test, clean and invaidate operations that set the | |
| 513 | + Z flag. We can't represent N = Z = 1, so it also clears clears | |
| 514 | + the N flag. Oh well. */ | |
| 515 | + env->NZF = 0; | |
| 516 | + return 0; | |
| 517 | + case 8: /* MMU TLB control. */ | |
| 518 | + goto bad_reg; | |
| 519 | + case 9: /* Cache lockdown. */ | |
| 520 | + switch (op2) { | |
| 521 | + case 0: | |
| 522 | + return env->cp15.c9_data; | |
| 523 | + case 1: | |
| 524 | + return env->cp15.c9_insn; | |
| 525 | + default: | |
| 526 | + goto bad_reg; | |
| 527 | + } | |
| 528 | + case 10: /* MMU TLB lockdown. */ | |
| 529 | + /* ??? TLB lockdown not implemented. */ | |
| 530 | + return 0; | |
| 531 | + case 11: /* TCM DMA control. */ | |
| 532 | + case 12: /* Reserved. */ | |
| 533 | + goto bad_reg; | |
| 534 | + case 13: /* Process ID. */ | |
| 535 | + switch (op2) { | |
| 536 | + case 0: | |
| 537 | + return env->cp15.c13_fcse; | |
| 538 | + case 1: | |
| 539 | + return env->cp15.c13_context; | |
| 540 | + default: | |
| 541 | + goto bad_reg; | |
| 542 | + } | |
| 543 | + case 14: /* Reserved. */ | |
| 544 | + goto bad_reg; | |
| 545 | + case 15: /* Implementation specific. */ | |
| 546 | + /* ??? Internal registers not implemented. */ | |
| 547 | + return 0; | |
| 548 | + } | |
| 549 | +bad_reg: | |
| 550 | + /* ??? For debugging only. Should raise illegal instruction exception. */ | |
| 551 | + cpu_abort(env, "Unimplemented cp15 register read\n"); | |
| 552 | + return 0; | |
| 553 | +} | |
| 554 | + | |
| 555 | +#endif | ... | ... |
target-arm/op.c
| ... | ... | @@ -101,6 +101,11 @@ void OPPROTO op_movl_T0_im(void) |
| 101 | 101 | T0 = PARAM1; |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | +void OPPROTO op_movl_T0_T1(void) | |
| 105 | +{ | |
| 106 | + T0 = T1; | |
| 107 | +} | |
| 108 | + | |
| 104 | 109 | void OPPROTO op_movl_T1_im(void) |
| 105 | 110 | { |
| 106 | 111 | T1 = PARAM1; |
| ... | ... | @@ -361,20 +366,27 @@ void OPPROTO op_exit_tb(void) |
| 361 | 366 | EXIT_TB(); |
| 362 | 367 | } |
| 363 | 368 | |
| 364 | -void OPPROTO op_movl_T0_psr(void) | |
| 369 | +void OPPROTO op_movl_T0_cpsr(void) | |
| 365 | 370 | { |
| 366 | - T0 = compute_cpsr(); | |
| 371 | + T0 = cpsr_read(env); | |
| 372 | + FORCE_RET(); | |
| 367 | 373 | } |
| 368 | 374 | |
| 369 | -/* NOTE: N = 1 and Z = 1 cannot be stored currently */ | |
| 370 | -void OPPROTO op_movl_psr_T0(void) | |
| 375 | +void OPPROTO op_movl_T0_spsr(void) | |
| 371 | 376 | { |
| 372 | - unsigned int psr; | |
| 373 | - psr = T0; | |
| 374 | - env->CF = (psr >> 29) & 1; | |
| 375 | - env->NZF = (psr & 0xc0000000) ^ 0x40000000; | |
| 376 | - env->VF = (psr << 3) & 0x80000000; | |
| 377 | - /* for user mode we do not update other state info */ | |
| 377 | + T0 = env->spsr; | |
| 378 | +} | |
| 379 | + | |
| 380 | +void OPPROTO op_movl_spsr_T0(void) | |
| 381 | +{ | |
| 382 | + uint32_t mask = PARAM1; | |
| 383 | + env->spsr = (env->spsr & ~mask) | (T0 & mask); | |
| 384 | +} | |
| 385 | + | |
| 386 | +void OPPROTO op_movl_cpsr_T0(void) | |
| 387 | +{ | |
| 388 | + cpsr_write(env, T0, PARAM1); | |
| 389 | + FORCE_RET(); | |
| 378 | 390 | } |
| 379 | 391 | |
| 380 | 392 | void OPPROTO op_mul_T0_T1(void) |
| ... | ... | @@ -433,67 +445,15 @@ void OPPROTO op_logicq_cc(void) |
| 433 | 445 | |
| 434 | 446 | /* memory access */ |
| 435 | 447 | |
| 436 | -void OPPROTO op_ldub_T0_T1(void) | |
| 437 | -{ | |
| 438 | - T0 = ldub((void *)T1); | |
| 439 | -} | |
| 440 | - | |
| 441 | -void OPPROTO op_ldsb_T0_T1(void) | |
| 442 | -{ | |
| 443 | - T0 = ldsb((void *)T1); | |
| 444 | -} | |
| 445 | - | |
| 446 | -void OPPROTO op_lduw_T0_T1(void) | |
| 447 | -{ | |
| 448 | - T0 = lduw((void *)T1); | |
| 449 | -} | |
| 450 | - | |
| 451 | -void OPPROTO op_ldsw_T0_T1(void) | |
| 452 | -{ | |
| 453 | - T0 = ldsw((void *)T1); | |
| 454 | -} | |
| 455 | - | |
| 456 | -void OPPROTO op_ldl_T0_T1(void) | |
| 457 | -{ | |
| 458 | - T0 = ldl((void *)T1); | |
| 459 | -} | |
| 460 | - | |
| 461 | -void OPPROTO op_stb_T0_T1(void) | |
| 462 | -{ | |
| 463 | - stb((void *)T1, T0); | |
| 464 | -} | |
| 465 | - | |
| 466 | -void OPPROTO op_stw_T0_T1(void) | |
| 467 | -{ | |
| 468 | - stw((void *)T1, T0); | |
| 469 | -} | |
| 470 | - | |
| 471 | -void OPPROTO op_stl_T0_T1(void) | |
| 472 | -{ | |
| 473 | - stl((void *)T1, T0); | |
| 474 | -} | |
| 475 | - | |
| 476 | -void OPPROTO op_swpb_T0_T1(void) | |
| 477 | -{ | |
| 478 | - int tmp; | |
| 448 | +#define MEMSUFFIX _raw | |
| 449 | +#include "op_mem.h" | |
| 479 | 450 | |
| 480 | - cpu_lock(); | |
| 481 | - tmp = ldub((void *)T1); | |
| 482 | - stb((void *)T1, T0); | |
| 483 | - T0 = tmp; | |
| 484 | - cpu_unlock(); | |
| 485 | -} | |
| 486 | - | |
| 487 | -void OPPROTO op_swpl_T0_T1(void) | |
| 488 | -{ | |
| 489 | - int tmp; | |
| 490 | - | |
| 491 | - cpu_lock(); | |
| 492 | - tmp = ldl((void *)T1); | |
| 493 | - stl((void *)T1, T0); | |
| 494 | - T0 = tmp; | |
| 495 | - cpu_unlock(); | |
| 496 | -} | |
| 451 | +#if !defined(CONFIG_USER_ONLY) | |
| 452 | +#define MEMSUFFIX _user | |
| 453 | +#include "op_mem.h" | |
| 454 | +#define MEMSUFFIX _kernel | |
| 455 | +#include "op_mem.h" | |
| 456 | +#endif | |
| 497 | 457 | |
| 498 | 458 | /* shifts */ |
| 499 | 459 | |
| ... | ... | @@ -744,17 +704,48 @@ void OPPROTO op_sarl_T0_im(void) |
| 744 | 704 | T0 = (int32_t)T0 >> PARAM1; |
| 745 | 705 | } |
| 746 | 706 | |
| 747 | -/* 16->32 Sign extend */ | |
| 748 | -void OPPROTO op_sxl_T0(void) | |
| 707 | +/* Sign/zero extend */ | |
| 708 | +void OPPROTO op_sxth_T0(void) | |
| 749 | 709 | { |
| 750 | 710 | T0 = (int16_t)T0; |
| 751 | 711 | } |
| 752 | 712 | |
| 753 | -void OPPROTO op_sxl_T1(void) | |
| 713 | +void OPPROTO op_sxth_T1(void) | |
| 754 | 714 | { |
| 755 | 715 | T1 = (int16_t)T1; |
| 756 | 716 | } |
| 757 | 717 | |
| 718 | +void OPPROTO op_sxtb_T1(void) | |
| 719 | +{ | |
| 720 | + T1 = (int8_t)T1; | |
| 721 | +} | |
| 722 | + | |
| 723 | +void OPPROTO op_uxtb_T1(void) | |
| 724 | +{ | |
| 725 | + T1 = (uint8_t)T1; | |
| 726 | +} | |
| 727 | + | |
| 728 | +void OPPROTO op_uxth_T1(void) | |
| 729 | +{ | |
| 730 | + T1 = (uint16_t)T1; | |
| 731 | +} | |
| 732 | + | |
| 733 | +void OPPROTO op_sxtb16_T1(void) | |
| 734 | +{ | |
| 735 | + uint32_t res; | |
| 736 | + res = (uint16_t)(int8_t)T1; | |
| 737 | + res |= (uint32_t)(int8_t)(T1 >> 16) << 16; | |
| 738 | + T1 = res; | |
| 739 | +} | |
| 740 | + | |
| 741 | +void OPPROTO op_uxtb16_T1(void) | |
| 742 | +{ | |
| 743 | + uint32_t res; | |
| 744 | + res = (uint16_t)(uint8_t)T1; | |
| 745 | + res |= (uint32_t)(uint8_t)(T1 >> 16) << 16; | |
| 746 | + T1 = res; | |
| 747 | +} | |
| 748 | + | |
| 758 | 749 | #define SIGNBIT (uint32_t)0x80000000 |
| 759 | 750 | /* saturating arithmetic */ |
| 760 | 751 | void OPPROTO op_addl_T0_T1_setq(void) |
| ... | ... | @@ -1128,23 +1119,52 @@ void OPPROTO op_vfp_mdrr(void) |
| 1128 | 1119 | FT0d = u.d; |
| 1129 | 1120 | } |
| 1130 | 1121 | |
| 1131 | -/* Floating point load/store. Address is in T1 */ | |
| 1132 | -void OPPROTO op_vfp_lds(void) | |
| 1122 | +/* Copy the most significant bit to T0 to all bits of T1. */ | |
| 1123 | +void OPPROTO op_signbit_T1_T0(void) | |
| 1133 | 1124 | { |
| 1134 | - FT0s = ldfl((void *)T1); | |
| 1125 | + T1 = (int32_t)T0 >> 31; | |
| 1135 | 1126 | } |
| 1136 | 1127 | |
| 1137 | -void OPPROTO op_vfp_ldd(void) | |
| 1128 | +void OPPROTO op_movl_cp15_T0(void) | |
| 1138 | 1129 | { |
| 1139 | - FT0d = ldfq((void *)T1); | |
| 1130 | + helper_set_cp15(env, PARAM1, T0); | |
| 1131 | + FORCE_RET(); | |
| 1140 | 1132 | } |
| 1141 | 1133 | |
| 1142 | -void OPPROTO op_vfp_sts(void) | |
| 1134 | +void OPPROTO op_movl_T0_cp15(void) | |
| 1143 | 1135 | { |
| 1144 | - stfl((void *)T1, FT0s); | |
| 1136 | + T0 = helper_get_cp15(env, PARAM1); | |
| 1137 | + FORCE_RET(); | |
| 1145 | 1138 | } |
| 1146 | 1139 | |
| 1147 | -void OPPROTO op_vfp_std(void) | |
| 1140 | +/* Access to user mode registers from privileged modes. */ | |
| 1141 | +void OPPROTO op_movl_T0_user(void) | |
| 1148 | 1142 | { |
| 1149 | - stfq((void *)T1, FT0d); | |
| 1143 | + int regno = PARAM1; | |
| 1144 | + if (regno == 13) { | |
| 1145 | + T0 = env->banked_r13[0]; | |
| 1146 | + } else if (regno == 14) { | |
| 1147 | + T0 = env->banked_r14[0]; | |
| 1148 | + } else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { | |
| 1149 | + T0 = env->usr_regs[regno - 8]; | |
| 1150 | + } else { | |
| 1151 | + T0 = env->regs[regno]; | |
| 1152 | + } | |
| 1153 | + FORCE_RET(); | |
| 1154 | +} | |
| 1155 | + | |
| 1156 | + | |
| 1157 | +void OPPROTO op_movl_user_T0(void) | |
| 1158 | +{ | |
| 1159 | + int regno = PARAM1; | |
| 1160 | + if (regno == 13) { | |
| 1161 | + env->banked_r13[0] = T0; | |
| 1162 | + } else if (regno == 14) { | |
| 1163 | + env->banked_r14[0] = T0; | |
| 1164 | + } else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { | |
| 1165 | + env->usr_regs[regno - 8] = T0; | |
| 1166 | + } else { | |
| 1167 | + env->regs[regno] = T0; | |
| 1168 | + } | |
| 1169 | + FORCE_RET(); | |
| 1150 | 1170 | } | ... | ... |
target-arm/op_helper.c
| ... | ... | @@ -172,3 +172,54 @@ void do_vfp_get_fpscr(void) |
| 172 | 172 | i = get_float_exception_flags(&env->vfp.fp_status); |
| 173 | 173 | T0 |= vfp_exceptbits_from_host(i); |
| 174 | 174 | } |
| 175 | + | |
| 176 | +#if !defined(CONFIG_USER_ONLY) | |
| 177 | + | |
| 178 | +#define MMUSUFFIX _mmu | |
| 179 | +#define GETPC() (__builtin_return_address(0)) | |
| 180 | + | |
| 181 | +#define SHIFT 0 | |
| 182 | +#include "softmmu_template.h" | |
| 183 | + | |
| 184 | +#define SHIFT 1 | |
| 185 | +#include "softmmu_template.h" | |
| 186 | + | |
| 187 | +#define SHIFT 2 | |
| 188 | +#include "softmmu_template.h" | |
| 189 | + | |
| 190 | +#define SHIFT 3 | |
| 191 | +#include "softmmu_template.h" | |
| 192 | + | |
| 193 | +/* try to fill the TLB and return an exception if error. If retaddr is | |
| 194 | + NULL, it means that the function was called in C code (i.e. not | |
| 195 | + from generated code or from helper.c) */ | |
| 196 | +/* XXX: fix it to restore all registers */ | |
| 197 | +void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) | |
| 198 | +{ | |
| 199 | + TranslationBlock *tb; | |
| 200 | + CPUState *saved_env; | |
| 201 | + target_phys_addr_t pc; | |
| 202 | + int ret; | |
| 203 | + | |
| 204 | + /* XXX: hack to restore env in all cases, even if not called from | |
| 205 | + generated code */ | |
| 206 | + saved_env = env; | |
| 207 | + env = cpu_single_env; | |
| 208 | + ret = cpu_arm_handle_mmu_fault(env, addr, is_write, is_user, 1); | |
| 209 | + if (__builtin_expect(ret, 0)) { | |
| 210 | + if (retaddr) { | |
| 211 | + /* now we have a real cpu fault */ | |
| 212 | + pc = (target_phys_addr_t)retaddr; | |
| 213 | + tb = tb_find_pc(pc); | |
| 214 | + if (tb) { | |
| 215 | + /* the PC is inside the translated code. It means that we have | |
| 216 | + a virtual CPU fault */ | |
| 217 | + cpu_restore_state(tb, env, pc, NULL); | |
| 218 | + } | |
| 219 | + } | |
| 220 | + raise_exception(env->exception_index); | |
| 221 | + } | |
| 222 | + env = saved_env; | |
| 223 | +} | |
| 224 | + | |
| 225 | +#endif | ... | ... |
target-arm/op_mem.h
0 โ 100644
| 1 | +/* ARM memory operations. */ | |
| 2 | + | |
| 3 | +/* Load from address T1 into T0. */ | |
| 4 | +#define MEM_LD_OP(name) \ | |
| 5 | +void OPPROTO glue(op_ld##name,MEMSUFFIX)(void) \ | |
| 6 | +{ \ | |
| 7 | + T0 = glue(ld##name,MEMSUFFIX)(T1); \ | |
| 8 | + FORCE_RET(); \ | |
| 9 | +} | |
| 10 | + | |
| 11 | +MEM_LD_OP(ub) | |
| 12 | +MEM_LD_OP(sb) | |
| 13 | +MEM_LD_OP(uw) | |
| 14 | +MEM_LD_OP(sw) | |
| 15 | +MEM_LD_OP(l) | |
| 16 | + | |
| 17 | +#undef MEM_LD_OP | |
| 18 | + | |
| 19 | +/* Store T0 to address T1. */ | |
| 20 | +#define MEM_ST_OP(name) \ | |
| 21 | +void OPPROTO glue(op_st##name,MEMSUFFIX)(void) \ | |
| 22 | +{ \ | |
| 23 | + glue(st##name,MEMSUFFIX)(T1, T0); \ | |
| 24 | + FORCE_RET(); \ | |
| 25 | +} | |
| 26 | + | |
| 27 | +MEM_ST_OP(b) | |
| 28 | +MEM_ST_OP(w) | |
| 29 | +MEM_ST_OP(l) | |
| 30 | + | |
| 31 | +#undef MEM_ST_OP | |
| 32 | + | |
| 33 | +/* Swap T0 with memory at address T1. */ | |
| 34 | +/* ??? Is this exception safe? */ | |
| 35 | +#define MEM_SWP_OP(name, lname) \ | |
| 36 | +void OPPROTO glue(op_swp##name,MEMSUFFIX)(void) \ | |
| 37 | +{ \ | |
| 38 | + uint32_t tmp; \ | |
| 39 | + cpu_lock(); \ | |
| 40 | + tmp = glue(ld##lname,MEMSUFFIX)(T1); \ | |
| 41 | + glue(st##name,MEMSUFFIX)(T1, T0); \ | |
| 42 | + T0 = tmp; \ | |
| 43 | + cpu_unlock(); \ | |
| 44 | + FORCE_RET(); \ | |
| 45 | +} | |
| 46 | + | |
| 47 | +MEM_SWP_OP(b, ub) | |
| 48 | +MEM_SWP_OP(l, l) | |
| 49 | + | |
| 50 | +#undef MEM_SWP_OP | |
| 51 | + | |
| 52 | +/* Floating point load/store. Address is in T1 */ | |
| 53 | +#define VFP_MEM_OP(p, w) \ | |
| 54 | +void OPPROTO glue(op_vfp_ld##p,MEMSUFFIX)(void) \ | |
| 55 | +{ \ | |
| 56 | + FT0##p = glue(ldf##w,MEMSUFFIX)(T1); \ | |
| 57 | + FORCE_RET(); \ | |
| 58 | +} \ | |
| 59 | +void OPPROTO glue(op_vfp_st##p,MEMSUFFIX)(void) \ | |
| 60 | +{ \ | |
| 61 | + glue(stf##w,MEMSUFFIX)(T1, FT0##p); \ | |
| 62 | + FORCE_RET(); \ | |
| 63 | +} | |
| 64 | + | |
| 65 | +VFP_MEM_OP(s,l) | |
| 66 | +VFP_MEM_OP(d,q) | |
| 67 | + | |
| 68 | +#undef VFP_MEM_OP | |
| 69 | + | |
| 70 | +#undef MEMSUFFIX | ... | ... |
target-arm/translate.c
| ... | ... | @@ -28,6 +28,12 @@ |
| 28 | 28 | #include "exec-all.h" |
| 29 | 29 | #include "disas.h" |
| 30 | 30 | |
| 31 | +#define ENABLE_ARCH_5J 0 | |
| 32 | +#define ENABLE_ARCH_6 1 | |
| 33 | +#define ENABLE_ARCH_6T2 1 | |
| 34 | + | |
| 35 | +#define ARCH(x) if (!ENABLE_ARCH_##x) goto illegal_op; | |
| 36 | + | |
| 31 | 37 | /* internal defines */ |
| 32 | 38 | typedef struct DisasContext { |
| 33 | 39 | target_ulong pc; |
| ... | ... | @@ -39,8 +45,17 @@ typedef struct DisasContext { |
| 39 | 45 | struct TranslationBlock *tb; |
| 40 | 46 | int singlestep_enabled; |
| 41 | 47 | int thumb; |
| 48 | +#if !defined(CONFIG_USER_ONLY) | |
| 49 | + int user; | |
| 50 | +#endif | |
| 42 | 51 | } DisasContext; |
| 43 | 52 | |
| 53 | +#if defined(CONFIG_USER_ONLY) | |
| 54 | +#define IS_USER(s) 1 | |
| 55 | +#else | |
| 56 | +#define IS_USER(s) (s->user) | |
| 57 | +#endif | |
| 58 | + | |
| 44 | 59 | #define DISAS_JUMP_NEXT 4 |
| 45 | 60 | |
| 46 | 61 | #ifdef USE_DIRECT_JUMP |
| ... | ... | @@ -270,6 +285,18 @@ static inline void gen_bx(DisasContext *s) |
| 270 | 285 | gen_op_bx_T0(); |
| 271 | 286 | } |
| 272 | 287 | |
| 288 | + | |
| 289 | +#if defined(CONFIG_USER_ONLY) | |
| 290 | +#define gen_ldst(name, s) gen_op_##name##_raw() | |
| 291 | +#else | |
| 292 | +#define gen_ldst(name, s) do { \ | |
| 293 | + if (IS_USER(s)) \ | |
| 294 | + gen_op_##name##_user(); \ | |
| 295 | + else \ | |
| 296 | + gen_op_##name##_kernel(); \ | |
| 297 | + } while (0) | |
| 298 | +#endif | |
| 299 | + | |
| 273 | 300 | static inline void gen_movl_TN_reg(DisasContext *s, int reg, int t) |
| 274 | 301 | { |
| 275 | 302 | int val; |
| ... | ... | @@ -319,6 +346,14 @@ static inline void gen_movl_reg_T1(DisasContext *s, int reg) |
| 319 | 346 | gen_movl_reg_TN(s, reg, 1); |
| 320 | 347 | } |
| 321 | 348 | |
| 349 | +/* Force a TB lookup after an instruction that changes the CPU state. */ | |
| 350 | +static inline void gen_lookup_tb(DisasContext *s) | |
| 351 | +{ | |
| 352 | + gen_op_movl_T0_im(s->pc); | |
| 353 | + gen_movl_reg_T0(s, 15); | |
| 354 | + s->is_jmp = DISAS_UPDATE; | |
| 355 | +} | |
| 356 | + | |
| 322 | 357 | static inline void gen_add_data_offset(DisasContext *s, unsigned int insn) |
| 323 | 358 | { |
| 324 | 359 | int val, rm, shift, shiftop; |
| ... | ... | @@ -395,11 +430,25 @@ VFP_OP(toui) |
| 395 | 430 | VFP_OP(touiz) |
| 396 | 431 | VFP_OP(tosi) |
| 397 | 432 | VFP_OP(tosiz) |
| 398 | -VFP_OP(ld) | |
| 399 | -VFP_OP(st) | |
| 400 | 433 | |
| 401 | 434 | #undef VFP_OP |
| 402 | 435 | |
| 436 | +static inline void gen_vfp_ld(DisasContext *s, int dp) | |
| 437 | +{ | |
| 438 | + if (dp) | |
| 439 | + gen_ldst(vfp_ldd, s); | |
| 440 | + else | |
| 441 | + gen_ldst(vfp_lds, s); | |
| 442 | +} | |
| 443 | + | |
| 444 | +static inline void gen_vfp_st(DisasContext *s, int dp) | |
| 445 | +{ | |
| 446 | + if (dp) | |
| 447 | + gen_ldst(vfp_std, s); | |
| 448 | + else | |
| 449 | + gen_ldst(vfp_sts, s); | |
| 450 | +} | |
| 451 | + | |
| 403 | 452 | static inline long |
| 404 | 453 | vfp_reg_offset (int dp, int reg) |
| 405 | 454 | { |
| ... | ... | @@ -437,6 +486,30 @@ static inline void gen_mov_vreg_F0(int dp, int reg) |
| 437 | 486 | gen_op_vfp_setreg_F0s(vfp_reg_offset(dp, reg)); |
| 438 | 487 | } |
| 439 | 488 | |
| 489 | +/* Disassemble system coprocessor (cp15) instruction. Return nonzero if | |
| 490 | + instruction is not defined. */ | |
| 491 | +static int disas_cp15_insn(DisasContext *s, uint32_t insn) | |
| 492 | +{ | |
| 493 | + uint32_t rd; | |
| 494 | + | |
| 495 | + /* ??? Some cp15 registers are accessible from userspace. */ | |
| 496 | + if (IS_USER(s)) { | |
| 497 | + return 1; | |
| 498 | + } | |
| 499 | + rd = (insn >> 12) & 0xf; | |
| 500 | + if (insn & (1 << 20)) { | |
| 501 | + gen_op_movl_T0_cp15(insn); | |
| 502 | + /* If the destination register is r15 then sets condition codes. */ | |
| 503 | + if (rd != 15) | |
| 504 | + gen_movl_reg_T0(s, rd); | |
| 505 | + } else { | |
| 506 | + gen_movl_T0_reg(s, rd); | |
| 507 | + gen_op_movl_cp15_T0(insn); | |
| 508 | + } | |
| 509 | + gen_lookup_tb(s); | |
| 510 | + return 0; | |
| 511 | +} | |
| 512 | + | |
| 440 | 513 | /* Disassemble a VFP instruction. Returns nonzero if an error occured |
| 441 | 514 | (ie. an undefined instruction). */ |
| 442 | 515 | static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) |
| ... | ... | @@ -499,8 +572,8 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) |
| 499 | 572 | gen_op_vfp_mrs(); |
| 500 | 573 | } |
| 501 | 574 | if (rd == 15) { |
| 502 | - /* This will only set the 4 flag bits */ | |
| 503 | - gen_op_movl_psr_T0(); | |
| 575 | + /* Set the 4 flag bits in the CPSR. */ | |
| 576 | + gen_op_movl_cpsr_T0(0xf0000000); | |
| 504 | 577 | } else |
| 505 | 578 | gen_movl_reg_T0(s, rd); |
| 506 | 579 | } else { |
| ... | ... | @@ -516,9 +589,7 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) |
| 516 | 589 | gen_op_vfp_movl_fpscr_T0(); |
| 517 | 590 | /* This could change vector settings, so jump to |
| 518 | 591 | the next instuction. */ |
| 519 | - gen_op_movl_T0_im(s->pc); | |
| 520 | - gen_movl_reg_T0(s, 15); | |
| 521 | - s->is_jmp = DISAS_UPDATE; | |
| 592 | + gen_lookup_tb(s); | |
| 522 | 593 | break; |
| 523 | 594 | default: |
| 524 | 595 | return 1; |
| ... | ... | @@ -848,11 +919,11 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) |
| 848 | 919 | offset = -offset; |
| 849 | 920 | gen_op_addl_T1_im(offset); |
| 850 | 921 | if (insn & (1 << 20)) { |
| 851 | - gen_vfp_ld(dp); | |
| 922 | + gen_vfp_ld(s, dp); | |
| 852 | 923 | gen_mov_vreg_F0(dp, rd); |
| 853 | 924 | } else { |
| 854 | 925 | gen_mov_F0_vreg(dp, rd); |
| 855 | - gen_vfp_st(dp); | |
| 926 | + gen_vfp_st(s, dp); | |
| 856 | 927 | } |
| 857 | 928 | } else { |
| 858 | 929 | /* load/store multiple */ |
| ... | ... | @@ -871,12 +942,12 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) |
| 871 | 942 | for (i = 0; i < n; i++) { |
| 872 | 943 | if (insn & (1 << 20)) { |
| 873 | 944 | /* load */ |
| 874 | - gen_vfp_ld(dp); | |
| 945 | + gen_vfp_ld(s, dp); | |
| 875 | 946 | gen_mov_vreg_F0(dp, rd + i); |
| 876 | 947 | } else { |
| 877 | 948 | /* store */ |
| 878 | 949 | gen_mov_F0_vreg(dp, rd + i); |
| 879 | - gen_vfp_st(dp); | |
| 950 | + gen_vfp_st(s, dp); | |
| 880 | 951 | } |
| 881 | 952 | gen_op_addl_T1_im(offset); |
| 882 | 953 | } |
| ... | ... | @@ -939,11 +1010,68 @@ static inline void gen_jmp (DisasContext *s, uint32_t dest) |
| 939 | 1010 | } |
| 940 | 1011 | } |
| 941 | 1012 | |
| 1013 | +static inline void gen_mulxy(int x, int y) | |
| 1014 | +{ | |
| 1015 | + if (x & 2) | |
| 1016 | + gen_op_sarl_T0_im(16); | |
| 1017 | + else | |
| 1018 | + gen_op_sxth_T0(); | |
| 1019 | + if (y & 1) | |
| 1020 | + gen_op_sarl_T1_im(16); | |
| 1021 | + else | |
| 1022 | + gen_op_sxth_T1(); | |
| 1023 | + gen_op_mul_T0_T1(); | |
| 1024 | +} | |
| 1025 | + | |
| 1026 | +/* Return the mask of PSR bits set by a MSR instruction. */ | |
| 1027 | +static uint32_t msr_mask(DisasContext *s, int flags) { | |
| 1028 | + uint32_t mask; | |
| 1029 | + | |
| 1030 | + mask = 0; | |
| 1031 | + if (flags & (1 << 0)) | |
| 1032 | + mask |= 0xff; | |
| 1033 | + if (flags & (1 << 1)) | |
| 1034 | + mask |= 0xff00; | |
| 1035 | + if (flags & (1 << 2)) | |
| 1036 | + mask |= 0xff0000; | |
| 1037 | + if (flags & (1 << 3)) | |
| 1038 | + mask |= 0xff000000; | |
| 1039 | + /* Mask out undefined bits and state bits. */ | |
| 1040 | + mask &= 0xf89f03df; | |
| 1041 | + /* Mask out privileged bits. */ | |
| 1042 | + if (IS_USER(s)) | |
| 1043 | + mask &= 0xf80f0200; | |
| 1044 | + return mask; | |
| 1045 | +} | |
| 1046 | + | |
| 1047 | +/* Returns nonzero if access to the PSR is not permitted. */ | |
| 1048 | +static int gen_set_psr_T0(DisasContext *s, uint32_t mask, int spsr) | |
| 1049 | +{ | |
| 1050 | + if (spsr) { | |
| 1051 | + /* ??? This is also undefined in system mode. */ | |
| 1052 | + if (IS_USER(s)) | |
| 1053 | + return 1; | |
| 1054 | + gen_op_movl_spsr_T0(mask); | |
| 1055 | + } else { | |
| 1056 | + gen_op_movl_cpsr_T0(mask); | |
| 1057 | + } | |
| 1058 | + gen_lookup_tb(s); | |
| 1059 | + return 0; | |
| 1060 | +} | |
| 1061 | + | |
| 1062 | +static void gen_exception_return(DisasContext *s) | |
| 1063 | +{ | |
| 1064 | + gen_op_movl_reg_TN[0][15](); | |
| 1065 | + gen_op_movl_T0_spsr(); | |
| 1066 | + gen_op_movl_cpsr_T0(0xffffffff); | |
| 1067 | + s->is_jmp = DISAS_UPDATE; | |
| 1068 | +} | |
| 1069 | + | |
| 942 | 1070 | static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 943 | 1071 | { |
| 944 | 1072 | unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; |
| 945 | 1073 | |
| 946 | - insn = ldl(s->pc); | |
| 1074 | + insn = ldl_code(s->pc); | |
| 947 | 1075 | s->pc += 4; |
| 948 | 1076 | |
| 949 | 1077 | cond = insn >> 28; |
| ... | ... | @@ -971,6 +1099,15 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 971 | 1099 | /* Coprocessor double register transfer. */ |
| 972 | 1100 | } else if ((insn & 0x0f000010) == 0x0e000010) { |
| 973 | 1101 | /* Additional coprocessor register transfer. */ |
| 1102 | + } else if ((insn & 0x0ff10010) == 0x01000000) { | |
| 1103 | + /* cps (privileged) */ | |
| 1104 | + } else if ((insn & 0x0ffffdff) == 0x01010000) { | |
| 1105 | + /* setend */ | |
| 1106 | + if (insn & (1 << 9)) { | |
| 1107 | + /* BE8 mode not implemented. */ | |
| 1108 | + goto illegal_op; | |
| 1109 | + } | |
| 1110 | + return; | |
| 974 | 1111 | } |
| 975 | 1112 | goto illegal_op; |
| 976 | 1113 | } |
| ... | ... | @@ -984,7 +1121,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 984 | 1121 | //s->is_jmp = DISAS_JUMP_NEXT; |
| 985 | 1122 | } |
| 986 | 1123 | if ((insn & 0x0f900000) == 0x03000000) { |
| 987 | - if ((insn & 0x0ff0f000) != 0x0360f000) | |
| 1124 | + if ((insn & 0x0fb0f000) != 0x0320f000) | |
| 988 | 1125 | goto illegal_op; |
| 989 | 1126 | /* CPSR = immediate */ |
| 990 | 1127 | val = insn & 0xff; |
| ... | ... | @@ -992,8 +1129,9 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 992 | 1129 | if (shift) |
| 993 | 1130 | val = (val >> shift) | (val << (32 - shift)); |
| 994 | 1131 | gen_op_movl_T0_im(val); |
| 995 | - if (insn & (1 << 19)) | |
| 996 | - gen_op_movl_psr_T0(); | |
| 1132 | + if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf), | |
| 1133 | + (insn & (1 << 22)) != 0)) | |
| 1134 | + goto illegal_op; | |
| 997 | 1135 | } else if ((insn & 0x0f900000) == 0x01000000 |
| 998 | 1136 | && (insn & 0x00000090) != 0x00000090) { |
| 999 | 1137 | /* miscellaneous instructions */ |
| ... | ... | @@ -1002,19 +1140,22 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1002 | 1140 | rm = insn & 0xf; |
| 1003 | 1141 | switch (sh) { |
| 1004 | 1142 | case 0x0: /* move program status register */ |
| 1005 | - if (op1 & 2) { | |
| 1006 | - /* SPSR not accessible in user mode */ | |
| 1007 | - goto illegal_op; | |
| 1008 | - } | |
| 1009 | 1143 | if (op1 & 1) { |
| 1010 | - /* CPSR = reg */ | |
| 1144 | + /* PSR = reg */ | |
| 1011 | 1145 | gen_movl_T0_reg(s, rm); |
| 1012 | - if (insn & (1 << 19)) | |
| 1013 | - gen_op_movl_psr_T0(); | |
| 1146 | + if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf), | |
| 1147 | + (op1 & 2) != 0)) | |
| 1148 | + goto illegal_op; | |
| 1014 | 1149 | } else { |
| 1015 | 1150 | /* reg = CPSR */ |
| 1016 | 1151 | rd = (insn >> 12) & 0xf; |
| 1017 | - gen_op_movl_T0_psr(); | |
| 1152 | + if (op1 & 2) { | |
| 1153 | + if (IS_USER(s)) | |
| 1154 | + goto illegal_op; | |
| 1155 | + gen_op_movl_T0_spsr(); | |
| 1156 | + } else { | |
| 1157 | + gen_op_movl_T0_cpsr(); | |
| 1158 | + } | |
| 1018 | 1159 | gen_movl_reg_T0(s, rd); |
| 1019 | 1160 | } |
| 1020 | 1161 | break; |
| ... | ... | @@ -1033,6 +1174,16 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1033 | 1174 | goto illegal_op; |
| 1034 | 1175 | } |
| 1035 | 1176 | break; |
| 1177 | + case 0x2: | |
| 1178 | + if (op1 == 1) { | |
| 1179 | + ARCH(5J); /* bxj */ | |
| 1180 | + /* Trivial implementation equivalent to bx. */ | |
| 1181 | + gen_movl_T0_reg(s, rm); | |
| 1182 | + gen_bx(s); | |
| 1183 | + } else { | |
| 1184 | + goto illegal_op; | |
| 1185 | + } | |
| 1186 | + break; | |
| 1036 | 1187 | case 0x3: |
| 1037 | 1188 | if (op1 != 1) |
| 1038 | 1189 | goto illegal_op; |
| ... | ... | @@ -1071,7 +1222,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1071 | 1222 | if (sh & 4) |
| 1072 | 1223 | gen_op_sarl_T1_im(16); |
| 1073 | 1224 | else |
| 1074 | - gen_op_sxl_T1(); | |
| 1225 | + gen_op_sxth_T1(); | |
| 1075 | 1226 | gen_op_imulw_T0_T1(); |
| 1076 | 1227 | if ((sh & 2) == 0) { |
| 1077 | 1228 | gen_movl_T1_reg(s, rn); |
| ... | ... | @@ -1081,22 +1232,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1081 | 1232 | } else { |
| 1082 | 1233 | /* 16 * 16 */ |
| 1083 | 1234 | gen_movl_T0_reg(s, rm); |
| 1084 | - if (sh & 2) | |
| 1085 | - gen_op_sarl_T0_im(16); | |
| 1086 | - else | |
| 1087 | - gen_op_sxl_T0(); | |
| 1088 | 1235 | gen_movl_T1_reg(s, rs); |
| 1089 | - if (sh & 4) | |
| 1090 | - gen_op_sarl_T1_im(16); | |
| 1091 | - else | |
| 1092 | - gen_op_sxl_T1(); | |
| 1236 | + gen_mulxy(sh & 2, sh & 4); | |
| 1093 | 1237 | if (op1 == 2) { |
| 1094 | - gen_op_imull_T0_T1(); | |
| 1238 | + gen_op_signbit_T1_T0(); | |
| 1095 | 1239 | gen_op_addq_T0_T1(rn, rd); |
| 1096 | 1240 | gen_movl_reg_T0(s, rn); |
| 1097 | 1241 | gen_movl_reg_T1(s, rd); |
| 1098 | 1242 | } else { |
| 1099 | - gen_op_mul_T0_T1(); | |
| 1100 | 1243 | if (op1 == 0) { |
| 1101 | 1244 | gen_movl_T1_reg(s, rn); |
| 1102 | 1245 | gen_op_addl_T0_T1_setq(); |
| ... | ... | @@ -1176,11 +1319,19 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1176 | 1319 | gen_op_logic_T0_cc(); |
| 1177 | 1320 | break; |
| 1178 | 1321 | case 0x02: |
| 1179 | - if (set_cc) | |
| 1322 | + if (set_cc && rd == 15) { | |
| 1323 | + /* SUBS r15, ... is used for exception return. */ | |
| 1324 | + if (IS_USER(s)) | |
| 1325 | + goto illegal_op; | |
| 1180 | 1326 | gen_op_subl_T0_T1_cc(); |
| 1181 | - else | |
| 1182 | - gen_op_subl_T0_T1(); | |
| 1183 | - gen_movl_reg_T0(s, rd); | |
| 1327 | + gen_exception_return(s); | |
| 1328 | + } else { | |
| 1329 | + if (set_cc) | |
| 1330 | + gen_op_subl_T0_T1_cc(); | |
| 1331 | + else | |
| 1332 | + gen_op_subl_T0_T1(); | |
| 1333 | + gen_movl_reg_T0(s, rd); | |
| 1334 | + } | |
| 1184 | 1335 | break; |
| 1185 | 1336 | case 0x03: |
| 1186 | 1337 | if (set_cc) |
| ... | ... | @@ -1246,9 +1397,17 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1246 | 1397 | gen_op_logic_T0_cc(); |
| 1247 | 1398 | break; |
| 1248 | 1399 | case 0x0d: |
| 1249 | - gen_movl_reg_T1(s, rd); | |
| 1250 | - if (logic_cc) | |
| 1251 | - gen_op_logic_T1_cc(); | |
| 1400 | + if (logic_cc && rd == 15) { | |
| 1401 | + /* MOVS r15, ... is used for exception return. */ | |
| 1402 | + if (IS_USER(s)) | |
| 1403 | + goto illegal_op; | |
| 1404 | + gen_op_movl_T0_T1(); | |
| 1405 | + gen_exception_return(s); | |
| 1406 | + } else { | |
| 1407 | + gen_movl_reg_T1(s, rd); | |
| 1408 | + if (logic_cc) | |
| 1409 | + gen_op_logic_T1_cc(); | |
| 1410 | + } | |
| 1252 | 1411 | break; |
| 1253 | 1412 | case 0x0e: |
| 1254 | 1413 | gen_op_bicl_T0_T1(); |
| ... | ... | @@ -1301,6 +1460,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1301 | 1460 | if (insn & (1 << 21)) /* mult accumulate */ |
| 1302 | 1461 | gen_op_addq_T0_T1(rn, rd); |
| 1303 | 1462 | if (!(insn & (1 << 23))) { /* double accumulate */ |
| 1463 | + ARCH(6); | |
| 1304 | 1464 | gen_op_addq_lo_T0_T1(rn); |
| 1305 | 1465 | gen_op_addq_lo_T0_T1(rd); |
| 1306 | 1466 | } |
| ... | ... | @@ -1322,9 +1482,9 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1322 | 1482 | gen_movl_T0_reg(s, rm); |
| 1323 | 1483 | gen_movl_T1_reg(s, rn); |
| 1324 | 1484 | if (insn & (1 << 22)) { |
| 1325 | - gen_op_swpb_T0_T1(); | |
| 1485 | + gen_ldst(swpb, s); | |
| 1326 | 1486 | } else { |
| 1327 | - gen_op_swpl_T0_T1(); | |
| 1487 | + gen_ldst(swpl, s); | |
| 1328 | 1488 | } |
| 1329 | 1489 | gen_movl_reg_T0(s, rd); |
| 1330 | 1490 | } |
| ... | ... | @@ -1340,14 +1500,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1340 | 1500 | /* load */ |
| 1341 | 1501 | switch(sh) { |
| 1342 | 1502 | case 1: |
| 1343 | - gen_op_lduw_T0_T1(); | |
| 1503 | + gen_ldst(lduw, s); | |
| 1344 | 1504 | break; |
| 1345 | 1505 | case 2: |
| 1346 | - gen_op_ldsb_T0_T1(); | |
| 1506 | + gen_ldst(ldsb, s); | |
| 1347 | 1507 | break; |
| 1348 | 1508 | default: |
| 1349 | 1509 | case 3: |
| 1350 | - gen_op_ldsw_T0_T1(); | |
| 1510 | + gen_ldst(ldsw, s); | |
| 1351 | 1511 | break; |
| 1352 | 1512 | } |
| 1353 | 1513 | gen_movl_reg_T0(s, rd); |
| ... | ... | @@ -1356,18 +1516,18 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1356 | 1516 | if (sh & 1) { |
| 1357 | 1517 | /* store */ |
| 1358 | 1518 | gen_movl_T0_reg(s, rd); |
| 1359 | - gen_op_stl_T0_T1(); | |
| 1519 | + gen_ldst(stl, s); | |
| 1360 | 1520 | gen_op_addl_T1_im(4); |
| 1361 | 1521 | gen_movl_T0_reg(s, rd + 1); |
| 1362 | - gen_op_stl_T0_T1(); | |
| 1522 | + gen_ldst(stl, s); | |
| 1363 | 1523 | if ((insn & (1 << 24)) || (insn & (1 << 20))) |
| 1364 | 1524 | gen_op_addl_T1_im(-4); |
| 1365 | 1525 | } else { |
| 1366 | 1526 | /* load */ |
| 1367 | - gen_op_ldl_T0_T1(); | |
| 1527 | + gen_ldst(ldl, s); | |
| 1368 | 1528 | gen_movl_reg_T0(s, rd); |
| 1369 | 1529 | gen_op_addl_T1_im(4); |
| 1370 | - gen_op_ldl_T0_T1(); | |
| 1530 | + gen_ldst(ldl, s); | |
| 1371 | 1531 | gen_movl_reg_T0(s, rd + 1); |
| 1372 | 1532 | if ((insn & (1 << 24)) || (insn & (1 << 20))) |
| 1373 | 1533 | gen_op_addl_T1_im(-4); |
| ... | ... | @@ -1375,7 +1535,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1375 | 1535 | } else { |
| 1376 | 1536 | /* store */ |
| 1377 | 1537 | gen_movl_T0_reg(s, rd); |
| 1378 | - gen_op_stw_T0_T1(); | |
| 1538 | + gen_ldst(stw, s); | |
| 1379 | 1539 | } |
| 1380 | 1540 | if (!(insn & (1 << 24))) { |
| 1381 | 1541 | gen_add_datah_offset(s, insn); |
| ... | ... | @@ -1393,14 +1553,29 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1393 | 1553 | rn = (insn >> 16) & 0xf; |
| 1394 | 1554 | rd = (insn >> 12) & 0xf; |
| 1395 | 1555 | gen_movl_T1_reg(s, rn); |
| 1556 | + i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000); | |
| 1396 | 1557 | if (insn & (1 << 24)) |
| 1397 | 1558 | gen_add_data_offset(s, insn); |
| 1398 | 1559 | if (insn & (1 << 20)) { |
| 1399 | 1560 | /* load */ |
| 1561 | +#if defined(CONFIG_USER_ONLY) | |
| 1400 | 1562 | if (insn & (1 << 22)) |
| 1401 | - gen_op_ldub_T0_T1(); | |
| 1563 | + gen_op_ldub_raw(); | |
| 1402 | 1564 | else |
| 1403 | - gen_op_ldl_T0_T1(); | |
| 1565 | + gen_op_ldl_raw(); | |
| 1566 | +#else | |
| 1567 | + if (insn & (1 << 22)) { | |
| 1568 | + if (i) | |
| 1569 | + gen_op_ldub_user(); | |
| 1570 | + else | |
| 1571 | + gen_op_ldub_kernel(); | |
| 1572 | + } else { | |
| 1573 | + if (i) | |
| 1574 | + gen_op_ldl_user(); | |
| 1575 | + else | |
| 1576 | + gen_op_ldl_kernel(); | |
| 1577 | + } | |
| 1578 | +#endif | |
| 1404 | 1579 | if (rd == 15) |
| 1405 | 1580 | gen_bx(s); |
| 1406 | 1581 | else |
| ... | ... | @@ -1408,10 +1583,24 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1408 | 1583 | } else { |
| 1409 | 1584 | /* store */ |
| 1410 | 1585 | gen_movl_T0_reg(s, rd); |
| 1586 | +#if defined(CONFIG_USER_ONLY) | |
| 1411 | 1587 | if (insn & (1 << 22)) |
| 1412 | - gen_op_stb_T0_T1(); | |
| 1588 | + gen_op_stb_raw(); | |
| 1413 | 1589 | else |
| 1414 | - gen_op_stl_T0_T1(); | |
| 1590 | + gen_op_stl_raw(); | |
| 1591 | +#else | |
| 1592 | + if (insn & (1 << 22)) { | |
| 1593 | + if (i) | |
| 1594 | + gen_op_stb_user(); | |
| 1595 | + else | |
| 1596 | + gen_op_stb_kernel(); | |
| 1597 | + } else { | |
| 1598 | + if (i) | |
| 1599 | + gen_op_stl_user(); | |
| 1600 | + else | |
| 1601 | + gen_op_stl_kernel(); | |
| 1602 | + } | |
| 1603 | +#endif | |
| 1415 | 1604 | } |
| 1416 | 1605 | if (!(insn & (1 << 24))) { |
| 1417 | 1606 | gen_add_data_offset(s, insn); |
| ... | ... | @@ -1423,11 +1612,17 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1423 | 1612 | case 0x08: |
| 1424 | 1613 | case 0x09: |
| 1425 | 1614 | { |
| 1426 | - int j, n; | |
| 1615 | + int j, n, user; | |
| 1427 | 1616 | /* load/store multiple words */ |
| 1428 | 1617 | /* XXX: store correct base if write back */ |
| 1429 | - if (insn & (1 << 22)) | |
| 1430 | - goto illegal_op; /* only usable in supervisor mode */ | |
| 1618 | + user = 0; | |
| 1619 | + if (insn & (1 << 22)) { | |
| 1620 | + if (IS_USER(s)) | |
| 1621 | + goto illegal_op; /* only usable in supervisor mode */ | |
| 1622 | + | |
| 1623 | + if ((insn & (1 << 15)) == 0) | |
| 1624 | + user = 1; | |
| 1625 | + } | |
| 1431 | 1626 | rn = (insn >> 16) & 0xf; |
| 1432 | 1627 | gen_movl_T1_reg(s, rn); |
| 1433 | 1628 | |
| ... | ... | @@ -1460,21 +1655,26 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1460 | 1655 | if (insn & (1 << i)) { |
| 1461 | 1656 | if (insn & (1 << 20)) { |
| 1462 | 1657 | /* load */ |
| 1463 | - gen_op_ldl_T0_T1(); | |
| 1464 | - if (i == 15) | |
| 1658 | + gen_ldst(ldl, s); | |
| 1659 | + if (i == 15) { | |
| 1465 | 1660 | gen_bx(s); |
| 1466 | - else | |
| 1661 | + } else if (user) { | |
| 1662 | + gen_op_movl_user_T0(i); | |
| 1663 | + } else { | |
| 1467 | 1664 | gen_movl_reg_T0(s, i); |
| 1665 | + } | |
| 1468 | 1666 | } else { |
| 1469 | 1667 | /* store */ |
| 1470 | 1668 | if (i == 15) { |
| 1471 | 1669 | /* special case: r15 = PC + 12 */ |
| 1472 | 1670 | val = (long)s->pc + 8; |
| 1473 | 1671 | gen_op_movl_TN_im[0](val); |
| 1672 | + } else if (user) { | |
| 1673 | + gen_op_movl_T0_user(i); | |
| 1474 | 1674 | } else { |
| 1475 | 1675 | gen_movl_T0_reg(s, i); |
| 1476 | 1676 | } |
| 1477 | - gen_op_stl_T0_T1(); | |
| 1677 | + gen_ldst(stl, s); | |
| 1478 | 1678 | } |
| 1479 | 1679 | j++; |
| 1480 | 1680 | /* no need to add after the last transfer */ |
| ... | ... | @@ -1503,6 +1703,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1503 | 1703 | } |
| 1504 | 1704 | gen_movl_reg_T1(s, rn); |
| 1505 | 1705 | } |
| 1706 | + if ((insn & (1 << 22)) && !user) { | |
| 1707 | + /* Restore CPSR from SPSR. */ | |
| 1708 | + gen_op_movl_T0_spsr(); | |
| 1709 | + gen_op_movl_cpsr_T0(0xffffffff); | |
| 1710 | + s->is_jmp = DISAS_UPDATE; | |
| 1711 | + } | |
| 1506 | 1712 | } |
| 1507 | 1713 | break; |
| 1508 | 1714 | case 0xa: |
| ... | ... | @@ -1532,6 +1738,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1532 | 1738 | if (disas_vfp_insn (env, s, insn)) |
| 1533 | 1739 | goto illegal_op; |
| 1534 | 1740 | break; |
| 1741 | + case 15: | |
| 1742 | + if (disas_cp15_insn (s, insn)) | |
| 1743 | + goto illegal_op; | |
| 1744 | + break; | |
| 1535 | 1745 | default: |
| 1536 | 1746 | /* unknown coprocessor. */ |
| 1537 | 1747 | goto illegal_op; |
| ... | ... | @@ -1561,9 +1771,9 @@ static void disas_thumb_insn(DisasContext *s) |
| 1561 | 1771 | int32_t offset; |
| 1562 | 1772 | int i; |
| 1563 | 1773 | |
| 1564 | - insn = lduw(s->pc); | |
| 1774 | + insn = lduw_code(s->pc); | |
| 1565 | 1775 | s->pc += 2; |
| 1566 | - | |
| 1776 | + | |
| 1567 | 1777 | switch (insn >> 12) { |
| 1568 | 1778 | case 0: case 1: |
| 1569 | 1779 | rd = insn & 7; |
| ... | ... | @@ -1628,7 +1838,7 @@ static void disas_thumb_insn(DisasContext *s) |
| 1628 | 1838 | val = s->pc + 2 + ((insn & 0xff) * 4); |
| 1629 | 1839 | val &= ~(uint32_t)2; |
| 1630 | 1840 | gen_op_movl_T1_im(val); |
| 1631 | - gen_op_ldl_T0_T1(); | |
| 1841 | + gen_ldst(ldl, s); | |
| 1632 | 1842 | gen_movl_reg_T0(s, rd); |
| 1633 | 1843 | break; |
| 1634 | 1844 | } |
| ... | ... | @@ -1771,28 +1981,28 @@ static void disas_thumb_insn(DisasContext *s) |
| 1771 | 1981 | |
| 1772 | 1982 | switch (op) { |
| 1773 | 1983 | case 0: /* str */ |
| 1774 | - gen_op_stl_T0_T1(); | |
| 1984 | + gen_ldst(stl, s); | |
| 1775 | 1985 | break; |
| 1776 | 1986 | case 1: /* strh */ |
| 1777 | - gen_op_stw_T0_T1(); | |
| 1987 | + gen_ldst(stw, s); | |
| 1778 | 1988 | break; |
| 1779 | 1989 | case 2: /* strb */ |
| 1780 | - gen_op_stb_T0_T1(); | |
| 1990 | + gen_ldst(stb, s); | |
| 1781 | 1991 | break; |
| 1782 | 1992 | case 3: /* ldrsb */ |
| 1783 | - gen_op_ldsb_T0_T1(); | |
| 1993 | + gen_ldst(ldsb, s); | |
| 1784 | 1994 | break; |
| 1785 | 1995 | case 4: /* ldr */ |
| 1786 | - gen_op_ldl_T0_T1(); | |
| 1996 | + gen_ldst(ldl, s); | |
| 1787 | 1997 | break; |
| 1788 | 1998 | case 5: /* ldrh */ |
| 1789 | - gen_op_lduw_T0_T1(); | |
| 1999 | + gen_ldst(lduw, s); | |
| 1790 | 2000 | break; |
| 1791 | 2001 | case 6: /* ldrb */ |
| 1792 | - gen_op_ldub_T0_T1(); | |
| 2002 | + gen_ldst(ldub, s); | |
| 1793 | 2003 | break; |
| 1794 | 2004 | case 7: /* ldrsh */ |
| 1795 | - gen_op_ldsw_T0_T1(); | |
| 2005 | + gen_ldst(ldsw, s); | |
| 1796 | 2006 | break; |
| 1797 | 2007 | } |
| 1798 | 2008 | if (op >= 3) /* load */ |
| ... | ... | @@ -1810,12 +2020,12 @@ static void disas_thumb_insn(DisasContext *s) |
| 1810 | 2020 | |
| 1811 | 2021 | if (insn & (1 << 11)) { |
| 1812 | 2022 | /* load */ |
| 1813 | - gen_op_ldl_T0_T1(); | |
| 2023 | + gen_ldst(ldl, s); | |
| 1814 | 2024 | gen_movl_reg_T0(s, rd); |
| 1815 | 2025 | } else { |
| 1816 | 2026 | /* store */ |
| 1817 | 2027 | gen_movl_T0_reg(s, rd); |
| 1818 | - gen_op_stl_T0_T1(); | |
| 2028 | + gen_ldst(stl, s); | |
| 1819 | 2029 | } |
| 1820 | 2030 | break; |
| 1821 | 2031 | |
| ... | ... | @@ -1830,12 +2040,12 @@ static void disas_thumb_insn(DisasContext *s) |
| 1830 | 2040 | |
| 1831 | 2041 | if (insn & (1 << 11)) { |
| 1832 | 2042 | /* load */ |
| 1833 | - gen_op_ldub_T0_T1(); | |
| 2043 | + gen_ldst(ldub, s); | |
| 1834 | 2044 | gen_movl_reg_T0(s, rd); |
| 1835 | 2045 | } else { |
| 1836 | 2046 | /* store */ |
| 1837 | 2047 | gen_movl_T0_reg(s, rd); |
| 1838 | - gen_op_stb_T0_T1(); | |
| 2048 | + gen_ldst(stb, s); | |
| 1839 | 2049 | } |
| 1840 | 2050 | break; |
| 1841 | 2051 | |
| ... | ... | @@ -1850,12 +2060,12 @@ static void disas_thumb_insn(DisasContext *s) |
| 1850 | 2060 | |
| 1851 | 2061 | if (insn & (1 << 11)) { |
| 1852 | 2062 | /* load */ |
| 1853 | - gen_op_lduw_T0_T1(); | |
| 2063 | + gen_ldst(lduw, s); | |
| 1854 | 2064 | gen_movl_reg_T0(s, rd); |
| 1855 | 2065 | } else { |
| 1856 | 2066 | /* store */ |
| 1857 | 2067 | gen_movl_T0_reg(s, rd); |
| 1858 | - gen_op_stw_T0_T1(); | |
| 2068 | + gen_ldst(stw, s); | |
| 1859 | 2069 | } |
| 1860 | 2070 | break; |
| 1861 | 2071 | |
| ... | ... | @@ -1869,12 +2079,12 @@ static void disas_thumb_insn(DisasContext *s) |
| 1869 | 2079 | |
| 1870 | 2080 | if (insn & (1 << 11)) { |
| 1871 | 2081 | /* load */ |
| 1872 | - gen_op_ldl_T0_T1(); | |
| 2082 | + gen_ldst(ldl, s); | |
| 1873 | 2083 | gen_movl_reg_T0(s, rd); |
| 1874 | 2084 | } else { |
| 1875 | 2085 | /* store */ |
| 1876 | 2086 | gen_movl_T0_reg(s, rd); |
| 1877 | - gen_op_stl_T0_T1(); | |
| 2087 | + gen_ldst(stl, s); | |
| 1878 | 2088 | } |
| 1879 | 2089 | break; |
| 1880 | 2090 | |
| ... | ... | @@ -1929,12 +2139,12 @@ static void disas_thumb_insn(DisasContext *s) |
| 1929 | 2139 | if (insn & (1 << i)) { |
| 1930 | 2140 | if (insn & (1 << 11)) { |
| 1931 | 2141 | /* pop */ |
| 1932 | - gen_op_ldl_T0_T1(); | |
| 2142 | + gen_ldst(ldl, s); | |
| 1933 | 2143 | gen_movl_reg_T0(s, i); |
| 1934 | 2144 | } else { |
| 1935 | 2145 | /* push */ |
| 1936 | 2146 | gen_movl_T0_reg(s, i); |
| 1937 | - gen_op_stl_T0_T1(); | |
| 2147 | + gen_ldst(stl, s); | |
| 1938 | 2148 | } |
| 1939 | 2149 | /* advance to the next address. */ |
| 1940 | 2150 | gen_op_addl_T1_T2(); |
| ... | ... | @@ -1943,13 +2153,13 @@ static void disas_thumb_insn(DisasContext *s) |
| 1943 | 2153 | if (insn & (1 << 8)) { |
| 1944 | 2154 | if (insn & (1 << 11)) { |
| 1945 | 2155 | /* pop pc */ |
| 1946 | - gen_op_ldl_T0_T1(); | |
| 2156 | + gen_ldst(ldl, s); | |
| 1947 | 2157 | /* don't set the pc until the rest of the instruction |
| 1948 | 2158 | has completed */ |
| 1949 | 2159 | } else { |
| 1950 | 2160 | /* push lr */ |
| 1951 | 2161 | gen_movl_T0_reg(s, 14); |
| 1952 | - gen_op_stl_T0_T1(); | |
| 2162 | + gen_ldst(stl, s); | |
| 1953 | 2163 | } |
| 1954 | 2164 | gen_op_addl_T1_T2(); |
| 1955 | 2165 | } |
| ... | ... | @@ -1978,19 +2188,20 @@ static void disas_thumb_insn(DisasContext *s) |
| 1978 | 2188 | if (insn & (1 << i)) { |
| 1979 | 2189 | if (insn & (1 << 11)) { |
| 1980 | 2190 | /* load */ |
| 1981 | - gen_op_ldl_T0_T1(); | |
| 2191 | + gen_ldst(ldl, s); | |
| 1982 | 2192 | gen_movl_reg_T0(s, i); |
| 1983 | 2193 | } else { |
| 1984 | 2194 | /* store */ |
| 1985 | 2195 | gen_movl_T0_reg(s, i); |
| 1986 | - gen_op_stl_T0_T1(); | |
| 2196 | + gen_ldst(stl, s); | |
| 1987 | 2197 | } |
| 1988 | 2198 | /* advance to the next address */ |
| 1989 | 2199 | gen_op_addl_T1_T2(); |
| 1990 | 2200 | } |
| 1991 | 2201 | } |
| 1992 | 2202 | /* Base register writeback. */ |
| 1993 | - gen_movl_reg_T1(s, rn); | |
| 2203 | + if ((insn & (1 << rn)) == 0) | |
| 2204 | + gen_movl_reg_T1(s, rn); | |
| 1994 | 2205 | break; |
| 1995 | 2206 | |
| 1996 | 2207 | case 13: |
| ... | ... | @@ -2036,7 +2247,7 @@ static void disas_thumb_insn(DisasContext *s) |
| 2036 | 2247 | case 15: |
| 2037 | 2248 | /* branch and link [and switch to arm] */ |
| 2038 | 2249 | offset = ((int32_t)insn << 21) >> 10; |
| 2039 | - insn = lduw(s->pc); | |
| 2250 | + insn = lduw_code(s->pc); | |
| 2040 | 2251 | offset |= insn & 0x7ff; |
| 2041 | 2252 | |
| 2042 | 2253 | val = (uint32_t)s->pc + 2; |
| ... | ... | @@ -2073,6 +2284,7 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2073 | 2284 | uint16_t *gen_opc_end; |
| 2074 | 2285 | int j, lj; |
| 2075 | 2286 | target_ulong pc_start; |
| 2287 | + uint32_t next_page_start; | |
| 2076 | 2288 | |
| 2077 | 2289 | /* generate intermediate code */ |
| 2078 | 2290 | pc_start = tb->pc; |
| ... | ... | @@ -2088,6 +2300,10 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2088 | 2300 | dc->singlestep_enabled = env->singlestep_enabled; |
| 2089 | 2301 | dc->condjmp = 0; |
| 2090 | 2302 | dc->thumb = env->thumb; |
| 2303 | +#if !defined(CONFIG_USER_ONLY) | |
| 2304 | + dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; | |
| 2305 | +#endif | |
| 2306 | + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; | |
| 2091 | 2307 | nb_gen_labels = 0; |
| 2092 | 2308 | lj = -1; |
| 2093 | 2309 | do { |
| ... | ... | @@ -2124,12 +2340,13 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2124 | 2340 | } |
| 2125 | 2341 | /* Translation stops when a conditional branch is enoutered. |
| 2126 | 2342 | * Otherwise the subsequent code could get translated several times. |
| 2127 | - */ | |
| 2343 | + * Also stop translation when a page boundary is reached. This | |
| 2344 | + * ensures prefech aborts occur at the right place. */ | |
| 2128 | 2345 | } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && |
| 2129 | 2346 | !env->singlestep_enabled && |
| 2130 | - (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32)); | |
| 2131 | - /* It this stage dc->condjmp will only be set when the skipped | |
| 2132 | - * instruction was a conditional branch, and teh PC has already been | |
| 2347 | + dc->pc < next_page_start); | |
| 2348 | + /* At this stage dc->condjmp will only be set when the skipped | |
| 2349 | + * instruction was a conditional branch, and the PC has already been | |
| 2133 | 2350 | * written. */ |
| 2134 | 2351 | if (__builtin_expect(env->singlestep_enabled, 0)) { |
| 2135 | 2352 | /* Make sure the pc is updated, and raise a debug exception. */ |
| ... | ... | @@ -2180,8 +2397,15 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2180 | 2397 | } |
| 2181 | 2398 | } |
| 2182 | 2399 | #endif |
| 2183 | - if (!search_pc) | |
| 2400 | + if (search_pc) { | |
| 2401 | + j = gen_opc_ptr - gen_opc_buf; | |
| 2402 | + lj++; | |
| 2403 | + while (lj <= j) | |
| 2404 | + gen_opc_instr_start[lj++] = 0; | |
| 2405 | + tb->size = 0; | |
| 2406 | + } else { | |
| 2184 | 2407 | tb->size = dc->pc - pc_start; |
| 2408 | + } | |
| 2185 | 2409 | return 0; |
| 2186 | 2410 | } |
| 2187 | 2411 | |
| ... | ... | @@ -2195,6 +2419,17 @@ int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) |
| 2195 | 2419 | return gen_intermediate_code_internal(env, tb, 1); |
| 2196 | 2420 | } |
| 2197 | 2421 | |
| 2422 | +void cpu_reset(CPUARMState *env) | |
| 2423 | +{ | |
| 2424 | +#if defined (CONFIG_USER_ONLY) | |
| 2425 | + /* SVC mode with interrupts disabled. */ | |
| 2426 | + env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; | |
| 2427 | +#else | |
| 2428 | + env->uncached_cpsr = ARM_CPU_MODE_USR; | |
| 2429 | +#endif | |
| 2430 | + env->regs[15] = 0; | |
| 2431 | +} | |
| 2432 | + | |
| 2198 | 2433 | CPUARMState *cpu_arm_init(void) |
| 2199 | 2434 | { |
| 2200 | 2435 | CPUARMState *env; |
| ... | ... | @@ -2203,6 +2438,8 @@ CPUARMState *cpu_arm_init(void) |
| 2203 | 2438 | if (!env) |
| 2204 | 2439 | return NULL; |
| 2205 | 2440 | cpu_exec_init(env); |
| 2441 | + cpu_reset(env); | |
| 2442 | + tlb_flush(env, 1); | |
| 2206 | 2443 | return env; |
| 2207 | 2444 | } |
| 2208 | 2445 | |
| ... | ... | @@ -2211,6 +2448,10 @@ void cpu_arm_close(CPUARMState *env) |
| 2211 | 2448 | free(env); |
| 2212 | 2449 | } |
| 2213 | 2450 | |
| 2451 | +static const char *cpu_mode_names[16] = { | |
| 2452 | + "usr", "fiq", "irq", "svc", "???", "???", "???", "abt", | |
| 2453 | + "???", "???", "???", "und", "???", "???", "???", "sys" | |
| 2454 | +}; | |
| 2214 | 2455 | void cpu_dump_state(CPUState *env, FILE *f, |
| 2215 | 2456 | int (*cpu_fprintf)(FILE *f, const char *fmt, ...), |
| 2216 | 2457 | int flags) |
| ... | ... | @@ -2221,6 +2462,7 @@ void cpu_dump_state(CPUState *env, FILE *f, |
| 2221 | 2462 | float s; |
| 2222 | 2463 | } s0, s1; |
| 2223 | 2464 | CPU_DoubleU d; |
| 2465 | + uint32_t psr; | |
| 2224 | 2466 | |
| 2225 | 2467 | for(i=0;i<16;i++) { |
| 2226 | 2468 | cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]); |
| ... | ... | @@ -2229,13 +2471,15 @@ void cpu_dump_state(CPUState *env, FILE *f, |
| 2229 | 2471 | else |
| 2230 | 2472 | cpu_fprintf(f, " "); |
| 2231 | 2473 | } |
| 2232 | - cpu_fprintf(f, "PSR=%08x %c%c%c%c %c\n", | |
| 2233 | - env->cpsr, | |
| 2234 | - env->cpsr & (1 << 31) ? 'N' : '-', | |
| 2235 | - env->cpsr & (1 << 30) ? 'Z' : '-', | |
| 2236 | - env->cpsr & (1 << 29) ? 'C' : '-', | |
| 2237 | - env->cpsr & (1 << 28) ? 'V' : '-', | |
| 2238 | - env->thumb ? 'T' : 'A'); | |
| 2474 | + psr = cpsr_read(env); | |
| 2475 | + cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d %x\n", | |
| 2476 | + psr, | |
| 2477 | + psr & (1 << 31) ? 'N' : '-', | |
| 2478 | + psr & (1 << 30) ? 'Z' : '-', | |
| 2479 | + psr & (1 << 29) ? 'C' : '-', | |
| 2480 | + psr & (1 << 28) ? 'V' : '-', | |
| 2481 | + psr & CPSR_T ? 'T' : 'A', | |
| 2482 | + cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26); | |
| 2239 | 2483 | |
| 2240 | 2484 | for (i = 0; i < 16; i++) { |
| 2241 | 2485 | d.d = env->vfp.regs[i]; |
| ... | ... | @@ -2250,27 +2494,3 @@ void cpu_dump_state(CPUState *env, FILE *f, |
| 2250 | 2494 | cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.fpscr); |
| 2251 | 2495 | } |
| 2252 | 2496 | |
| 2253 | -target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | |
| 2254 | -{ | |
| 2255 | - return addr; | |
| 2256 | -} | |
| 2257 | - | |
| 2258 | -#if defined(CONFIG_USER_ONLY) | |
| 2259 | - | |
| 2260 | -int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, | |
| 2261 | - int is_user, int is_softmmu) | |
| 2262 | -{ | |
| 2263 | - env->cp15_6 = address; | |
| 2264 | - if (rw == 2) { | |
| 2265 | - env->exception_index = EXCP_PREFETCH_ABORT; | |
| 2266 | - } else { | |
| 2267 | - env->exception_index = EXCP_DATA_ABORT; | |
| 2268 | - } | |
| 2269 | - return 1; | |
| 2270 | -} | |
| 2271 | - | |
| 2272 | -#else | |
| 2273 | - | |
| 2274 | -#error not implemented | |
| 2275 | - | |
| 2276 | -#endif | ... | ... |
vl.c
| ... | ... | @@ -3359,6 +3359,19 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) |
| 3359 | 3359 | tlb_flush(env, 1); |
| 3360 | 3360 | return 0; |
| 3361 | 3361 | } |
| 3362 | + | |
| 3363 | +#elif defined(TARGET_ARM) | |
| 3364 | + | |
| 3365 | +/* ??? Need to implement these. */ | |
| 3366 | +void cpu_save(QEMUFile *f, void *opaque) | |
| 3367 | +{ | |
| 3368 | +} | |
| 3369 | + | |
| 3370 | +int cpu_load(QEMUFile *f, void *opaque, int version_id) | |
| 3371 | +{ | |
| 3372 | + return 0; | |
| 3373 | +} | |
| 3374 | + | |
| 3362 | 3375 | #else |
| 3363 | 3376 | |
| 3364 | 3377 | #warning No CPU save/restore functions |
| ... | ... | @@ -4054,6 +4067,10 @@ void register_machines(void) |
| 4054 | 4067 | #else |
| 4055 | 4068 | qemu_register_machine(&sun4m_machine); |
| 4056 | 4069 | #endif |
| 4070 | +#elif defined(TARGET_ARM) | |
| 4071 | + qemu_register_machine(&integratorcp_machine); | |
| 4072 | +#else | |
| 4073 | +#error unsupported CPU | |
| 4057 | 4074 | #endif |
| 4058 | 4075 | } |
| 4059 | 4076 | ... | ... |
vl.h
| ... | ... | @@ -928,6 +928,9 @@ void do_usb_add(const char *devname); |
| 928 | 928 | void do_usb_del(const char *devname); |
| 929 | 929 | void usb_info(void); |
| 930 | 930 | |
| 931 | +/* integratorcp.c */ | |
| 932 | +extern QEMUMachine integratorcp_machine; | |
| 933 | + | |
| 931 | 934 | /* ps2.c */ |
| 932 | 935 | void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); |
| 933 | 936 | void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); | ... | ... |