x86_64 linux user emulation
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3646 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
6b23f77722
commit
d2fd1af767
@ -147,8 +147,31 @@ static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
p[1] = tswapl(e2);
|
||||
}
|
||||
|
||||
#if TARGET_X86_64
|
||||
uint64_t idt_table[512];
|
||||
|
||||
static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
|
||||
uint64_t addr, unsigned int sel)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
uint32_t *p;
|
||||
e1 = (addr & 0xffff) | (sel << 16);
|
||||
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
|
||||
p = ptr;
|
||||
p[0] = tswapl(e1);
|
||||
p[1] = tswapl(e2);
|
||||
p[2] = addr >> 32;
|
||||
}
|
||||
/* 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
|
||||
uint64_t idt_table[256];
|
||||
|
||||
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
||||
unsigned long addr, unsigned int sel)
|
||||
uint32_t addr, unsigned int sel)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
uint32_t *p;
|
||||
@ -159,13 +182,12 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
||||
p[1] = tswapl(e2);
|
||||
}
|
||||
|
||||
uint64_t idt_table[256];
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@ -177,7 +199,7 @@ void cpu_loop(CPUX86State *env)
|
||||
trapnr = cpu_x86_exec(env);
|
||||
switch(trapnr) {
|
||||
case 0x80:
|
||||
/* linux syscall */
|
||||
/* linux syscall from int $0x80 */
|
||||
env->regs[R_EAX] = do_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EBX],
|
||||
@ -187,6 +209,20 @@ void cpu_loop(CPUX86State *env)
|
||||
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_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
|
||||
case EXCP0B_NOSEG:
|
||||
case EXCP0C_STACK:
|
||||
info.si_signo = SIGBUS;
|
||||
@ -196,6 +232,7 @@ void cpu_loop(CPUX86State *env)
|
||||
queue_signal(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);
|
||||
@ -2075,12 +2112,18 @@ int main(int argc, char **argv)
|
||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||
env->hflags |= HF_OSFXSR_MASK;
|
||||
}
|
||||
#ifndef TARGET_ABI32
|
||||
/* enable 64 bit mode */
|
||||
env->cr[4] |= CR4_PAE_MASK;
|
||||
env->efer |= MSR_EFER_LMA;
|
||||
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 */
|
||||
#if defined(TARGET_X86_64)
|
||||
#ifndef TARGET_ABI32
|
||||
env->regs[R_EAX] = regs->rax;
|
||||
env->regs[R_EBX] = regs->rbx;
|
||||
env->regs[R_ECX] = regs->rcx;
|
||||
@ -2131,24 +2174,38 @@ int main(int argc, char **argv)
|
||||
{
|
||||
uint64_t *gdt_table;
|
||||
gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
|
||||
env->gdt.base = h2g(gdt_table);
|
||||
env->gdt.base = h2g((unsigned long)gdt_table);
|
||||
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
|
||||
#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_SS, __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_ARM)
|
||||
{
|
||||
int i;
|
||||
|
@ -2521,6 +2521,41 @@ abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef TARGET_ABI32
|
||||
abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
|
||||
{
|
||||
abi_long ret;
|
||||
abi_ulong val;
|
||||
int idx;
|
||||
|
||||
switch(code) {
|
||||
case TARGET_ARCH_SET_GS:
|
||||
case TARGET_ARCH_SET_FS:
|
||||
if (code == TARGET_ARCH_SET_GS)
|
||||
idx = R_GS;
|
||||
else
|
||||
idx = R_FS;
|
||||
cpu_x86_load_seg(env, idx, 0);
|
||||
env->segs[idx].base = addr;
|
||||
break;
|
||||
case TARGET_ARCH_GET_GS:
|
||||
case TARGET_ARCH_GET_FS:
|
||||
if (code == TARGET_ARCH_GET_GS)
|
||||
idx = R_GS;
|
||||
else
|
||||
idx = R_FS;
|
||||
val = env->segs[idx].base;
|
||||
if (put_user(val, addr, abi_ulong))
|
||||
return -TARGET_EFAULT;
|
||||
break;
|
||||
default:
|
||||
ret = -TARGET_EINVAL;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined(TARGET_I386) */
|
||||
|
||||
/* this stack is the equivalent of the kernel stack associated with a
|
||||
@ -2797,7 +2832,7 @@ void syscall_init(void)
|
||||
target_to_host_errno_table[host_to_target_errno_table[i]] = i;
|
||||
|
||||
/* automatic consistency check if same arch */
|
||||
#if defined(__i386__) && defined(TARGET_I386)
|
||||
#if defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32)
|
||||
if (ie->target_cmd != ie->host_cmd) {
|
||||
fprintf(stderr, "ERROR: ioctl: target=0x%x host=0x%x\n",
|
||||
ie->target_cmd, ie->host_cmd);
|
||||
@ -3861,7 +3896,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
#endif
|
||||
#ifdef TARGET_NR_mmap
|
||||
case TARGET_NR_mmap:
|
||||
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS)
|
||||
#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS)
|
||||
{
|
||||
abi_ulong *v;
|
||||
abi_ulong v1, v2, v3, v4, v5, v6;
|
||||
@ -4183,42 +4218,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
|
||||
if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0))
|
||||
goto efault;
|
||||
#if defined(TARGET_MIPS) || (defined(TARGET_SPARC64) && !defined(TARGET_ABI32))
|
||||
target_st->st_dev = tswap32(st.st_dev);
|
||||
#else
|
||||
target_st->st_dev = tswap16(st.st_dev);
|
||||
#endif
|
||||
target_st->st_ino = tswapl(st.st_ino);
|
||||
#if defined(TARGET_PPC) || defined(TARGET_MIPS)
|
||||
target_st->st_mode = tswapl(st.st_mode); /* XXX: check this */
|
||||
target_st->st_uid = tswap32(st.st_uid);
|
||||
target_st->st_gid = tswap32(st.st_gid);
|
||||
#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
|
||||
target_st->st_mode = tswap32(st.st_mode);
|
||||
target_st->st_uid = tswap32(st.st_uid);
|
||||
target_st->st_gid = tswap32(st.st_gid);
|
||||
#else
|
||||
target_st->st_mode = tswap16(st.st_mode);
|
||||
target_st->st_uid = tswap16(st.st_uid);
|
||||
target_st->st_gid = tswap16(st.st_gid);
|
||||
#endif
|
||||
#if defined(TARGET_MIPS)
|
||||
/* If this is the same on PPC, then just merge w/ the above ifdef */
|
||||
target_st->st_nlink = tswapl(st.st_nlink);
|
||||
target_st->st_rdev = tswapl(st.st_rdev);
|
||||
#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
|
||||
target_st->st_nlink = tswap32(st.st_nlink);
|
||||
target_st->st_rdev = tswap32(st.st_rdev);
|
||||
#else
|
||||
target_st->st_nlink = tswap16(st.st_nlink);
|
||||
target_st->st_rdev = tswap16(st.st_rdev);
|
||||
#endif
|
||||
target_st->st_size = tswapl(st.st_size);
|
||||
target_st->st_blksize = tswapl(st.st_blksize);
|
||||
target_st->st_blocks = tswapl(st.st_blocks);
|
||||
target_st->target_st_atime = tswapl(st.st_atime);
|
||||
target_st->target_st_mtime = tswapl(st.st_mtime);
|
||||
target_st->target_st_ctime = tswapl(st.st_ctime);
|
||||
__put_user(st.st_dev, &target_st->st_dev);
|
||||
__put_user(st.st_ino, &target_st->st_ino);
|
||||
__put_user(st.st_mode, &target_st->st_mode);
|
||||
__put_user(st.st_uid, &target_st->st_uid);
|
||||
__put_user(st.st_gid, &target_st->st_gid);
|
||||
__put_user(st.st_nlink, &target_st->st_nlink);
|
||||
__put_user(st.st_rdev, &target_st->st_rdev);
|
||||
__put_user(st.st_size, &target_st->st_size);
|
||||
__put_user(st.st_blksize, &target_st->st_blksize);
|
||||
__put_user(st.st_blocks, &target_st->st_blocks);
|
||||
__put_user(st.st_atime, &target_st->target_st_atime);
|
||||
__put_user(st.st_mtime, &target_st->target_st_mtime);
|
||||
__put_user(st.st_ctime, &target_st->target_st_ctime);
|
||||
unlock_user_struct(target_st, arg2, 1);
|
||||
}
|
||||
}
|
||||
@ -4671,6 +4683,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#ifdef TARGET_NR_arch_prctl
|
||||
case TARGET_NR_arch_prctl:
|
||||
#if defined(TARGET_I386) && !defined(TARGET_ABI32)
|
||||
ret = do_arch_prctl(cpu_env, arg1, arg2);
|
||||
break;
|
||||
#else
|
||||
goto unimplemented;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef TARGET_NR_pread
|
||||
case TARGET_NR_pread:
|
||||
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
|
||||
|
@ -881,7 +881,7 @@ struct target_winsize {
|
||||
#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_CRIS)
|
||||
#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS)
|
||||
struct target_stat {
|
||||
unsigned short st_dev;
|
||||
unsigned short __pad1;
|
||||
@ -1474,6 +1474,30 @@ struct target_stat64 {
|
||||
unsigned long long st_ino;
|
||||
};
|
||||
|
||||
#elif defined(TARGET_I386) && !defined(TARGET_ABI32)
|
||||
struct target_stat {
|
||||
abi_ulong st_dev;
|
||||
abi_ulong st_ino;
|
||||
abi_ulong st_nlink;
|
||||
|
||||
unsigned int st_mode;
|
||||
unsigned int st_uid;
|
||||
unsigned int st_gid;
|
||||
unsigned int __pad0;
|
||||
abi_ulong st_rdev;
|
||||
abi_long st_size;
|
||||
abi_long st_blksize;
|
||||
abi_long st_blocks; /* Number 512-byte blocks allocated. */
|
||||
|
||||
abi_ulong target_st_atime;
|
||||
abi_ulong target_st_atime_nsec;
|
||||
abi_ulong target_st_mtime;
|
||||
abi_ulong target_st_mtime_nsec;
|
||||
abi_ulong target_st_ctime;
|
||||
abi_ulong target_st_ctime_nsec;
|
||||
|
||||
abi_long __unused[3];
|
||||
};
|
||||
#else
|
||||
#error unsupported CPU
|
||||
#endif
|
||||
|
@ -91,3 +91,8 @@ struct target_msqid64_ds {
|
||||
};
|
||||
|
||||
#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
|
||||
|
@ -340,6 +340,9 @@
|
||||
#define EXCP11_ALGN 17
|
||||
#define EXCP12_MCHK 18
|
||||
|
||||
#define EXCP_SYSCALL 0x100 /* only happens in user only emulation
|
||||
for syscall instruction */
|
||||
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
|
||||
CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */
|
||||
|
@ -971,6 +971,14 @@ static void do_interrupt64(int intno, int is_int, int error_code,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
void helper_syscall(int next_eip_addend)
|
||||
{
|
||||
env->exception_index = EXCP_SYSCALL;
|
||||
env->exception_next_eip = env->eip + next_eip_addend;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#else
|
||||
void helper_syscall(int next_eip_addend)
|
||||
{
|
||||
int selector;
|
||||
@ -1024,6 +1032,7 @@ void helper_syscall(int next_eip_addend)
|
||||
env->eip = (uint32_t)env->star;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void helper_sysret(int dflag)
|
||||
{
|
||||
@ -1143,18 +1152,23 @@ void do_interrupt_user(int intno, int is_int, int error_code,
|
||||
{
|
||||
SegmentCache *dt;
|
||||
target_ulong ptr;
|
||||
int dpl, cpl;
|
||||
int dpl, cpl, shift;
|
||||
uint32_t e2;
|
||||
|
||||
dt = &env->idt;
|
||||
ptr = dt->base + (intno * 8);
|
||||
if (env->hflags & HF_LMA_MASK) {
|
||||
shift = 4;
|
||||
} else {
|
||||
shift = 3;
|
||||
}
|
||||
ptr = dt->base + (intno << shift);
|
||||
e2 = ldl_kernel(ptr + 4);
|
||||
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
cpl = env->hflags & HF_CPL_MASK;
|
||||
/* check privledge if software int */
|
||||
if (is_int && dpl < cpl)
|
||||
raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
|
||||
raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
|
||||
|
||||
/* Since we emulate only user space, we cannot do more than
|
||||
exiting the emulation with the suitable exception and error
|
||||
|
Loading…
Reference in New Issue
Block a user