Date: Sat, 18 May 2002 00:00:42 -0700 (PDT) From: David Xu <davidx@viasoft.com.cn> To: freebsd-gnats-submit@FreeBSD.org Subject: i386/38223: fix vm86 bios call crash bug (updated) Message-ID: <200205180700.g4I70goR075276@www.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 38223 >Category: i386 >Synopsis: fix vm86 bios call crash bug (updated) >Confidential: no >Severity: critical >Priority: high >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sat May 18 00:10:02 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: this PR is updated version of PR 38012. http://www.freebsd.org/cgi/query-pr.cgi?pr=i386/38012 in that PR, a patch for vm86bios.s does not fully restore orignal thread PCB's tss descriptor into gdt, the patch fixes it. 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 Sat May 18 14:44:00 2002 +++ /usr/src/sys/i386/i386/vm86bios.s.new Sat May 18 14:43:52 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,26 @@ 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 */ + + 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 +129,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 +154,53 @@ * 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 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 SCR_TSS0(%edx),%eax - movl %eax,0(%ebx) /* restore first word */ - movl SCR_TSS1(%edx),%eax - movl %eax,4(%ebx) /* restore second word */ + popl %edx /* original thread PCB */ + movl PCPU(CPUID),%esi + cmpl $0, PCB_EXT(%edx) + je 1f + btsl %esi,private_tss + movl PCB_EXT(%edx),%edi + jmp 2f +1: + leal -16(%edx),%ebx + movl %ebx,PCPU(COMMON_TSS) + TSS_ESP0 + btrl %esi,private_tss + PCPU_ADDR(COMMON_TSSD,%edi) +2: + movl PCPU(TSS_GDT),%ebx /* entry in GDT */ + 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 - + + movl PCPU(CURTHREAD),%ecx + movl %edx,TD_PCB(%ecx) popl PCPU(CURPCB) /* restore curpcb/curproc */ - movl SCR_ARGFRAME(%edx),%edx /* original stack frame */ + + 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 */ 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 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 */ popl %edx /* original thread PCB */ movl PCPU(CPUID),%esi cmpl $0, PCB_EXT(%edx) je 1f btsl %esi,private_tss movl PCB_EXT(%edx),%edi jmp 2f 1: leal -16(%edx),%ebx movl %ebx,PCPU(COMMON_TSS) + TSS_ESP0 btrl %esi,private_tss PCPU_ADDR(COMMON_TSSD,%edi) 2: movl PCPU(TSS_GDT),%ebx /* entry in GDT */ 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 movl PCPU(CURTHREAD),%ecx movl %edx,TD_PCB(%ecx) popl PCPU(CURPCB) /* restore curpcb/curproc */ 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?200205180700.g4I70goR075276>