Commit b5ff1b31 authored by bellard's avatar bellard
Browse files

ARM system emulation (Paul Brook)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1661 c046a42c-6fe2-441c-8c8c-71466251a162
parent 0e43e99c
version 0.7.3:
- ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
cpu (Paul Brook)
- SMP support
- Mac OS X cocoa improvements (Mike Kronenberg)
- Mac OS X CoreAudio driver (Mike Kronenberg)
- DirectSound driver (malc)
......@@ -10,7 +13,6 @@ version 0.7.3:
- Linux host serial port access
- Linux host low level parallel port access
- New network emulation code supporting VLANs.
- SMP support
version 0.7.2:
......
......@@ -211,7 +211,7 @@ LIBOBJS+= op_helper.o helper.o
endif
ifeq ($(TARGET_BASE_ARCH), arm)
LIBOBJS+= op_helper.o
LIBOBJS+= op_helper.o helper.o
endif
# 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
VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
endif
endif
ifeq ($(TARGET_BASE_ARCH), arm)
VL_OBJS+= integratorcp.o ps2.o
endif
ifdef CONFIG_GDBSTUB
VL_OBJS+=gdbstub.o
endif
......
......@@ -227,7 +227,7 @@ fi
if test -z "$target_list" ; then
# these targets are portable
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu"
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu arm-softmmu"
# the following are Linux specific
if [ "$linux" = "yes" ] ; then
target_list="i386-user arm-user armeb-user sparc-user ppc-user $target_list"
......
......@@ -172,7 +172,9 @@ static inline TranslationBlock *tb_find_fast(void)
pc = cs_base + env->eip;
#elif defined(TARGET_ARM)
flags = env->thumb | (env->vfp.vec_len << 1)
| (env->vfp.vec_stride << 4);
| (env->vfp.vec_stride << 4);
if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR)
flags |= (1 << 6);
cs_base = 0;
pc = env->regs[15];
#elif defined(TARGET_SPARC)
......@@ -322,15 +324,6 @@ int cpu_exec(CPUState *env1)
CC_OP = CC_OP_EFLAGS;
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
#elif defined(TARGET_ARM)
{
unsigned int psr;
psr = env->cpsr;
env->CF = (psr >> 29) & 1;
env->NZF = (psr & 0xc0000000) ^ 0x40000000;
env->VF = (psr << 3) & 0x80000000;
env->QF = (psr >> 27) & 1;
env->cpsr = psr & ~CACHED_CPSR_BITS;
}
#elif defined(TARGET_SPARC)
#if defined(reg_REGWPTR)
saved_regwptr = REGWPTR;
......@@ -379,6 +372,8 @@ int cpu_exec(CPUState *env1)
do_interrupt(env);
#elif defined(TARGET_SPARC)
do_interrupt(env->exception_index);
#elif defined(TARGET_ARM)
do_interrupt(env);
#endif
}
env->exception_index = -1;
......@@ -508,8 +503,19 @@ int cpu_exec(CPUState *env1)
//do_interrupt(0, 0, 0, 0, 0);
env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
}
#elif defined(TARGET_ARM)
if (interrupt_request & CPU_INTERRUPT_FIQ
&& !(env->uncached_cpsr & CPSR_F)) {
env->exception_index = EXCP_FIQ;
do_interrupt(env);
}
if (interrupt_request & CPU_INTERRUPT_HARD
&& !(env->uncached_cpsr & CPSR_I)) {
env->exception_index = EXCP_IRQ;
do_interrupt(env);
}
#endif
if (interrupt_request & CPU_INTERRUPT_EXITTB) {
if (env->interrupt_request & CPU_INTERRUPT_EXITTB) {
env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
/* ensure that no TB jump will be modified as
the program flow was changed */
......@@ -526,7 +532,7 @@ int cpu_exec(CPUState *env1)
}
}
#ifdef DEBUG_EXEC
if ((loglevel & CPU_LOG_EXEC)) {
if ((loglevel & CPU_LOG_TB_CPU)) {
#if defined(TARGET_I386)
/* restore flags in standard format */
#ifdef reg_EAX
......@@ -557,9 +563,7 @@ int cpu_exec(CPUState *env1)
cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
#elif defined(TARGET_ARM)
env->cpsr = compute_cpsr();
cpu_dump_state(env, logfile, fprintf, 0);
env->cpsr &= ~CACHED_CPSR_BITS;
#elif defined(TARGET_SPARC)
REGWPTR = env->regbase + (env->cwp * 16);
env->regwptr = REGWPTR;
......@@ -760,7 +764,6 @@ int cpu_exec(CPUState *env1)
EDI = saved_EDI;
#endif
#elif defined(TARGET_ARM)
env->cpsr = compute_cpsr();
/* XXX: Save/restore host fpu exception state?. */
#elif defined(TARGET_SPARC)
#if defined(reg_REGWPTR)
......
......@@ -549,8 +549,10 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
#elif defined (TARGET_SPARC)
is_user = (env->psrs == 0);
#elif defined (TARGET_ARM)
is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR);
#else
#error "Unimplemented !"
#error unimplemented CPU
#endif
if (__builtin_expect(env->tlb_read[is_user][index].address !=
(addr & TARGET_PAGE_MASK), 0)) {
......
......@@ -1868,14 +1868,14 @@ int cpu_register_io_memory(int io_index,
int i;
if (io_index <= 0) {
if (io_index >= IO_MEM_NB_ENTRIES)
if (io_mem_nb >= IO_MEM_NB_ENTRIES)
return -1;
io_index = io_mem_nb++;
} else {
if (io_index >= IO_MEM_NB_ENTRIES)
return -1;
}
for(i = 0;i < 3; i++) {
io_mem_read[io_index][i] = mem_read[i];
io_mem_write[io_index][i] = mem_write[i];
......
......@@ -399,7 +399,7 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
memset (ptr, 0, 8 * 12 + 4);
ptr += 8 * 12 + 4;
/* CPSR (4 bytes). */
*(uint32_t *)ptr = tswapl (env->cpsr);
*(uint32_t *)ptr = tswapl (cpsr_read(env));
ptr += 4;
return ptr - mem_buf;
......@@ -419,7 +419,7 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
}
/* Ignore FPA regs and scr. */
ptr += 8 * 12 + 4;
env->cpsr = tswapl(*(uint32_t *)ptr);
cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
}
#else
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)
#elif defined (TARGET_SPARC)
env->pc = addr;
env->npc = addr + 4;
#elif defined (TARGET_ARM)
env->regs[15] = addr;
#endif
}
#ifdef CONFIG_USER_ONLY
......@@ -481,6 +483,8 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
#elif defined (TARGET_SPARC)
env->pc = addr;
env->npc = addr + 4;
#elif defined (TARGET_ARM)
env->regs[15] = addr;
#endif
}
cpu_single_step(env, 1);
......
This diff is collapsed.
......@@ -331,6 +331,7 @@ void cpu_loop(CPUARMState *env)
int trapnr;
unsigned int n, insn;
target_siginfo_t info;
uint32_t addr;
for(;;) {
trapnr = cpu_arm_exec(env);
......@@ -397,13 +398,18 @@ void cpu_loop(CPUARMState *env)
/* just indicate that signals should be handled asap */
break;
case EXCP_PREFETCH_ABORT:
addr = env->cp15.c6_data;
goto do_segv;
case EXCP_DATA_ABORT:
addr = env->cp15.c6_insn;
goto do_segv;
do_segv:
{
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->cp15_6;
info._sifields._sigfault._addr = addr;
queue_signal(info.si_signo, &info);
}
break;
......@@ -1190,10 +1196,10 @@ int main(int argc, char **argv)
#elif defined(TARGET_ARM)
{
int i;
cpsr_write(env, regs->uregs[16], 0xffffffff);
for(i = 0; i < 16; i++) {
env->regs[i] = regs->uregs[i];
}
env->cpsr = regs->uregs[16];
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
......
......@@ -1003,7 +1003,7 @@ setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
__put_user_error(env->regs[14], &sc->arm_lr, err);
__put_user_error(env->regs[15], &sc->arm_pc, err);
#ifdef TARGET_CONFIG_CPU_32
__put_user_error(env->cpsr, &sc->arm_cpsr, err);
__put_user_error(cpsr_read(env), &sc->arm_cpsr, err);
#endif
__put_user_error(/* current->thread.trap_no */ 0, &sc->trap_no, err);
......@@ -1040,9 +1040,9 @@ setup_return(CPUState *env, struct emulated_sigaction *ka,
target_ulong retcode;
int thumb = 0;
#if defined(TARGET_CONFIG_CPU_32)
#if 0
target_ulong cpsr = env->cpsr;
#if 0
/*
* Maybe we need to deliver a 32-bit signal to a 26-bit task.
*/
......@@ -1088,8 +1088,10 @@ setup_return(CPUState *env, struct emulated_sigaction *ka,
env->regs[14] = retcode;
env->regs[15] = handler & (thumb ? ~1 : ~3);
#if 0
#ifdef TARGET_CONFIG_CPU_32
env->cpsr = cpsr;
#endif
#endif
return 0;
......@@ -1157,6 +1159,7 @@ static int
restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
{
int err = 0;
uint32_t cpsr;
__get_user_error(env->regs[0], &sc->arm_r0, err);
__get_user_error(env->regs[1], &sc->arm_r1, err);
......@@ -1175,7 +1178,8 @@ restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
__get_user_error(env->regs[14], &sc->arm_lr, err);
__get_user_error(env->regs[15], &sc->arm_pc, err);
#ifdef TARGET_CONFIG_CPU_32
__get_user_error(env->cpsr, &sc->arm_cpsr, err);
__get_user_error(cpsr, &sc->arm_cpsr, err);
cpsr_write(env, cpsr, 0xffffffff);
#endif
err |= !valid_user_regs(env);
......
......@@ -59,6 +59,10 @@
#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
#elif defined (TARGET_SPARC)
#define CPU_MEM_INDEX ((env->psrs) == 0)
#elif defined (TARGET_ARM)
#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
#else
#error unsupported CPU
#endif
#define MMUSUFFIX _mmu
......@@ -72,6 +76,10 @@
#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
#elif defined (TARGET_SPARC)
#define CPU_MEM_INDEX ((env->psrs) == 0)
#elif defined (TARGET_ARM)
#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
#else
#error unsupported CPU
#endif
#define MMUSUFFIX _cmmu
......
......@@ -32,6 +32,8 @@
#define EXCP_SWI 2 /* software interrupt */
#define EXCP_PREFETCH_ABORT 3
#define EXCP_DATA_ABORT 4
#define EXCP_IRQ 5
#define EXCP_FIQ 6
/* We currently assume float and double are IEEE single and double
precision respectively.
......@@ -42,8 +44,22 @@
*/
typedef struct CPUARMState {
/* Regs for current mode. */
uint32_t regs[16];
uint32_t cpsr;
/* Frequently accessed CPSR bits are stored separately for efficiently.
This contains all the other bits. Use cpsr_{read,write} to accless
the whole CPSR. */
uint32_t uncached_cpsr;
uint32_t spsr;
/* Banked registers. */
uint32_t banked_spsr[6];
uint32_t banked_r13[6];
uint32_t banked_r14[6];
/* These hold r8-r12. */
uint32_t usr_regs[5];
uint32_t fiq_regs[5];
/* cpsr flag cache for faster execution */
uint32_t CF; /* 0 or 1 */
......@@ -53,8 +69,21 @@ typedef struct CPUARMState {
int thumb; /* 0 = arm mode, 1 = thumb mode */
/* coprocessor 15 (MMU) status */
uint32_t cp15_6;
/* System control coprocessor (cp15) */
struct {
uint32_t c1_sys; /* System control register. */
uint32_t c1_coproc; /* Coprocessor access register. */
uint32_t c2; /* MMU translation table base. */
uint32_t c3; /* MMU domain access control register. */
uint32_t c5_insn; /* Fault status registers. */
uint32_t c5_data;
uint32_t c6_insn; /* Fault address registers. */
uint32_t c6_data;
uint32_t c9_insn; /* Cache lockdown registers. */
uint32_t c9_data;
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
} cp15;
/* exception/interrupt handling */
jmp_buf jmp_env;
......@@ -87,6 +116,9 @@ typedef struct CPUARMState {
CPUARMState *cpu_arm_init(void);
int cpu_arm_exec(CPUARMState *s);
void cpu_arm_close(CPUARMState *s);
void do_interrupt(CPUARMState *);
void switch_mode(CPUARMState *, int);
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
......@@ -94,7 +126,69 @@ struct siginfo;
int cpu_arm_signal_handler(int host_signum, struct siginfo *info,
void *puc);
#define CPSR_M (0x1f)
#define CPSR_T (1 << 5)
#define CPSR_F (1 << 6)
#define CPSR_I (1 << 7)
#define CPSR_A (1 << 8)
#define CPSR_E (1 << 9)
#define CPSR_IT_2_7 (0xfc00)
/* Bits 20-23 reserved. */
#define CPSR_J (1 << 24)
#define CPSR_IT_0_1 (3 << 25)
#define CPSR_Q (1 << 27)
#define CPSR_NZCV (0xf << 28)
#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV)
/* Return the current CPSR value. */
static inline uint32_t cpsr_read(CPUARMState *env)
{
int ZF;
ZF = (env->NZF == 0);
return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
| (env->thumb << 5);
}
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
{
/* NOTE: N = 1 and Z = 1 cannot be stored currently */
if (mask & CPSR_NZCV) {
env->NZF = (val & 0xc0000000) ^ 0x40000000;
env->CF = (val >> 29) & 1;
env->VF = (val << 3) & 0x80000000;
}
if (mask & CPSR_Q)
env->QF = ((val & CPSR_Q) != 0);
if (mask & CPSR_T)
env->thumb = ((val & CPSR_T) != 0);
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
switch_mode(env, val & CPSR_M);
}
mask &= ~CACHED_CPSR_BITS;
env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
}
enum arm_cpu_mode {
ARM_CPU_MODE_USR = 0x10,
ARM_CPU_MODE_FIQ = 0x11,
ARM_CPU_MODE_IRQ = 0x12,
ARM_CPU_MODE_SVC = 0x13,
ARM_CPU_MODE_ABT = 0x17,
ARM_CPU_MODE_UND = 0x1b,
ARM_CPU_MODE_SYS = 0x1f
};
#if defined(CONFIG_USER_ONLY)
#define TARGET_PAGE_BITS 12
#else
/* The ARM MMU allows 1k pages. */
/* ??? Linux doesn't actually use these, and they're deprecated in recent
architecture revisions. Maybe an a configure option to disable them. */
#define TARGET_PAGE_BITS 10
#endif
#include "cpu-all.h"
#endif
......@@ -34,16 +34,6 @@ register uint32_t T2 asm(AREG3);
#include "cpu.h"
#include "exec-all.h"
/* Implemented CPSR bits. */
#define CACHED_CPSR_BITS 0xf8000000
static inline int compute_cpsr(void)
{
int ZF;
ZF = (env->NZF == 0);
return env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27);
}
static inline void env_to_regs(void)
{
}
......@@ -55,10 +45,17 @@ static inline void regs_to_env(void)
int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int is_user, int is_softmmu);
#if !defined(CONFIG_USER_ONLY)
#include "softmmu_exec.h"
#endif
/* In op_helper.c */
void cpu_lock(void);
void cpu_unlock(void);
void helper_set_cp15(CPUState *, uint32_t, uint32_t);
uint32_t helper_get_cp15(CPUState *, uint32_t);
void cpu_loop_exit(void);
void raise_exception(int);
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cpu.h"
#include "exec-all.h"
#if defined(CONFIG_USER_ONLY)
void do_interrupt (CPUState *env)
{
env->exception_index = -1;
}
int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int is_user, int is_softmmu)
{
if (rw == 2) {
env->exception_index = EXCP_PREFETCH_ABORT;
env->cp15.c6_insn = address;
} else {
env->exception_index = EXCP_DATA_ABORT;
env->cp15.c6_data = address;
}
return 1;
}
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
return addr;
}
/* These should probably raise undefined insn exceptions. */
void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
{
cpu_abort(env, "cp15 insn %08x\n", insn);
}
uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
{
cpu_abort(env, "cp15 insn %08x\n", insn);
return 0;
}
void switch_mode(CPUState *env, int mode)
{
if (mode != ARM_CPU_MODE_USR)
cpu_abort(env, "Tried to switch out of user mode\n");
}
#else
/* Map CPU modes onto saved register banks. */
static inline int bank_number (int mode)
{
switch (mode) {
case ARM_CPU_MODE_USR:
case ARM_CPU_MODE_SYS:
return 0;
case ARM_CPU_MODE_SVC:
return 1;
case ARM_CPU_MODE_ABT:
return 2;
case ARM_CPU_MODE_UND:
return 3;
case ARM_CPU_MODE_IRQ:
return 4;
case ARM_CPU_MODE_FIQ:
return 5;
}
cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
return -1;
}
void switch_mode(CPUState *env, int mode)
{
int old_mode;
int i;
old_mode = env->uncached_cpsr & CPSR_M;
if (mode == old_mode)
return;
if (old_mode == ARM_CPU_MODE_FIQ) {
memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
memcpy (env->regs, env->usr_regs + 8, 5 * sizeof(uint32_t));
} else if (mode == ARM_CPU_MODE_FIQ) {
memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
memcpy (env->regs, env->fiq_regs + 8, 5 * sizeof(uint32_t));
}
i = bank_number(old_mode);
env->banked_r13[i] = env->regs[13];
env->banked_r14[i] = env->regs[14];
env->banked_spsr[i] = env->spsr;
i = bank_number(mode);
env->regs[13] = env->banked_r13[i];
env->regs[14] = env->banked_r14[i];
env->spsr = env->banked_spsr[i];
}
/* Handle a CPU exception. */
void do_interrupt(CPUARMState *env)
{
uint32_t addr;
uint32_t mask;
int new_mode;
uint32_t offset;
/* TODO: Vectored interrupt controller. */
switch (env->exception_index) {
case EXCP_UDEF:
new_mode = ARM_CPU_MODE_UND;
addr = 0x04;
mask = CPSR_I;
if (env->thumb)
offset = 2;
else
offset = 4;
break;
case EXCP_SWI:
new_mode = ARM_CPU_MODE_SVC;
addr = 0x08;
mask = CPSR_I;
/* The PC already points to the next instructon. */
offset = 0;
break;
case EXCP_PREFETCH_ABORT:
new_mode = ARM_CPU_MODE_ABT;
addr = 0x0c;
mask = CPSR_A | CPSR_I;
offset = 4;
break;
case EXCP_DATA_ABORT:
new_mode = ARM_CPU_MODE_ABT;
addr = 0x10;
mask = CPSR_A | CPSR_I;
offset = 8;
break;
case EXCP_IRQ:
new_mode = ARM_CPU_MODE_IRQ;
addr = 0x18;
/* Disable IRQ and imprecise data aborts. */
mask = CPSR_A | CPSR_I;
offset = 4;
break;
case EXCP_FIQ:
new_mode = ARM_CPU_MODE_FIQ;
addr = 0x1c;
/* Disable FIQ, IRQ and imprecise data aborts. */
mask = CPSR_A | CPSR_I | CPSR_F;
offset = 4;
break;
default: