Commit 6e469c37 authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[PATCH] vm86 updates.

Numerous updates from 2.4 (see changelog)
parent 955a9169
...@@ -2,7 +2,34 @@ ...@@ -2,7 +2,34 @@
* linux/kernel/vm86.c * linux/kernel/vm86.c
* *
* Copyright (C) 1994 Linus Torvalds * Copyright (C) 1994 Linus Torvalds
*
* 29 dec 2001 - Fixed oopses caused by unchecked access to the vm86
* stack - Manfred Spraul <manfreds@colorfullife.com>
*
* 22 mar 2002 - Manfred detected the stackfaults, but didn't handle
* them correctly. Now the emulation will be in a
* consistent state after stackfaults - Kasper Dupont
* <kasperd@daimi.au.dk>
*
* 22 mar 2002 - Added missing clear_IF in set_vflags_* Kasper Dupont
* <kasperd@daimi.au.dk>
*
* ?? ??? 2002 - Fixed premature returns from handle_vm86_fault
* caused by Kasper Dupont's changes - Stas Sergeev
*
* 4 apr 2002 - Fixed CHECK_IF_IN_TRAP broken by Stas' changes.
* Kasper Dupont <kasperd@daimi.au.dk>
*
* 9 apr 2002 - Changed syntax of macros in handle_vm86_fault.
* Kasper Dupont <kasperd@daimi.au.dk>
*
* 9 apr 2002 - Changed stack access macros to jump to a label
* instead of returning to userspace. This simplifies
* do_int, and is needed by handle_vm6_fault. Kasper
* Dupont <kasperd@daimi.au.dk>
*
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -17,6 +44,7 @@ ...@@ -17,6 +44,7 @@
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/irq.h>
/* /*
* Known problems: * Known problems:
...@@ -97,23 +125,24 @@ static void mark_screen_rdonly(struct task_struct * tsk) ...@@ -97,23 +125,24 @@ static void mark_screen_rdonly(struct task_struct * tsk)
pte_t *pte, *mapped; pte_t *pte, *mapped;
int i; int i;
preempt_disable();
spin_lock(&tsk->mm->page_table_lock);
pgd = pgd_offset(tsk->mm, 0xA0000); pgd = pgd_offset(tsk->mm, 0xA0000);
if (pgd_none(*pgd)) if (pgd_none(*pgd))
return; goto out;
if (pgd_bad(*pgd)) { if (pgd_bad(*pgd)) {
pgd_ERROR(*pgd); pgd_ERROR(*pgd);
pgd_clear(pgd); pgd_clear(pgd);
return; goto out;
} }
pmd = pmd_offset(pgd, 0xA0000); pmd = pmd_offset(pgd, 0xA0000);
if (pmd_none(*pmd)) if (pmd_none(*pmd))
return; goto out;
if (pmd_bad(*pmd)) { if (pmd_bad(*pmd)) {
pmd_ERROR(*pmd); pmd_ERROR(*pmd);
pmd_clear(pmd); pmd_clear(pmd);
return; goto out;
} }
preempt_disable();
pte = mapped = pte_offset_map(pmd, 0xA0000); pte = mapped = pte_offset_map(pmd, 0xA0000);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
if (pte_present(*pte)) if (pte_present(*pte))
...@@ -121,6 +150,8 @@ static void mark_screen_rdonly(struct task_struct * tsk) ...@@ -121,6 +150,8 @@ static void mark_screen_rdonly(struct task_struct * tsk)
pte++; pte++;
} }
pte_unmap(mapped); pte_unmap(mapped);
out:
spin_unlock(&tsk->mm->page_table_lock);
preempt_enable(); preempt_enable();
flush_tlb(); flush_tlb();
} }
...@@ -293,12 +324,30 @@ static inline void clear_TF(struct kernel_vm86_regs * regs) ...@@ -293,12 +324,30 @@ static inline void clear_TF(struct kernel_vm86_regs * regs)
regs->eflags &= ~TF_MASK; regs->eflags &= ~TF_MASK;
} }
static inline void clear_AC(struct kernel_vm86_regs * regs)
{
regs->eflags &= ~AC_MASK;
}
/* It is correct to call set_IF(regs) from the set_vflags_*
* functions. However someone forgot to call clear_IF(regs)
* in the opposite case.
* After the command sequence CLI PUSHF STI POPF you should
* end up with interrups disabled, but you ended up with
* interrupts enabled.
* ( I was testing my own changes, but the only bug I
* could find was in a function I had not changed. )
* [KD]
*/
static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs) static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs)
{ {
set_flags(VEFLAGS, eflags, current->thread.v86mask); set_flags(VEFLAGS, eflags, current->thread.v86mask);
set_flags(regs->eflags, eflags, SAFE_MASK); set_flags(regs->eflags, eflags, SAFE_MASK);
if (eflags & IF_MASK) if (eflags & IF_MASK)
set_IF(regs); set_IF(regs);
else
clear_IF(regs);
} }
static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs) static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs)
...@@ -307,6 +356,8 @@ static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_reg ...@@ -307,6 +356,8 @@ static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_reg
set_flags(regs->eflags, flags, SAFE_MASK); set_flags(regs->eflags, flags, SAFE_MASK);
if (flags & IF_MASK) if (flags & IF_MASK)
set_IF(regs); set_IF(regs);
else
clear_IF(regs);
} }
static inline unsigned long get_vflags(struct kernel_vm86_regs * regs) static inline unsigned long get_vflags(struct kernel_vm86_regs * regs)
...@@ -326,80 +377,90 @@ static inline int is_revectored(int nr, struct revectored_struct * bitmap) ...@@ -326,80 +377,90 @@ static inline int is_revectored(int nr, struct revectored_struct * bitmap)
return nr; return nr;
} }
/* #define val_byte(val, n) (((__u8 *)&val)[n])
* 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 #define pushb(base, ptr, val, err_label) \
* conventions.. 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]
*/ */
#define pushb(base, ptr, val) \ static void do_int(struct kernel_vm86_regs *regs, int i,
__asm__ __volatile__( \ unsigned char * ssp, unsigned short sp)
"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; })
static void do_int(struct kernel_vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp)
{ {
unsigned long *intr_ptr, segoffs; unsigned long *intr_ptr, segoffs;
...@@ -414,14 +475,15 @@ static void do_int(struct kernel_vm86_regs *regs, int i, unsigned char * ssp, un ...@@ -414,14 +475,15 @@ static void do_int(struct kernel_vm86_regs *regs, int i, unsigned char * ssp, un
goto cannot_handle; goto cannot_handle;
if ((segoffs >> 16) == BIOSSEG) if ((segoffs >> 16) == BIOSSEG)
goto cannot_handle; goto cannot_handle;
pushw(ssp, sp, get_vflags(regs)); pushw(ssp, sp, get_vflags(regs), cannot_handle);
pushw(ssp, sp, regs->cs); pushw(ssp, sp, regs->cs, cannot_handle);
pushw(ssp, sp, IP(regs)); pushw(ssp, sp, IP(regs), cannot_handle);
regs->cs = segoffs >> 16; regs->cs = segoffs >> 16;
SP(regs) -= 6; SP(regs) -= 6;
IP(regs) = segoffs & 0xffff; IP(regs) = segoffs & 0xffff;
clear_TF(regs); clear_TF(regs);
clear_IF(regs); clear_IF(regs);
clear_AC(regs);
return; return;
cannot_handle: cannot_handle:
...@@ -453,75 +515,80 @@ int handle_vm86_trap(struct kernel_vm86_regs * regs, long error_code, int trapno ...@@ -453,75 +515,80 @@ int handle_vm86_trap(struct kernel_vm86_regs * regs, long error_code, int trapno
void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code)
{ {
unsigned char *csp, *ssp; unsigned char *csp, *ssp, opcode;
unsigned long ip, sp; unsigned short ip, sp;
int data32, pref_done;
#define CHECK_IF_IN_TRAP \ #define CHECK_IF_IN_TRAP \
if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \ if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
pushw(ssp,sp,popw(ssp,sp) | TF_MASK); newflags |= TF_MASK
#define VM86_FAULT_RETURN \ #define VM86_FAULT_RETURN do { \
if (VMPI.force_return_for_pic && (VEFLAGS & (IF_MASK | VIF_MASK))) \ if (VMPI.force_return_for_pic && (VEFLAGS & (IF_MASK | VIF_MASK))) \
return_to_32bit(regs, VM86_PICRETURN); \ return_to_32bit(regs, VM86_PICRETURN); \
return; return; } while (0)
csp = (unsigned char *) (regs->cs << 4); csp = (unsigned char *) (regs->cs << 4);
ssp = (unsigned char *) (regs->ss << 4); ssp = (unsigned char *) (regs->ss << 4);
sp = SP(regs); sp = SP(regs);
ip = IP(regs); ip = IP(regs);
switch (popb(csp, ip)) { data32 = 0;
pref_done = 0;
/* operand size override */ do {
case 0x66: switch (opcode = popb(csp, ip, simulate_sigsegv)) {
switch (popb(csp, ip)) { case 0x66: /* 32-bit data */ data32=1; break;
case 0x67: /* 32-bit address */ break;
/* pushfd */ case 0x2e: /* CS */ break;
case 0x9c: case 0x3e: /* DS */ break;
SP(regs) -= 4; case 0x26: /* ES */ break;
IP(regs) += 2; case 0x36: /* SS */ break;
pushl(ssp, sp, get_vflags(regs)); case 0x65: /* GS */ break;
VM86_FAULT_RETURN; case 0x64: /* FS */ break;
case 0xf2: /* repnz */ break;
/* popfd */ case 0xf3: /* rep */ break;
case 0x9d: default: pref_done = 1;
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);
} }
} while (!pref_done);
switch (opcode) {
/* pushf */ /* pushf */
case 0x9c: case 0x9c:
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; SP(regs) -= 2;
IP(regs)++; }
pushw(ssp, sp, get_vflags(regs)); IP(regs) = ip;
VM86_FAULT_RETURN; VM86_FAULT_RETURN;
/* popf */ /* popf */
case 0x9d: case 0x9d:
{
unsigned long newflags;
if (data32) {
newflags=popl(ssp, sp, simulate_sigsegv);
SP(regs) += 4;
} else {
newflags = popw(ssp, sp, simulate_sigsegv);
SP(regs) += 2; SP(regs) += 2;
IP(regs)++; }
CHECK_IF_IN_TRAP IP(regs) = ip;
set_vflags_short(popw(ssp, sp), regs); CHECK_IF_IN_TRAP;
if (data32) {
set_vflags_long(newflags, regs);
} else {
set_vflags_short(newflags, regs);
}
VM86_FAULT_RETURN; VM86_FAULT_RETURN;
}
/* int xx */ /* int xx */
case 0xcd: { case 0xcd: {
int intno=popb(csp, ip); int intno=popb(csp, ip, simulate_sigsegv);
IP(regs) += 2; IP(regs) = ip;
if (VMPI.vm86dbg_active) { if (VMPI.vm86dbg_active) {
if ( (1 << (intno &7)) & VMPI.vm86dbg_intxxtab[intno >> 3] ) if ( (1 << (intno &7)) & VMPI.vm86dbg_intxxtab[intno >> 3] )
return_to_32bit(regs, VM86_INTx + (intno << 8)); return_to_32bit(regs, VM86_INTx + (intno << 8));
...@@ -532,16 +599,35 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) ...@@ -532,16 +599,35 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code)
/* iret */ /* iret */
case 0xcf: case 0xcf:
{
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; SP(regs) += 6;
IP(regs) = popw(ssp, sp); }
regs->cs = popw(ssp, sp); IP(regs) = newip;
CHECK_IF_IN_TRAP regs->cs = newcs;
set_vflags_short(popw(ssp, sp), regs); CHECK_IF_IN_TRAP;
if (data32) {
set_vflags_long(newflags, regs);
} else {
set_vflags_short(newflags, regs);
}
VM86_FAULT_RETURN; VM86_FAULT_RETURN;
}
/* cli */ /* cli */
case 0xfa: case 0xfa:
IP(regs)++; IP(regs) = ip;
clear_IF(regs); clear_IF(regs);
VM86_FAULT_RETURN; VM86_FAULT_RETURN;
...@@ -553,13 +639,28 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) ...@@ -553,13 +639,28 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code)
* Probably needs some horsing around with the TF flag. Aiee.. * Probably needs some horsing around with the TF flag. Aiee..
*/ */
case 0xfb: case 0xfb:
IP(regs)++; IP(regs) = ip;
set_IF(regs); set_IF(regs);
VM86_FAULT_RETURN; VM86_FAULT_RETURN;
default: default:
return_to_32bit(regs, VM86_UNKNOWN); 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 ----------------- */ /* ---------------- vm86 special IRQ passing stuff ----------------- */
...@@ -623,6 +724,14 @@ static inline int task_valid(struct task_struct *tsk) ...@@ -623,6 +724,14 @@ static inline int task_valid(struct task_struct *tsk)
return ret; return ret;
} }
void release_x86_irqs(struct task_struct *task)
{
int i;
for (i=3; i<16; i++)
if (vm86_irqs[i].tsk == task)
free_vm86_irq(i);
}
static inline void handle_irq_zombies(void) static inline void handle_irq_zombies(void)
{ {
int i; int i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment