aboutsummaryrefslogtreecommitdiff
path: root/linux-user/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/main.c')
-rw-r--r--linux-user/main.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
new file mode 100644
index 0000000000..e3950835e3
--- /dev/null
+++ b/linux-user/main.c
@@ -0,0 +1,310 @@
+/*
+ * 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 = &regs1;
+ 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;
+}