diff -Nur linux.old/arch/i386/kernel/traps.c linux.new/arch/i386/kernel/traps.c --- linux.old/arch/i386/kernel/traps.c Sun Aug 11 09:41:56 2002 +++ linux.new/arch/i386/kernel/traps.c Sun Aug 11 09:42:26 2002 @@ -309,8 +309,12 @@ static void inline do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs * regs, long error_code, siginfo_t *info) { - if (vm86 && regs->eflags & VM_MASK) - goto vm86_trap; + if (regs->eflags & VM_MASK) { + if (vm86) + goto vm86_trap; + else + goto trap_signal; + } #ifdef CONFIG_PNPBIOS if (regs->xcs == 0x60 || regs->xcs == 0x68) diff -Nur linux.old/arch/i386/kernel/vm86.c linux.new/arch/i386/kernel/vm86.c --- linux.old/arch/i386/kernel/vm86.c Sun Aug 11 09:41:51 2002 +++ linux.new/arch/i386/kernel/vm86.c Sun Aug 11 09:42:14 2002 @@ -98,21 +98,22 @@ pte_t *pte, *mapped; int i; + spin_lock(&tsk->mm->page_table_lock); pgd = pgd_offset(tsk->mm, 0xA0000); if (pgd_none(*pgd)) - return; + goto out; if (pgd_bad(*pgd)) { pgd_ERROR(*pgd); pgd_clear(pgd); - return; + goto out; } pmd = pmd_offset(pgd, 0xA0000); if (pmd_none(*pmd)) - return; + goto out; if (pmd_bad(*pmd)) { pmd_ERROR(*pmd); pmd_clear(pmd); - return; + goto out; } preempt_disable(); pte = mapped = pte_offset_map(pmd, 0xA0000); @@ -123,6 +124,8 @@ } pte_unmap(mapped); preempt_enable(); +out: + spin_unlock(&tsk->mm->page_table_lock); flush_tlb(); } @@ -294,12 +297,19 @@ regs->eflags &= ~TF_MASK; } +static inline void clear_AC(struct kernel_vm86_regs * regs) +{ + regs->eflags &= ~AC_MASK; +} + static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs) { set_flags(VEFLAGS, eflags, current->thread.v86mask); set_flags(regs->eflags, eflags, SAFE_MASK); if (eflags & IF_MASK) set_IF(regs); + else + clear_IF(regs); } static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs) @@ -308,6 +318,8 @@ set_flags(regs->eflags, flags, SAFE_MASK); if (flags & IF_MASK) set_IF(regs); + else + clear_IF(regs); } static inline unsigned long get_vflags(struct kernel_vm86_regs * regs) @@ -327,80 +339,90 @@ return nr; } -/* - * Boy are these ugly, but we need to do the correct 16-bit arithmetic. - * Gcc makes a mess of it, so we do it inline and use non-obvious calling - * conventions.. - */ -#define pushb(base, ptr, val) \ -__asm__ __volatile__( \ - "decw %w0\n\t" \ - "movb %2,0(%1,%0)" \ - : "=r" (ptr) \ - : "r" (base), "q" (val), "0" (ptr)) - -#define pushw(base, ptr, val) \ -__asm__ __volatile__( \ - "decw %w0\n\t" \ - "movb %h2,0(%1,%0)\n\t" \ - "decw %w0\n\t" \ - "movb %b2,0(%1,%0)" \ - : "=r" (ptr) \ - : "r" (base), "q" (val), "0" (ptr)) - -#define pushl(base, ptr, val) \ -__asm__ __volatile__( \ - "decw %w0\n\t" \ - "rorl $16,%2\n\t" \ - "movb %h2,0(%1,%0)\n\t" \ - "decw %w0\n\t" \ - "movb %b2,0(%1,%0)\n\t" \ - "decw %w0\n\t" \ - "rorl $16,%2\n\t" \ - "movb %h2,0(%1,%0)\n\t" \ - "decw %w0\n\t" \ - "movb %b2,0(%1,%0)" \ - : "=r" (ptr) \ - : "r" (base), "q" (val), "0" (ptr)) - -#define popb(base, ptr) \ -({ unsigned long __res; \ -__asm__ __volatile__( \ - "movb 0(%1,%0),%b2\n\t" \ - "incw %w0" \ - : "=r" (ptr), "=r" (base), "=q" (__res) \ - : "0" (ptr), "1" (base), "2" (0)); \ -__res; }) - -#define popw(base, ptr) \ -({ unsigned long __res; \ -__asm__ __volatile__( \ - "movb 0(%1,%0),%b2\n\t" \ - "incw %w0\n\t" \ - "movb 0(%1,%0),%h2\n\t" \ - "incw %w0" \ - : "=r" (ptr), "=r" (base), "=q" (__res) \ - : "0" (ptr), "1" (base), "2" (0)); \ -__res; }) - -#define popl(base, ptr) \ -({ unsigned long __res; \ -__asm__ __volatile__( \ - "movb 0(%1,%0),%b2\n\t" \ - "incw %w0\n\t" \ - "movb 0(%1,%0),%h2\n\t" \ - "incw %w0\n\t" \ - "rorl $16,%2\n\t" \ - "movb 0(%1,%0),%b2\n\t" \ - "incw %w0\n\t" \ - "movb 0(%1,%0),%h2\n\t" \ - "incw %w0\n\t" \ - "rorl $16,%2" \ - : "=r" (ptr), "=r" (base), "=q" (__res) \ - : "0" (ptr), "1" (base)); \ -__res; }) +#define val_byte(val, n) (((__u8 *)&val)[n]) -static void do_int(struct kernel_vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp) +#define pushb(base, ptr, val, err_label) \ + do { \ + __u8 __val = val; \ + ptr--; \ + if (put_user(__val, base + ptr) < 0) \ + goto err_label; \ + } while(0) + +#define pushw(base, ptr, val, err_label) \ + do { \ + __u16 __val = val; \ + ptr--; \ + if (put_user(val_byte(__val, 1), base + ptr) < 0) \ + goto err_label; \ + ptr--; \ + if (put_user(val_byte(__val, 0), base + ptr) < 0) \ + goto err_label; \ + } while(0) + +#define pushl(base, ptr, val, err_label) \ + do { \ + __u32 __val = val; \ + ptr--; \ + if (put_user(val_byte(__val, 3), base + ptr) < 0) \ + goto err_label; \ + ptr--; \ + if (put_user(val_byte(__val, 2), base + ptr) < 0) \ + goto err_label; \ + ptr--; \ + if (put_user(val_byte(__val, 1), base + ptr) < 0) \ + goto err_label; \ + ptr--; \ + if (put_user(val_byte(__val, 0), base + ptr) < 0) \ + goto err_label; \ + } while(0) + +#define popb(base, ptr, err_label) \ + ({ \ + __u8 __res; \ + if (get_user(__res, base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + __res; \ + }) + +#define popw(base, ptr, err_label) \ + ({ \ + __u16 __res; \ + if (get_user(val_byte(__res, 0), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + if (get_user(val_byte(__res, 1), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + __res; \ + }) + +#define popl(base, ptr, err_label) \ + ({ \ + __u32 __res; \ + if (get_user(val_byte(__res, 0), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + if (get_user(val_byte(__res, 1), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + if (get_user(val_byte(__res, 2), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + if (get_user(val_byte(__res, 3), base + ptr) < 0) \ + goto err_label; \ + ptr++; \ + __res; \ + }) + +/* There are so many possible reasons for this function to return + * VM86_INTx, so adding another doesn't bother me. We can expect + * userspace programs to be able to handle it. (Getting a problem + * in userspace is always better than an Oops anyway.) [KD] + */ +static void do_int(struct kernel_vm86_regs *regs, int i, + unsigned char * ssp, unsigned short sp) { unsigned long *intr_ptr, segoffs; @@ -415,14 +437,15 @@ goto cannot_handle; if ((segoffs >> 16) == BIOSSEG) goto cannot_handle; - pushw(ssp, sp, get_vflags(regs)); - pushw(ssp, sp, regs->cs); - pushw(ssp, sp, IP(regs)); + pushw(ssp, sp, get_vflags(regs), cannot_handle); + pushw(ssp, sp, regs->cs, cannot_handle); + pushw(ssp, sp, IP(regs), cannot_handle); regs->cs = segoffs >> 16; SP(regs) -= 6; IP(regs) = segoffs & 0xffff; clear_TF(regs); clear_IF(regs); + clear_AC(regs); return; cannot_handle: @@ -454,75 +477,80 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) { - unsigned char *csp, *ssp; - unsigned long ip, sp; + unsigned char *csp, *ssp, opcode; + unsigned short ip, sp; + int data32, pref_done; #define CHECK_IF_IN_TRAP \ if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \ - pushw(ssp,sp,popw(ssp,sp) | TF_MASK); -#define VM86_FAULT_RETURN \ + newflags |= TF_MASK +#define VM86_FAULT_RETURN do { \ if (VMPI.force_return_for_pic && (VEFLAGS & (IF_MASK | VIF_MASK))) \ return_to_32bit(regs, VM86_PICRETURN); \ - return; + return; } while (0) csp = (unsigned char *) (regs->cs << 4); ssp = (unsigned char *) (regs->ss << 4); sp = SP(regs); ip = IP(regs); - switch (popb(csp, ip)) { - - /* operand size override */ - case 0x66: - switch (popb(csp, ip)) { - - /* pushfd */ - case 0x9c: - SP(regs) -= 4; - IP(regs) += 2; - pushl(ssp, sp, get_vflags(regs)); - VM86_FAULT_RETURN; - - /* popfd */ - case 0x9d: - SP(regs) += 4; - IP(regs) += 2; - CHECK_IF_IN_TRAP - set_vflags_long(popl(ssp, sp), regs); - VM86_FAULT_RETURN; - - /* iretd */ - case 0xcf: - SP(regs) += 12; - IP(regs) = (unsigned short)popl(ssp, sp); - regs->cs = (unsigned short)popl(ssp, sp); - CHECK_IF_IN_TRAP - set_vflags_long(popl(ssp, sp), regs); - VM86_FAULT_RETURN; - /* need this to avoid a fallthrough */ - default: - return_to_32bit(regs, VM86_UNKNOWN); + data32 = 0; + pref_done = 0; + do { + switch (opcode = popb(csp, ip, simulate_sigsegv)) { + case 0x66: /* 32-bit data */ data32=1; break; + case 0x67: /* 32-bit address */ break; + case 0x2e: /* CS */ break; + case 0x3e: /* DS */ break; + case 0x26: /* ES */ break; + case 0x36: /* SS */ break; + case 0x65: /* GS */ break; + case 0x64: /* FS */ break; + case 0xf2: /* repnz */ break; + case 0xf3: /* rep */ break; + default: pref_done = 1; } + } while (!pref_done); + + switch (opcode) { /* pushf */ case 0x9c: - SP(regs) -= 2; - IP(regs)++; - pushw(ssp, sp, get_vflags(regs)); + if (data32) { + pushl(ssp, sp, get_vflags(regs), simulate_sigsegv); + SP(regs) -= 4; + } else { + pushw(ssp, sp, get_vflags(regs), simulate_sigsegv); + SP(regs) -= 2; + } + IP(regs) = ip; VM86_FAULT_RETURN; /* popf */ case 0x9d: - SP(regs) += 2; - IP(regs)++; - CHECK_IF_IN_TRAP - set_vflags_short(popw(ssp, sp), regs); + { + unsigned long newflags; + if (data32) { + newflags=popl(ssp, sp, simulate_sigsegv); + SP(regs) += 4; + } else { + newflags = popw(ssp, sp, simulate_sigsegv); + SP(regs) += 2; + } + IP(regs) = ip; + CHECK_IF_IN_TRAP; + if (data32) { + set_vflags_long(newflags, regs); + } else { + set_vflags_short(newflags, regs); + } VM86_FAULT_RETURN; + } /* int xx */ case 0xcd: { - int intno=popb(csp, ip); - IP(regs) += 2; + int intno=popb(csp, ip, simulate_sigsegv); + IP(regs) = ip; if (VMPI.vm86dbg_active) { if ( (1 << (intno &7)) & VMPI.vm86dbg_intxxtab[intno >> 3] ) return_to_32bit(regs, VM86_INTx + (intno << 8)); @@ -533,16 +561,35 @@ /* iret */ case 0xcf: - SP(regs) += 6; - IP(regs) = popw(ssp, sp); - regs->cs = popw(ssp, sp); - CHECK_IF_IN_TRAP - set_vflags_short(popw(ssp, sp), regs); + { + unsigned long newip; + unsigned long newcs; + unsigned long newflags; + if (data32) { + newip=popl(ssp, sp, simulate_sigsegv); + newcs=popl(ssp, sp, simulate_sigsegv); + newflags=popl(ssp, sp, simulate_sigsegv); + SP(regs) += 12; + } else { + newip = popw(ssp, sp, simulate_sigsegv); + newcs = popw(ssp, sp, simulate_sigsegv); + newflags = popw(ssp, sp, simulate_sigsegv); + SP(regs) += 6; + } + IP(regs) = newip; + regs->cs = newcs; + CHECK_IF_IN_TRAP; + if (data32) { + set_vflags_long(newflags, regs); + } else { + set_vflags_short(newflags, regs); + } VM86_FAULT_RETURN; + } /* cli */ case 0xfa: - IP(regs)++; + IP(regs) = ip; clear_IF(regs); VM86_FAULT_RETURN; @@ -554,13 +601,28 @@ * Probably needs some horsing around with the TF flag. Aiee.. */ case 0xfb: - IP(regs)++; + IP(regs) = ip; set_IF(regs); VM86_FAULT_RETURN; default: return_to_32bit(regs, VM86_UNKNOWN); } + + return; + +simulate_sigsegv: + /* FIXME: After a long discussion with Stas we finally + * agreed, that this is wrong. Here we should + * really send a SIGSEGV to the user program. + * But how do we create the correct context? We + * are inside a general protection fault handler + * and has just returned from a page fault handler. + * The correct context for the signal handler + * should be a mixture of the two, but how do we + * get the information? [KD] + */ + return_to_32bit(regs, VM86_UNKNOWN); } /* ---------------- vm86 special IRQ passing stuff ----------------- */ diff -Nur linux.old/include/asm-i386/processor.h linux.new/include/asm-i386/processor.h --- linux.old/include/asm-i386/processor.h Sun Aug 11 09:41:44 2002 +++ linux.new/include/asm-i386/processor.h Sun Aug 11 09:42:14 2002 @@ -373,7 +373,7 @@ /* virtual 86 mode info */ struct vm86_struct * vm86_info; unsigned long screen_bitmap; - unsigned long v86flags, v86mask, v86mode, saved_esp0; + unsigned long v86flags, v86mask, saved_esp0; /* IO permissions */ unsigned long *ts_io_bitmap; /* TLS cached descriptor */