diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-05-27 23:28:08 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-05-27 23:28:08 +0000 |
commit | f4beb510a41980e119f787746442ca1b87c06754 (patch) | |
tree | ad59b9c88a1870f1a86fd5837c266c9b96796bf4 /linux-user | |
parent | d731dae8e3c9591727073f6e54af08c1ee960540 (diff) | |
download | qemu-f4beb510a41980e119f787746442ca1b87c06754.tar.gz qemu-f4beb510a41980e119f787746442ca1b87c06754.tar.bz2 qemu-f4beb510a41980e119f787746442ca1b87c06754.zip |
precise exceptions - more accurate interrupt semantics
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@193 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/main.c | 108 |
1 files changed, 76 insertions, 32 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index a6a84e5363..eeea342d2b 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -100,24 +100,35 @@ int cpu_x86_inl(CPUX86State *env, int addr) return 0; } -void write_dt(void *ptr, unsigned long addr, unsigned long limit, - int seg32_bit) +static void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int flags) { - unsigned int e1, e2, limit_in_pages; - limit_in_pages = 0; - if (limit > 0xffff) { - limit = limit >> 12; - limit_in_pages = 1; - } + unsigned int e1, e2; e1 = (addr << 16) | (limit & 0xffff); e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); - e2 |= limit_in_pages << 23; /* byte granularity */ - e2 |= seg32_bit << 22; /* 32 bit segment */ + e2 |= flags; + stl((uint8_t *)ptr, e1); + stl((uint8_t *)ptr + 4, e2); +} + +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + unsigned long addr, unsigned int sel) +{ + unsigned int e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); stl((uint8_t *)ptr, e1); stl((uint8_t *)ptr + 4, e2); } uint64_t gdt_table[6]; +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); +} void cpu_loop(CPUX86State *env) { @@ -128,30 +139,34 @@ void cpu_loop(CPUX86State *env) for(;;) { trapnr = cpu_x86_exec(env); switch(trapnr) { + case 0x80: + /* linux syscall */ + env->regs[R_EAX] = do_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; + 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(info.si_signo, &info); + break; case EXCP0D_GPF: if (env->eflags & VM_MASK) { handle_vm86_fault(env); } else { - pc = env->seg_cache[R_CS].base + env->eip; - if (pc[0] == 0xcd && pc[1] == 0x80) { - /* syscall */ - env->eip += 2; - env->regs[R_EAX] = do_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]); - } else { - /* XXX: more precise info */ - info.si_signo = SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(info.si_signo, &info); - } + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(info.si_signo, &info); } break; case EXCP0E_PAGE: @@ -365,11 +380,40 @@ int main(int argc, char **argv) env->regs[R_ESP] = regs->esp; env->eip = regs->eip; + /* linux interrupt setup */ + env->idt.base = (void *)idt_table; + env->idt.limit = sizeof(idt_table) - 1; + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 3); + 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 */ env->gdt.base = (void *)gdt_table; env->gdt.limit = sizeof(gdt_table) - 1; - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1); - write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1); + 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)); + 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_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); |