| /* |
| * emu main |
| * |
| * Copyright (c) 2003 Fabrice Bellard |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <elf.h> |
| #include <endian.h> |
| #include <errno.h> |
| |
| #include "gemu.h" |
| |
| #include "i386/hsw_interp.h" |
| |
| unsigned long x86_stack_size; |
| unsigned long stktop; |
| |
| void gemu_log(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| } |
| |
| /* virtual x86 CPU stuff */ |
| |
| extern int invoke_code16(Interp_ENV *, int, int); |
| extern int invoke_code32(Interp_ENV *, int); |
| extern char *e_print_cpuemu_regs(ENVPARAMS, int is32); |
| extern char *e_emu_disasm(ENVPARAMS, unsigned char *org, int is32); |
| extern void init_npu(void); |
| |
| Interp_ENV env_global; |
| Interp_ENV *envp_global; |
| |
| QWORD EMUtime = 0; |
| |
| int CEmuStat = 0; |
| |
| long instr_count; |
| |
| /* who will initialize this? */ |
| unsigned long io_bitmap[IO_BITMAP_SIZE+1]; |
| |
| /* debug flag, 0=disable 1..9=level */ |
| int d_emu = 0; |
| |
| unsigned long CRs[5] = |
| { |
| 0x00000013, /* valid bits: 0xe005003f */ |
| 0x00000000, /* invalid */ |
| 0x00000000, |
| 0x00000000, |
| 0x00000000 |
| }; |
| |
| /* |
| * DR0-3 = linear address of breakpoint 0-3 |
| * DR4=5 = reserved |
| * DR6 b0-b3 = BP active |
| * b13 = BD |
| * b14 = BS |
| * b15 = BT |
| * DR7 b0-b1 = G:L bp#0 |
| * b2-b3 = G:L bp#1 |
| * b4-b5 = G:L bp#2 |
| * b6-b7 = G:L bp#3 |
| * b8-b9 = GE:LE |
| * b13 = GD |
| * b16-19= LLRW bp#0 LL=00(1),01(2),11(4) |
| * b20-23= LLRW bp#1 RW=00(x),01(w),11(rw) |
| * b24-27= LLRW bp#2 |
| * b28-31= LLRW bp#3 |
| */ |
| unsigned long DRs[8] = |
| { |
| 0x00000000, |
| 0x00000000, |
| 0x00000000, |
| 0x00000000, |
| 0xffff1ff0, |
| 0x00000400, |
| 0xffff1ff0, |
| 0x00000400 |
| }; |
| |
| unsigned long TRs[2] = |
| { |
| 0x00000000, |
| 0x00000000 |
| }; |
| |
| void FatalAppExit(UINT wAction, LPCSTR lpText) |
| { |
| fprintf(stderr, "Fatal error '%s' in CPU\n", lpText); |
| exit(1); |
| } |
| |
| int e_debug_check(unsigned char *PC) |
| { |
| register unsigned long d7 = DRs[7]; |
| |
| if (d7&0x03) { |
| if (d7&0x30000) return 0; /* only execute(00) bkp */ |
| if ((long)PC==DRs[0]) { |
| e_printf("DBRK: DR0 hit at %p\n",PC); |
| DRs[6] |= 1; |
| return 1; |
| } |
| } |
| if (d7&0x0c) { |
| if (d7&0x300000) return 0; |
| if ((long)PC==DRs[1]) { |
| e_printf("DBRK: DR1 hit at %p\n",PC); |
| DRs[6] |= 2; |
| return 1; |
| } |
| } |
| if (d7&0x30) { |
| if (d7&0x3000000) return 0; |
| if ((long)PC==DRs[2]) { |
| e_printf("DBRK: DR2 hit at %p\n",PC); |
| DRs[6] |= 4; |
| return 1; |
| } |
| } |
| if (d7&0xc0) { |
| if (d7&0x30000000) return 0; |
| if ((long)PC==DRs[3]) { |
| e_printf("DBRK: DR3 hit at %p\n",PC); |
| DRs[6] |= 8; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /* Debug stuff */ |
| void logstr(unsigned long mask, const char *fmt,...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| } |
| |
| /* unconditional message into debug log and stderr */ |
| #undef error |
| void error(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| exit(1); |
| } |
| |
| int PortIO(DWORD port, DWORD value, UINT size, BOOL is_write) |
| { |
| fprintf(stderr, "IO: %s port=0x%lx value=0x%lx size=%d", |
| is_write ? "write" : "read", port, value, size); |
| return value; |
| } |
| |
| void LogProcName(WORD wSel, WORD wOff, WORD wAction) |
| { |
| |
| } |
| |
| void INT_handler(int num, void *env) |
| { |
| fprintf(stderr, "EM86: int %d\n", num); |
| } |
| |
| /***********************************************************/ |
| |
| /* XXX: currently we use LDT entries */ |
| #define __USER_CS (0x23|4) |
| #define __USER_DS (0x2B|4) |
| |
| void usage(void) |
| { |
| printf("gemu version 0.1, Copyright (c) 2003 Fabrice Bellard\n" |
| "usage: gemu program [arguments...]\n" |
| "Linux x86 emulator\n" |
| ); |
| exit(1); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| const char *filename; |
| struct pt_regs regs1, *regs = ®s1; |
| struct image_info info1, *info = &info1; |
| Interp_ENV *env; |
| |
| if (argc <= 1) |
| usage(); |
| |
| filename = argv[1]; |
| |
| /* Zero out regs */ |
| memset(regs, 0, sizeof(struct pt_regs)); |
| |
| /* Zero out image_info */ |
| memset(info, 0, sizeof(struct image_info)); |
| |
| if(elf_exec(filename, argv+1, __environ, regs, info) != 0) { |
| printf("Error loading %s\n", filename); |
| exit(1); |
| } |
| |
| #if 0 |
| printf("start_brk 0x%08lx\n" , info->start_brk); |
| printf("end_code 0x%08lx\n" , info->end_code); |
| printf("start_code 0x%08lx\n" , info->start_code); |
| printf("end_data 0x%08lx\n" , info->end_data); |
| printf("start_stack 0x%08lx\n" , info->start_stack); |
| printf("brk 0x%08lx\n" , info->brk); |
| printf("esp 0x%08lx\n" , regs->esp); |
| printf("eip 0x%08lx\n" , regs->eip); |
| #endif |
| |
| target_set_brk((char *)info->brk); |
| syscall_init(); |
| |
| env = &env_global; |
| envp_global = env; |
| memset(env, 0, sizeof(Interp_ENV)); |
| |
| env->rax.e = regs->eax; |
| env->rbx.e = regs->ebx; |
| env->rcx.e = regs->ecx; |
| env->rdx.e = regs->edx; |
| env->rsi.esi = regs->esi; |
| env->rdi.edi = regs->edi; |
| env->rbp.ebp = regs->ebp; |
| env->rsp.esp = regs->esp; |
| env->cs.cs = __USER_CS; |
| env->ds.ds = __USER_DS; |
| env->es.es = __USER_DS; |
| env->ss.ss = __USER_DS; |
| env->fs.fs = __USER_DS; |
| env->gs.gs = __USER_DS; |
| env->trans_addr = regs->eip; |
| |
| LDT[__USER_CS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; |
| LDT[__USER_CS >> 3].dwSelLimit = 0xfffff; |
| LDT[__USER_CS >> 3].lpSelBase = NULL; |
| |
| LDT[__USER_DS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; |
| LDT[__USER_DS >> 3].dwSelLimit = 0xfffff; |
| LDT[__USER_DS >> 3].lpSelBase = NULL; |
| init_npu(); |
| |
| for(;;) { |
| int err; |
| uint8_t *pc; |
| |
| err = invoke_code32(env, -1); |
| env->trans_addr = env->return_addr; |
| pc = env->seg_regs[0] + env->trans_addr; |
| switch(err) { |
| case EXCP0D_GPF: |
| if (pc[0] == 0xcd && pc[1] == 0x80) { |
| /* syscall */ |
| env->trans_addr += 2; |
| env->rax.e = do_syscall(env->rax.e, |
| env->rbx.e, |
| env->rcx.e, |
| env->rdx.e, |
| env->rsi.esi, |
| env->rdi.edi, |
| env->rbp.ebp); |
| } else { |
| goto trap_error; |
| } |
| break; |
| default: |
| trap_error: |
| fprintf(stderr, "GEMU: Unknown error %d, aborting\n", err); |
| d_emu = 9; |
| fprintf(stderr, "%s\n%s\n", |
| e_print_cpuemu_regs(env, 1), |
| e_emu_disasm(env,pc,1)); |
| abort(); |
| } |
| } |
| return 0; |
| } |