Date: Sun, 12 May 2002 18:20:15 -0700 (PDT) From: David Xu <davidx@viasoft.com.cn> To: freebsd-gnats-submit@FreeBSD.org Subject: i386/38012: fix vm86 bios call crash bug Message-ID: <200205130120.g4D1KFb2078019@www.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 38012
>Category: i386
>Synopsis: fix vm86 bios call crash bug
>Confidential: no
>Severity: critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Sun May 12 18:30:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: David Xu
>Release: FreeBSD 5.0-CURRENT
>Organization:
Viatech
>Environment:
FreeBSD davidbsd.viasoft.com.cn 5.0-CURRENT FreeBSD 5.0-CURRENT #22:
Tue Apr 30 16:10:05 CST 2002
davidx@davidbsd.viasoft.com.cn:/usr/src/sys/i386/compile/xu i386
>Description:
there is a bug in vm86 bios call, our current implementation assumes
the vm86 bios call will never be preempted, but it is not true because
interrupt thread was implemented in current source. original vm86
bios call uses a static pcb(vm86pcb) to call bios and stores some
arguments in the pcb, problem is vm86pcb is not current thread's pcb,
when the thread is preempted and later gets cpu again, the working pcb
is no longer vm86pcb, cause machine hangs.
>How-To-Repeat:
repeat following command several times, machine will die.
#vidcontrol -g100x37 VESA_800x600
this commmand will trigger the bug, because the command use vesa.ko
which turns to call bios vesa service.
>Fix:
my solution is to let vm86pcb become current thread's pcb, because
it's current pcb, some parameters stored in vm86pcb will be modified
by cpu_switch(), so I store these parameters on stack, before every bios
call, I reset some parameters in the pcb, this is in vm86.c,
I also remove variable in_vm86call, and let the flag becames
pcb attribute.
--- /usr/src/sys/i386/i386/genassym.c Wed Mar 27 13:39:18 2002
+++ /usr/src/sys/i386/i386/genassym.c.new Sun May 12 22:15:29 2002
@@ -150,6 +150,8 @@
ASSYM(PCB_SIZE, sizeof(struct pcb));
+ASSYM(PCB_VM86CALL, PCB_VM86CALL);
+
ASSYM(TF_TRAPNO, offsetof(struct trapframe, tf_trapno));
ASSYM(TF_ERR, offsetof(struct trapframe, tf_err));
ASSYM(TF_CS, offsetof(struct trapframe, tf_cs));
--- /usr/src/sys/i386/isa/ipl.s Sun May 5 11:19:48 2002
+++ /usr/src/sys/i386/isa/ipl.s.new Sun May 12 22:20:59 2002
@@ -63,8 +63,9 @@
*/
testl $PSL_VM,TF_EFLAGS(%esp) /* are we in vm86 mode? */
jz doreti_notvm86
- cmpl $1,in_vm86call /* are we in a vm86 call? */
- jne doreti_ast /* can handle ASTs now if not */
+ movl PCPU(CURPCB),%ecx
+ testl $PCB_VM86CALL,PCB_FLAGS(%ecx)
+ jz doreti_ast /* can handle ASTs now if not */
jmp doreti_exit
doreti_notvm86:
--- /usr/src/sys/i386/include/pcb.h Wed Mar 27 13:39:19 2002
+++ /usr/src/sys/i386/include/pcb.h.new Sun May 12 22:22:33 2002
@@ -66,6 +66,7 @@
#define FP_SOFTFP 0x01 /* process using software fltng pnt emulator */
#define PCB_DBREGS 0x02 /* process using debug registers */
#define PCB_NPXTRAP 0x04 /* npx trap pending */
+#define PCB_VM86CALL 0x08
caddr_t pcb_onfault; /* copyin/out fault recovery */
int pcb_gs;
struct pcb_ext *pcb_ext; /* optional pcb extension */
--- /usr/src/sys/i386/i386/trap.c Sun Apr 28 01:07:15 2002
+++ /usr/src/sys/i386/i386/trap.c.new Sun May 12 22:14:51 2002
@@ -252,7 +252,7 @@
#endif /* DEVICE_POLLING */
if ((ISPL(frame.tf_cs) == SEL_UPL) ||
- ((frame.tf_eflags & PSL_VM) && !in_vm86call)) {
+ ((frame.tf_eflags & PSL_VM) && !(PCPU_GET(curpcb)->pcb_flags & PCB_VM86CALL))) {
/* user trap */
sticks = td->td_kse->ke_sticks;
@@ -462,7 +462,7 @@
/* FALL THROUGH */
case T_SEGNPFLT: /* segment not present fault */
- if (in_vm86call)
+ if (PCPU_GET(curpcb)->pcb_flags & PCB_VM86CALL)
break;
if (td->td_intr_nesting_level != 0)
@@ -564,7 +564,7 @@
* debugging the kernel.
*/
/* XXX Giant */
- if (user_dbreg_trap() && !in_vm86call) {
+ if (user_dbreg_trap() && !(PCPU_GET(curpcb)->pcb_flags & PCB_VM86CALL)) {
/*
* Reset breakpoint bits because the
* processor doesn't
--- /usr/src/sys/i386/include/vm86.h Wed Mar 20 13:48:58 2002
+++ /usr/src/sys/i386/include/vm86.h.new Sun May 12 22:23:05 2002
@@ -145,7 +145,6 @@
};
#ifdef _KERNEL
-extern int in_vm86call;
extern int vm86paddr;
struct thread;
--- /usr/src/sys/i386/i386/vm86.c Fri Apr 5 05:03:19 2002
+++ /usr/src/sys/i386/i386/vm86.c.new Mon May 13 07:22:58 2002
@@ -55,6 +55,7 @@
extern void vm86_biosret(struct vm86frame *);
void vm86_prepcall(struct vm86frame);
+static void vm86_setargs(void);
struct system_map {
int type;
@@ -434,7 +435,7 @@
pcb->pcb_ext = ext;
bzero(ext, sizeof(struct pcb_ext));
- ext->ext_tss.tss_esp0 = vm86paddr;
+ ext->ext_tss.tss_esp0 = vm86paddr - sizeof(struct vm86frame);
ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
ext->ext_tss.tss_ioopt =
((u_int)vml->vml_iomap - (u_int)&ext->ext_tss) << 16;
@@ -465,6 +466,13 @@
#endif
}
+static void vm86_setargs(void)
+{
+ vm86pcb->new_ptd = vm86pa | PG_V | PG_RW | PG_U;
+ vm86pcb->vm86_frame = vm86paddr - sizeof(struct vm86frame);
+ vm86pcb->pgtable_va = vm86paddr;
+}
+
vm_offset_t
vm86_getpage(struct vm86context *vmc, int pagenum)
{
@@ -576,6 +584,7 @@
vmf->vmf_trapno = intnum;
mtx_lock(&vm86_lock);
+ vm86_setargs();
retval = vm86_bioscall(vmf);
mtx_unlock(&vm86_lock);
return (retval);
@@ -598,6 +607,7 @@
int i, entry, retval;
mtx_lock(&vm86_lock);
+ vm86_setargs();
for (i = 0; i < vmc->npages; i++) {
page = vtophys(vmc->pmap[i].kva & PG_FRAME);
entry = vmc->pmap[i].pte_num;
--- /usr/src/sys/i386/i386/vm86bios.s Wed Dec 12 07:33:40 2001
+++ /usr/src/sys/i386/i386/vm86bios.s.new Sun May 12 22:15:14 2002
@@ -35,19 +35,22 @@
#define SCR_NEWPTD PCB_ESI /* readability macros */
#define SCR_VMFRAME PCB_EBP /* see vm86.c for explanation */
-#define SCR_STACK PCB_ESP
#define SCR_PGTABLE PCB_EBX
-#define SCR_ARGFRAME PCB_EIP
#define SCR_TSS0 PCB_SPARE
#define SCR_TSS1 (PCB_SPARE+4)
+#define ARG_SRCFRAME 8(%ebp)
+#define ARG_NEWPTD -4(%ebp)
+#define ARG_VMFRAME -8(%ebp)
+#define ARG_PGTABLE -12(%ebp)
+
.data
ALIGN_DATA
- .globl in_vm86call, vm86pcb
+ .globl vm86pcb
-in_vm86call: .long 0
vm86pcb: .long 0
+oldstack: .long 0
.text
@@ -55,11 +58,18 @@
* vm86_bioscall(struct trapframe_vm86 *vm86)
*/
ENTRY(vm86_bioscall)
+ pushl %ebp
+ movl %esp,%ebp
movl vm86pcb,%edx /* scratch data area */
- movl 4(%esp),%eax
- movl %eax,SCR_ARGFRAME(%edx) /* save argument pointer */
+ /*
+ vm86pcb could be preempted and contents will be modified,
+ save arguments on stack
+ */
+ pushl SCR_NEWPTD(%edx)
+ pushl SCR_VMFRAME(%edx)
+ pushl SCR_PGTABLE(%edx)
+
pushl %ebx
- pushl %ebp
pushl %esi
pushl %edi
pushl %gs
@@ -83,17 +93,28 @@
popfl
#endif
- movl SCR_VMFRAME(%edx),%ebx /* target frame location */
- movl %ebx,%edi /* destination */
- movl SCR_ARGFRAME(%edx),%esi /* source (set on entry) */
+ movl ARG_VMFRAME,%edi /* target frame location */
+ movl ARG_SRCFRAME,%esi /* source */
movl $VM86_FRAMESIZE/4,%ecx /* sizeof(struct vm86frame)/4 */
cld
rep
movsl /* copy frame to new stack */
- movl PCPU(CURPCB),%eax
- pushl %eax /* save curpcb */
- movl %edx,PCPU(CURPCB) /* set curpcb to vm86pcb */
+ movl $PCB_VM86CALL,PCB_FLAGS(%edx) /* set vm86 call flag */
+ movl %cr3,%eax
+ movl %eax,PCB_CR3(%edx) /* set address space */
+
+ xorl %eax,%eax
+ movl PCPU(CPUID),%ecx
+ btsl %ecx,private_tss /* set private_tss flag */
+ adcl $0,%eax
+ pushl %eax /* save flag */
+
+ pushl PCPU(CURPCB) /* save curpcb */
+ movl PCPU(CURTHREAD),%ecx
+ pushl TD_PCB(%ecx) /* save thread pcb */
+ movl %edx,TD_PCB(%ecx) /* set thread pcb */
+ movl %edx,PCPU(CURPCB) /* set curpcb */
movl PCPU(TSS_GDT),%ebx /* entry in GDT */
movl 0(%ebx),%eax
@@ -110,26 +131,19 @@
movl $GPROC0_SEL*8,%esi /* GSEL(entry, SEL_KPL) */
ltr %si
- movl %cr3,%eax
- pushl %eax /* save address space */
- movl IdlePTD,%ecx
- movl %ecx,%ebx
- addl $KERNBASE,%ebx /* va of Idle PTD */
+ leal PTD,%ebx
movl 0(%ebx),%eax
pushl %eax /* old ptde != 0 when booting */
pushl %ebx /* keep for reuse */
-
- movl %esp,SCR_STACK(%edx) /* save current stack location */
-
- movl SCR_NEWPTD(%edx),%eax /* mapping for vm86 page table */
+ movl ARG_NEWPTD,%eax /* mapping for vm86 page table */
movl %eax,0(%ebx) /* ... install as PTD entry 0 */
- movl %ecx,%cr3 /* new page tables */
- movl SCR_VMFRAME(%edx),%esp /* switch to new stack */
-
- call vm86_prepcall /* finish setup */
+ pushl %ebp /* easy for later recover */
+ movl PCB_EXT(%edx),%edi
+ movl %esp,oldstack /* save current stack location */
- movl $1,in_vm86call /* set flag for trap() */
+ movl ARG_VMFRAME,%esp /* switch to new stack */
+ call vm86_prepcall /* finish setup */
/*
* Return via doreti
@@ -142,39 +156,48 @@
* vm86_biosret(struct trapframe_vm86 *vm86)
*/
ENTRY(vm86_biosret)
- movl vm86pcb,%edx /* data area */
-
movl 4(%esp),%esi /* source */
- movl SCR_ARGFRAME(%edx),%edi /* destination */
+ movl vm86pcb,%edx /* working pcb */
+ movl oldstack,%esp /* back to old stack */
+ popl %ebp /* old argument location */
+
+ movl ARG_SRCFRAME,%edi /* destination */
movl $VM86_FRAMESIZE/4,%ecx /* size */
cld
rep
movsl /* copy frame to original frame */
- movl SCR_STACK(%edx),%esp /* back to old stack */
- popl %ebx /* saved va of Idle PTD */
+ popl %ebx /* PTD */
popl %eax
movl %eax,0(%ebx) /* restore old pte */
- popl %eax
- movl %eax,%cr3 /* install old page table */
- movl $0,in_vm86call /* reset trapflag */
-
- movl PCPU(TSS_GDT),%ebx /* entry in GDT */
+ movl PCPU(TSS_GDT),%ebx /* entry in GDT */
movl SCR_TSS0(%edx),%eax
movl %eax,0(%ebx) /* restore first word */
movl SCR_TSS1(%edx),%eax
movl %eax,4(%ebx) /* restore second word */
movl $GPROC0_SEL*8,%esi /* GSEL(entry, SEL_KPL) */
ltr %si
-
+
+ movl PCPU(CURTHREAD),%ecx
+ popl TD_PCB(%ecx)
popl PCPU(CURPCB) /* restore curpcb/curproc */
- movl SCR_ARGFRAME(%edx),%edx /* original stack frame */
+
+ movl PCPU(CPUID),%ecx
+ popl %eax /* restore private_tss flag */
+ cmpl $0,%eax
+ je 1f
+ btsl %ecx,private_tss
+1:
+ btrl %ecx,private_tss
+
+ movl ARG_SRCFRAME,%edx /* original stack frame */
movl TF_TRAPNO(%edx),%eax /* return (trapno) */
popl %gs
popl %edi
popl %esi
- popl %ebp
popl %ebx
+ movl %ebp, %esp
+ popl %ebp
ret /* back to our normal program */
following is patched vm86bios.s, because the patch is big, I provide
a full source file here:
/*-
* Copyright (c) 1998 Jonathan Lemon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/i386/i386/vm86bios.s,v 1.28 2001/12/11 23:33:40 jhb Exp $
*/
#include "opt_npx.h"
#include <machine/asmacros.h> /* miscellaneous asm macros */
#include <machine/trap.h>
#include "assym.s"
#define SCR_NEWPTD PCB_ESI /* readability macros */
#define SCR_VMFRAME PCB_EBP /* see vm86.c for explanation */
#define SCR_PGTABLE PCB_EBX
#define SCR_TSS0 PCB_SPARE
#define SCR_TSS1 (PCB_SPARE+4)
#define ARG_SRCFRAME 8(%ebp)
#define ARG_NEWPTD -4(%ebp)
#define ARG_VMFRAME -8(%ebp)
#define ARG_PGTABLE -12(%ebp)
.data
ALIGN_DATA
.globl vm86pcb
vm86pcb: .long 0
oldstack: .long 0
.text
/*
* vm86_bioscall(struct trapframe_vm86 *vm86)
*/
ENTRY(vm86_bioscall)
pushl %ebp
movl %esp,%ebp
movl vm86pcb,%edx /* scratch data area */
/*
vm86pcb could be preempted and contents will be modified,
save arguments on stack
*/
pushl SCR_NEWPTD(%edx)
pushl SCR_VMFRAME(%edx)
pushl SCR_PGTABLE(%edx)
pushl %ebx
pushl %esi
pushl %edi
pushl %gs
#ifdef DEV_NPX
pushfl
cli
movl PCPU(CURTHREAD),%ecx
cmpl %ecx,PCPU(FPCURTHREAD) /* do we need to save fp? */
jne 1f
testl %ecx,%ecx
je 1f /* no curproc/npxproc */
pushl %edx
movl TD_PCB(%ecx),%ecx
addl $PCB_SAVEFPU,%ecx
pushl %ecx
call npxsave
popl %ecx
popl %edx /* recover our pcb */
1:
popfl
#endif
movl ARG_VMFRAME,%edi /* target frame location */
movl ARG_SRCFRAME,%esi /* source */
movl $VM86_FRAMESIZE/4,%ecx /* sizeof(struct vm86frame)/4 */
cld
rep
movsl /* copy frame to new stack */
movl $PCB_VM86CALL,PCB_FLAGS(%edx) /* set vm86 call flag */
movl %cr3,%eax
movl %eax,PCB_CR3(%edx) /* set address space */
xorl %eax,%eax
movl PCPU(CPUID),%ecx
btsl %ecx,private_tss /* set private_tss flag */
adcl $0,%eax
pushl %eax /* save flag */
pushl PCPU(CURPCB) /* save curpcb */
movl PCPU(CURTHREAD),%ecx
pushl TD_PCB(%ecx) /* save thread pcb */
movl %edx,TD_PCB(%ecx) /* set thread pcb */
movl %edx,PCPU(CURPCB) /* set curpcb */
movl PCPU(TSS_GDT),%ebx /* entry in GDT */
movl 0(%ebx),%eax
movl %eax,SCR_TSS0(%edx) /* save first word */
movl 4(%ebx),%eax
andl $~0x200, %eax /* flip 386BSY -> 386TSS */
movl %eax,SCR_TSS1(%edx) /* save second word */
movl PCB_EXT(%edx),%edi /* vm86 tssd entry */
movl 0(%edi),%eax
movl %eax,0(%ebx)
movl 4(%edi),%eax
movl %eax,4(%ebx)
movl $GPROC0_SEL*8,%esi /* GSEL(entry, SEL_KPL) */
ltr %si
leal PTD,%ebx
movl 0(%ebx),%eax
pushl %eax /* old ptde != 0 when booting */
pushl %ebx /* keep for reuse */
movl ARG_NEWPTD,%eax /* mapping for vm86 page table */
movl %eax,0(%ebx) /* ... install as PTD entry 0 */
pushl %ebp /* easy for later recover */
movl PCB_EXT(%edx),%edi
movl %esp,oldstack /* save current stack location */
movl ARG_VMFRAME,%esp /* switch to new stack */
call vm86_prepcall /* finish setup */
/*
* Return via doreti
*/
MEXITCOUNT
jmp doreti
/*
* vm86_biosret(struct trapframe_vm86 *vm86)
*/
ENTRY(vm86_biosret)
movl 4(%esp),%esi /* source */
movl vm86pcb,%edx /* working pcb */
movl oldstack,%esp /* back to old stack */
popl %ebp /* old argument location */
movl ARG_SRCFRAME,%edi /* destination */
movl $VM86_FRAMESIZE/4,%ecx /* size */
cld
rep
movsl /* copy frame to original frame */
popl %ebx /* PTD */
popl %eax
movl %eax,0(%ebx) /* restore old pte */
movl PCPU(TSS_GDT),%ebx /* entry in GDT */
movl SCR_TSS0(%edx),%eax
movl %eax,0(%ebx) /* restore first word */
movl SCR_TSS1(%edx),%eax
movl %eax,4(%ebx) /* restore second word */
movl $GPROC0_SEL*8,%esi /* GSEL(entry, SEL_KPL) */
ltr %si
movl PCPU(CURTHREAD),%ecx
popl TD_PCB(%ecx)
popl PCPU(CURPCB) /* restore curpcb/curproc */
movl PCPU(CPUID),%ecx
popl %eax /* restore private_tss flag */
cmpl $0,%eax
je 1f
btsl %ecx,private_tss
1:
btrl %ecx,private_tss
movl ARG_SRCFRAME,%edx /* original stack frame */
movl TF_TRAPNO(%edx),%eax /* return (trapno) */
popl %gs
popl %edi
popl %esi
popl %ebx
movl %ebp, %esp
popl %ebp
ret /* back to our normal program */
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200205130120.g4D1KFb2078019>
