From 31fc12df2c15a79e75a78288a552797a1be47f25 Mon Sep 17 00:00:00 2001 From: blueswir1 Date: Sat, 11 Apr 2009 11:09:31 +0000 Subject: BSD user: initial support for i386 and x86_64 targets git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7084 c046a42c-6fe2-441c-8c8c-71466251a162 --- bsd-user/i386/syscall.h | 147 +++++++++++++++ bsd-user/i386/target_signal.h | 20 +++ bsd-user/main.c | 389 +++++++++++++++++++++++++++++++++++++++- bsd-user/x86_64/syscall.h | 98 ++++++++++ bsd-user/x86_64/target_signal.h | 19 ++ 5 files changed, 671 insertions(+), 2 deletions(-) create mode 100644 bsd-user/i386/syscall.h create mode 100644 bsd-user/i386/target_signal.h create mode 100644 bsd-user/x86_64/syscall.h create mode 100644 bsd-user/x86_64/target_signal.h (limited to 'bsd-user') diff --git a/bsd-user/i386/syscall.h b/bsd-user/i386/syscall.h new file mode 100644 index 0000000000..cf53fba45e --- /dev/null +++ b/bsd-user/i386/syscall.h @@ -0,0 +1,147 @@ +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) + +struct target_pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}; + +/* ioctls */ + +#define TARGET_LDT_ENTRIES 8192 +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 9 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 6 +#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) + +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; + +/* vm86 defines */ + +#define TARGET_BIOSSEG 0x0f000 + +#define TARGET_CPU_086 0 +#define TARGET_CPU_186 1 +#define TARGET_CPU_286 2 +#define TARGET_CPU_386 3 +#define TARGET_CPU_486 4 +#define TARGET_CPU_586 5 + +#define TARGET_VM86_SIGNAL 0 /* return due to signal */ +#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */ +#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */ +#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */ + +/* + * Additional return values when invoking new vm86() + */ +#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */ +#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */ + +/* + * function codes when invoking new vm86() + */ +#define TARGET_VM86_PLUS_INSTALL_CHECK 0 +#define TARGET_VM86_ENTER 1 +#define TARGET_VM86_ENTER_NO_BYPASS 2 +#define TARGET_VM86_REQUEST_IRQ 3 +#define TARGET_VM86_FREE_IRQ 4 +#define TARGET_VM86_GET_IRQ_BITS 5 +#define TARGET_VM86_GET_AND_RESET_IRQ 6 + +/* + * This is the stack-layout seen by the user space program when we have + * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout + * is 'kernel_vm86_regs' (see below). + */ + +struct target_vm86_regs { +/* + * normal regs, with special meaning for the segment descriptors.. + */ + abi_long ebx; + abi_long ecx; + abi_long edx; + abi_long esi; + abi_long edi; + abi_long ebp; + abi_long eax; + abi_long __null_ds; + abi_long __null_es; + abi_long __null_fs; + abi_long __null_gs; + abi_long orig_eax; + abi_long eip; + unsigned short cs, __csh; + abi_long eflags; + abi_long esp; + unsigned short ss, __ssh; +/* + * these are specific to v86 mode: + */ + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned short fs, __fsh; + unsigned short gs, __gsh; +}; + +struct target_revectored_struct { + abi_ulong __map[8]; /* 256 bits */ +}; + +struct target_vm86_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; +}; + +/* + * flags masks + */ +#define TARGET_VM86_SCREEN_BITMAP 0x0001 + +struct target_vm86plus_info_struct { + abi_ulong flags; +#define TARGET_force_return_for_pic (1 << 0) +#define TARGET_vm86dbg_active (1 << 1) /* for debugger */ +#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */ +#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */ + unsigned char vm86dbg_intxxtab[32]; /* for debugger */ +}; + +struct target_vm86plus_struct { + struct target_vm86_regs regs; + abi_ulong flags; + abi_ulong screen_bitmap; + abi_ulong cpu_type; + struct target_revectored_struct int_revectored; + struct target_revectored_struct int21_revectored; + struct target_vm86plus_info_struct vm86plus; +}; + +#define UNAME_MACHINE "i386" + diff --git a/bsd-user/i386/target_signal.h b/bsd-user/i386/target_signal.h new file mode 100644 index 0000000000..2ef36d1f98 --- /dev/null +++ b/bsd-user/i386/target_signal.h @@ -0,0 +1,20 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/bsd-user/main.c b/bsd-user/main.c index 34a6b0762b..827c9c348d 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "qemu.h" #include "qemu-common.h" @@ -53,6 +55,46 @@ void gemu_log(const char *fmt, ...) va_end(ap); } +void cpu_outb(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val); +} + +void cpu_outw(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val); +} + +void cpu_outl(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val); +} + +int cpu_inb(CPUState *env, int addr) +{ + fprintf(stderr, "inb: port=0x%04x\n", addr); + return 0; +} + +int cpu_inw(CPUState *env, int addr) +{ + fprintf(stderr, "inw: port=0x%04x\n", addr); + return 0; +} + +int cpu_inl(CPUState *env, int addr) +{ + fprintf(stderr, "inl: port=0x%04x\n", addr); + return 0; +} + +#if defined(TARGET_I386) +int cpu_get_pic_interrupt(CPUState *env) +{ + return -1; +} +#endif + /* These are no-ops because we are not threadsafe. */ static inline void cpu_exec_start(CPUState *env) { @@ -89,6 +131,226 @@ void cpu_list_unlock(void) { } +#ifdef TARGET_I386 +/***********************************************************/ +/* CPUX86 core interface */ + +void cpu_smm_update(CPUState *env) +{ +} + +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpu_get_real_ticks(); +} + +static void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int flags) +{ + unsigned int e1, e2; + uint32_t *p; + e1 = (addr << 16) | (limit & 0xffff); + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); + e2 |= flags; + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +static uint64_t *idt_table; +#ifdef TARGET_X86_64 +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, + uint64_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); + p[2] = tswap32(addr >> 32); + p[3] = 0; +} +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); +} +#else +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + uint32_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate(idt_table + n, 0, dpl, 0, 0); +} +#endif + +void cpu_loop(CPUX86State *env, enum BSDType bsd_type) +{ + int trapnr; + abi_ulong pc; + //target_siginfo_t info; + + for(;;) { + trapnr = cpu_x86_exec(env); + switch(trapnr) { + case 0x80: + /* syscall from int $0x80 */ + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP]); + break; +#ifndef TARGET_ABI32 + case EXCP_SYSCALL: + /* linux syscall from syscall intruction */ + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9]); + env->eip = env->exception_next_eip; + break; +#endif +#if 0 + case EXCP0B_NOSEG: + case EXCP0C_STACK: + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + break; + case EXCP0D_GPF: + /* XXX: potential problem if ABI32 */ +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_fault(env); + } else +#endif + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP0E_PAGE: + info.si_signo = SIGSEGV; + info.si_errno = 0; + if (!(env->error_code & 1)) + info.si_code = TARGET_SEGV_MAPERR; + else + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[2]; + queue_signal(env, info.si_signo, &info); + break; + case EXCP00_DIVZ: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + /* division by zero */ + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP01_DB: + case EXCP03_INT3: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = SIGTRAP; + info.si_errno = 0; + if (trapnr == EXCP01_DB) { + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->eip; + } else { + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + } + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP04_INTO: + case EXCP05_BOUND: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP06_ILLOP: + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, &info); + break; +#endif + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; +#if 0 + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig (env, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, &info); + } + } + break; +#endif + default: + pc = env->segs[R_CS].base + env->eip; + fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); + abort(); + } + process_pending_signals(env); + } +} +#endif + #ifdef TARGET_SPARC #define SPARC64_STACK_BIAS 2047 @@ -526,7 +788,13 @@ int main(int argc, char **argv) init_paths(interp_prefix); if (cpu_model == NULL) { -#if defined(TARGET_SPARC) +#if defined(TARGET_I386) +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif +#elif defined(TARGET_SPARC) #ifdef TARGET_SPARC64 cpu_model = "TI UltraSparc II"; #else @@ -601,7 +869,124 @@ int main(int argc, char **argv) ts->info = info; env->opaque = ts; -#if defined(TARGET_SPARC) +#if defined(TARGET_I386) + cpu_x86_set_cpl(env, 3); + + env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; + env->hflags |= HF_PE_MASK; + if (env->cpuid_features & CPUID_SSE) { + env->cr[4] |= CR4_OSFXSR_MASK; + env->hflags |= HF_OSFXSR_MASK; + } +#ifndef TARGET_ABI32 + /* enable 64 bit mode if possible */ + if (!(env->cpuid_ext2_features & CPUID_EXT2_LM)) { + fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); + exit(1); + } + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA | MSR_EFER_LME; + env->hflags |= HF_LMA_MASK; +#endif + + /* flags setup : we activate the IRQs by default as in user mode */ + env->eflags |= IF_MASK; + + /* linux register setup */ +#ifndef TARGET_ABI32 + env->regs[R_EAX] = regs->rax; + env->regs[R_EBX] = regs->rbx; + env->regs[R_ECX] = regs->rcx; + env->regs[R_EDX] = regs->rdx; + env->regs[R_ESI] = regs->rsi; + env->regs[R_EDI] = regs->rdi; + env->regs[R_EBP] = regs->rbp; + env->regs[R_ESP] = regs->rsp; + env->eip = regs->rip; +#else + env->regs[R_EAX] = regs->eax; + env->regs[R_EBX] = regs->ebx; + env->regs[R_ECX] = regs->ecx; + env->regs[R_EDX] = regs->edx; + env->regs[R_ESI] = regs->esi; + env->regs[R_EDI] = regs->edi; + env->regs[R_EBP] = regs->ebp; + env->regs[R_ESP] = regs->esp; + env->eip = regs->eip; +#endif + + /* linux interrupt setup */ +#ifndef TARGET_ABI32 + env->idt.limit = 511; +#else + env->idt.limit = 255; +#endif + env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + idt_table = g2h(env->idt.base); + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 0); + set_idt(6, 0); + set_idt(7, 0); + set_idt(8, 0); + set_idt(9, 0); + set_idt(10, 0); + set_idt(11, 0); + set_idt(12, 0); + set_idt(13, 0); + set_idt(14, 0); + set_idt(15, 0); + set_idt(16, 0); + set_idt(17, 0); + set_idt(18, 0); + set_idt(19, 0); + set_idt(0x80, 3); + + /* linux segment setup */ + { + uint64_t *gdt_table; + env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; + gdt_table = g2h(env->gdt.base); +#ifdef TARGET_ABI32 + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#else + /* 64 bit code segment */ + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + DESC_L_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#endif + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); + } + + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); +#ifdef TARGET_ABI32 + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_FS, __USER_DS); + cpu_x86_load_seg(env, R_GS, __USER_DS); + /* This hack makes Wine work... */ + env->segs[R_FS].selector = 0; +#else + cpu_x86_load_seg(env, R_DS, 0); + cpu_x86_load_seg(env, R_ES, 0); + cpu_x86_load_seg(env, R_FS, 0); + cpu_x86_load_seg(env, R_GS, 0); +#endif +#elif defined(TARGET_SPARC) { int i; env->pc = regs->pc; diff --git a/bsd-user/x86_64/syscall.h b/bsd-user/x86_64/syscall.h new file mode 100644 index 0000000000..2a8d696bf2 --- /dev/null +++ b/bsd-user/x86_64/syscall.h @@ -0,0 +1,98 @@ +#define __USER_CS (0x33) +#define __USER_DS (0x2B) + +struct target_pt_regs { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong rbp; + abi_ulong rbx; +/* arguments: non interrupts/non tracing syscalls only save upto here*/ + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong rax; + abi_ulong rcx; + abi_ulong rdx; + abi_ulong rsi; + abi_ulong rdi; + abi_ulong orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + abi_ulong rip; + abi_ulong cs; + abi_ulong eflags; + abi_ulong rsp; + abi_ulong ss; +/* top of stack page */ +}; + +/* Maximum number of LDT entries supported. */ +#define TARGET_LDT_ENTRIES 8192 +/* The size of each LDT entry. */ +#define TARGET_LDT_ENTRY_SIZE 8 + +#define TARGET_GDT_ENTRIES 16 +#define TARGET_GDT_ENTRY_TLS_ENTRIES 3 +#define TARGET_GDT_ENTRY_TLS_MIN 12 +#define TARGET_GDT_ENTRY_TLS_MAX 14 + +#if 0 // Redefine this +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int lm:1; +}; +#else +struct target_modify_ldt_ldt_s { + unsigned int entry_number; + abi_ulong base_addr; + unsigned int limit; + unsigned int flags; +}; +#endif + +struct target_ipc64_perm +{ + int key; + uint32_t uid; + uint32_t gid; + uint32_t cuid; + uint32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_msqid64_ds { + struct target_ipc64_perm msg_perm; + unsigned int msg_stime; /* last msgsnd time */ + unsigned int msg_rtime; /* last msgrcv time */ + unsigned int msg_ctime; /* last change time */ + abi_ulong msg_cbytes; /* current number of bytes on queue */ + abi_ulong msg_qnum; /* number of messages in queue */ + abi_ulong msg_qbytes; /* max number of bytes on queue */ + unsigned int msg_lspid; /* pid of last msgsnd */ + unsigned int msg_lrpid; /* last receive pid */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#define UNAME_MACHINE "x86_64" + +#define TARGET_ARCH_SET_GS 0x1001 +#define TARGET_ARCH_SET_FS 0x1002 +#define TARGET_ARCH_GET_FS 0x1003 +#define TARGET_ARCH_GET_GS 0x1004 diff --git a/bsd-user/x86_64/target_signal.h b/bsd-user/x86_64/target_signal.h new file mode 100644 index 0000000000..659cd401b8 --- /dev/null +++ b/bsd-user/x86_64/target_signal.h @@ -0,0 +1,19 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_long ss_flags; + abi_ulong ss_size; +} target_stack_t; + +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} + +#endif /* TARGET_SIGNAL_H */ -- cgit v1.2.3