diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-06-15 20:05:50 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-06-15 20:05:50 +0000 |
commit | b346ff468efed71e42e9f306c6bf975809cd2c0f (patch) | |
tree | 335f7701c2883582cf9c0d18302bb681531538aa | |
parent | 5a9fdfec7eff4f053705cf160be87ebf01a57833 (diff) |
ARM emulation support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@244 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r-- | exec.c | 5 | ||||
-rw-r--r-- | exec.h | 71 | ||||
-rw-r--r-- | linux-user/elfload.c | 42 | ||||
-rw-r--r-- | linux-user/main.c | 84 | ||||
-rw-r--r-- | linux-user/qemu.h | 28 | ||||
-rw-r--r-- | linux-user/signal.c | 31 |
6 files changed, 231 insertions, 30 deletions
@@ -70,7 +70,7 @@ unsigned long host_page_mask; static PageDesc *l1_map[L1_SIZE]; -void page_init(void) +static void page_init(void) { /* NOTE: we can always suppose that host_page_size >= TARGET_PAGE_SIZE */ @@ -190,10 +190,11 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) spin_unlock(&tb_lock); } -void cpu_x86_tblocks_init(void) +void cpu_exec_init(void) { if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; + page_init(); } } @@ -18,6 +18,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* allow to see translation results - the slowdown should be negligible, so we leave it */ +#define DEBUG_DISAS + +/* is_jmp field values */ +#define DISAS_NEXT 0 /* next instruction can be analyzed */ +#define DISAS_JUMP 1 /* only pc was modified dynamically */ +#define DISAS_UPDATE 2 /* cpu state was modified dynamically */ +#define DISAS_TB_JUMP 3 /* only pc was modified statically */ + +struct TranslationBlock; + +/* XXX: make safe guess about sizes */ +#define MAX_OP_PER_INSTR 32 +#define OPC_BUF_SIZE 512 +#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR) + +#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * 3) + +extern uint16_t gen_opc_buf[OPC_BUF_SIZE]; +extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE]; +extern uint32_t gen_opc_pc[OPC_BUF_SIZE]; +extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; + +#if defined(TARGET_I386) + #define GEN_FLAG_CODE32_SHIFT 0 #define GEN_FLAG_ADDSEG_SHIFT 1 #define GEN_FLAG_SS32_SHIFT 2 @@ -27,13 +52,18 @@ #define GEN_FLAG_CPL_SHIFT 9 #define GEN_FLAG_IOPL_SHIFT 12 /* same position as eflags */ -struct TranslationBlock; -int cpu_x86_gen_code(struct TranslationBlock *tb, - int max_code_size, int *gen_code_size_ptr); -int cpu_x86_search_pc(struct TranslationBlock *tb, - uint32_t *found_pc, unsigned long searched_pc); -void cpu_x86_tblocks_init(void); -void page_init(void); +#endif + +extern FILE *logfile; +extern int loglevel; + +int gen_intermediate_code(struct TranslationBlock *tb, int search_pc); +void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf); +int cpu_gen_code(struct TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr); +int cpu_search_pc(struct TranslationBlock *tb, + uint32_t *found_pc, unsigned long searched_pc); +void cpu_exec_init(void); int page_unprotect(unsigned long address); #define CODE_GEN_MAX_SIZE 65536 @@ -167,6 +197,33 @@ TranslationBlock *tb_find_pc(unsigned long pc_ptr); #define offsetof(type, field) ((size_t) &((type *)0)->field) #endif +#if defined(__powerpc__) + +/* on PowerPC we patch the jump instruction directly */ +#define JUMP_TB(tbparam, n, eip)\ +do {\ + static void __attribute__((unused)) *__op_label ## n = &&label ## n;\ + asm volatile ("b %0" : : "i" (&__op_jmp ## n));\ +label ## n:\ + T0 = (long)(tbparam) + (n);\ + EIP = eip;\ +} while (0) + +#else + +/* jump to next block operations (more portable code, does not need + cache flushing, but slower because of indirect jump) */ +#define JUMP_TB(tbparam, n, eip)\ +do {\ + static void __attribute__((unused)) *__op_label ## n = &&label ## n;\ + goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\ +label ## n:\ + T0 = (long)(tbparam) + (n);\ + EIP = eip;\ +} while (0) + +#endif + #ifdef __powerpc__ static inline int testandset (int *p) { diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2afde77a88..94059917c2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -38,6 +38,45 @@ A value of 0 tells we have no such handler. */ #define ELF_PLAT_INIT(_r) _r->edx = 0 +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->esp = infop->start_stack; + regs->eip = infop->entry; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif + +#ifdef TARGET_ARM + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_ARM ) + +#define ELF_CLASS ELFCLASS32 +#ifdef TARGET_WORDS_BIGENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif +#define ELF_ARCH EM_ARM + +#define ELF_PLAT_INIT(_r) _r->ARM_r0 = 0 + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + target_long *stack = (void *)infop->start_stack; + memset(regs, 0, sizeof(*regs)); + regs->ARM_cpsr = 0x10; + regs->ARM_pc = infop->entry; + regs->ARM_sp = infop->start_stack; + regs->ARM_r2 = tswapl(stack[2]); /* envp */ + regs->ARM_r1 = tswapl(stack[1]); /* argv */ + regs->ARM_r0 = tswapl(stack[0]); /* argc */ +} + #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 @@ -1148,8 +1187,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, } if(retval>=0) { /* success. Initialize important registers */ - regs->esp = infop->start_stack; - regs->eip = infop->entry; + init_thread(regs, infop); return retval; } diff --git a/linux-user/main.c b/linux-user/main.c index 6c03d6f925..889958b313 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -63,6 +63,7 @@ void gemu_log(const char *fmt, ...) va_end(ap); } +#ifdef TARGET_I386 /***********************************************************/ /* CPUX86 core interface */ @@ -238,20 +239,76 @@ void cpu_loop(CPUX86State *env) process_pending_signals(env); } } +#endif + +#ifdef TARGET_ARM + +#define ARM_SYSCALL_BASE 0x900000 + +void cpu_loop(CPUARMState *env) +{ + int trapnr; + unsigned int n, insn; + target_siginfo_t info; + + for(;;) { + trapnr = cpu_arm_exec(env); + switch(trapnr) { + case EXCP_UDEF: + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->regs[15]; + queue_signal(info.si_signo, &info); + break; + case EXCP_SWI: + { + /* system call */ + insn = ldl((void *)(env->regs[15] - 4)); + n = insn & 0xffffff; + if (n >= ARM_SYSCALL_BASE) { + /* linux syscall */ + n -= ARM_SYSCALL_BASE; + env->regs[0] = do_syscall(env, + n, + env->regs[0], + env->regs[1], + env->regs[2], + env->regs[3], + env->regs[4], + 0); + } else { + goto error; + } + } + break; + default: + error: + fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + cpu_arm_dump_state(env, stderr, 0); + abort(); + } + process_pending_signals(env); + } +} + +#endif void usage(void) { printf("qemu version " QEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n" "usage: qemu [-h] [-d] [-L path] [-s size] program [arguments...]\n" - "Linux x86 emulator\n" + "Linux CPU emulator (compiled for %s emulation)\n" "\n" "-h print this help\n" - "-L path set the x86 elf interpreter prefix (default=%s)\n" - "-s size set the x86 stack size in bytes (default=%ld)\n" + "-L path set the elf interpreter prefix (default=%s)\n" + "-s size set the stack size in bytes (default=%ld)\n" "\n" "debug options:\n" "-d activate log (logfile=%s)\n" "-p pagesize set the host page size to 'pagesize'\n", + TARGET_ARCH, interp_prefix, x86_stack_size, DEBUG_LOGFILE); @@ -259,7 +316,7 @@ void usage(void) } /* XXX: currently only used for async signals (see signal.c) */ -CPUX86State *global_env; +CPUState *global_env; /* used to free thread contexts */ TaskState *first_task_state; @@ -269,7 +326,7 @@ int main(int argc, char **argv) struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; TaskState ts1, *ts = &ts1; - CPUX86State *env; + CPUState *env; int optind; const char *r; @@ -337,7 +394,7 @@ int main(int argc, char **argv) /* NOTE: we need to init the CPU at this stage to get the host_page_size */ - env = cpu_x86_init(); + env = cpu_init(); if (elf_exec(filename, argv+optind, environ, regs, info) != 0) { printf("Error loading %s\n", filename); @@ -353,8 +410,7 @@ int main(int argc, char **argv) fprintf(logfile, "end_data 0x%08lx\n" , info->end_data); fprintf(logfile, "start_stack 0x%08lx\n" , info->start_stack); fprintf(logfile, "brk 0x%08lx\n" , info->brk); - fprintf(logfile, "esp 0x%08lx\n" , regs->esp); - fprintf(logfile, "eip 0x%08lx\n" , regs->eip); + fprintf(logfile, "entry 0x%08lx\n" , info->entry); } target_set_brk((char *)info->brk); @@ -368,6 +424,7 @@ int main(int argc, char **argv) env->opaque = ts; ts->used = 1; +#if defined(TARGET_I386) /* linux register setup */ env->regs[R_EAX] = regs->eax; env->regs[R_EBX] = regs->ebx; @@ -419,6 +476,17 @@ int main(int argc, char **argv) cpu_x86_load_seg(env, R_SS, __USER_DS); cpu_x86_load_seg(env, R_FS, __USER_DS); cpu_x86_load_seg(env, R_GS, __USER_DS); +#elif defined(TARGET_ARM) + { + int i; + for(i = 0; i < 16; i++) { + env->regs[i] = regs->uregs[i]; + } + env->cpsr = regs->uregs[16]; + } +#else +#error unsupported target CPU +#endif cpu_loop(env); /* never exits */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index d6e615e0ab..d0a96e6e1f 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -6,10 +6,8 @@ #include <signal.h> #include "syscall_defs.h" -#ifdef TARGET_I386 -#include "cpu-i386.h" -#include "syscall-i386.h" -#endif +#include "cpu-" TARGET_ARCH ".h" +#include "syscall-" TARGET_ARCH ".h" /* This struct is used to hold certain information about the image. * Basically, it replicates in user space what would be certain @@ -33,6 +31,7 @@ struct image_info { int personality; }; +#ifdef TARGET_I386 /* Information about the current linux thread */ struct vm86_saved_state { uint32_t eax; /* return code */ @@ -47,16 +46,19 @@ struct vm86_saved_state { uint32_t eip; uint16_t cs, ss, ds, es, fs, gs; }; +#endif /* NOTE: we force a big alignment so that the stack stored after is aligned too */ typedef struct TaskState { struct TaskState *next; +#ifdef TARGET_I386 struct target_vm86plus_struct *target_v86; struct vm86_saved_state vm86_saved_regs; struct target_vm86plus_struct vm86plus; uint32_t v86flags; uint32_t v86mask; +#endif int used; /* non zero if used */ uint8_t stack[0]; } __attribute__((aligned(16))) TaskState; @@ -71,23 +73,31 @@ void syscall_init(void); long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6); void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); -extern CPUX86State *global_env; -void cpu_loop(CPUX86State *env); -void process_pending_signals(void *cpu_env); -void signal_init(void); -int queue_signal(int sig, target_siginfo_t *info); +extern CPUState *global_env; +void cpu_loop(CPUState *env); void init_paths(const char *prefix); const char *path(const char *pathname); extern int loglevel; extern FILE *logfile; +/* signal.c */ +void process_pending_signals(void *cpu_env); +void signal_init(void); +int queue_signal(int sig, target_siginfo_t *info); +void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); +void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); +long do_sigreturn(CPUState *env); +long do_rt_sigreturn(CPUState *env); + +#ifdef TARGET_I386 /* vm86.c */ void save_v86_state(CPUX86State *env); void handle_vm86_trap(CPUX86State *env, int trapno); void handle_vm86_fault(CPUX86State *env); int do_vm86(CPUX86State *env, long subfunction, struct target_vm86plus_struct * target_v86); +#endif /* mmap.c */ int target_mprotect(unsigned long start, unsigned long len, int prot); diff --git a/linux-user/signal.c b/linux-user/signal.c index 2aad2ab5f4..8c8bc0b269 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -318,7 +318,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info, /* the CPU emulator uses some host signals to detect exceptions, we we forward to it some signals */ if (host_signum == SIGSEGV || host_signum == SIGBUS) { - if (cpu_x86_signal_handler(host_signum, info, puc)) + if (cpu_signal_handler(host_signum, info, puc)) return; } @@ -333,7 +333,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info, host_to_target_siginfo_noswap(&tinfo, info); if (queue_signal(sig, &tinfo) == 1) { /* interrupt the virtual CPU as soon as possible */ - cpu_x86_interrupt(global_env); + cpu_interrupt(global_env); } } @@ -824,6 +824,33 @@ badframe: return 0; } +#else + +static void setup_frame(int sig, struct emulated_sigaction *ka, + target_sigset_t *set, CPUState *env) +{ + fprintf(stderr, "setup_frame: not implemented\n"); +} + +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + fprintf(stderr, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUState *env) +{ + fprintf(stderr, "do_sigreturn: not implemented\n"); + return -ENOSYS; +} + +long do_rt_sigreturn(CPUState *env) +{ + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); + return -ENOSYS; +} + #endif void process_pending_signals(void *cpu_env) |