Author: kib
Date: Tue Aug 14 12:13:27 2012
New Revision: 239251
URL: http://svn.freebsd.org/changeset/base/239251

Log:
  Real hardware, as opposed to QEMU, does not allow to have a call gate
  in long mode which transfers control to 32bit code segment. Unbreak
  the lcall $7,$0 implementation on amd64 by putting the 64bit user code
  segment' selector into call gate, and execute the 64bit trampoline
  which converts the return frame into 32bit format and switches back to
  32bit mode for executing int $0x80 trampoline.
  
  Note that all jumps over the hoops are performed in the user mode.
  
  MFC after:    1 week

Modified:
  head/sys/amd64/ia32/ia32_sigtramp.S
  head/sys/amd64/ia32/ia32_syscall.c

Modified: head/sys/amd64/ia32/ia32_sigtramp.S
==============================================================================
--- head/sys/amd64/ia32/ia32_sigtramp.S Tue Aug 14 12:11:48 2012        
(r239250)
+++ head/sys/amd64/ia32/ia32_sigtramp.S Tue Aug 14 12:13:27 2012        
(r239251)
@@ -91,8 +91,29 @@ ia32_osigcode:
  */
        ALIGN_TEXT
 lcall_tramp:
+       .code64
+       /*
+        * There, we are in 64bit mode and need to return to 32bit.
+        * First, convert call frame from 64 to 32 bit format.
+        */
+       pushq   %rax
+       movl    16(%rsp),%eax
+       movl    %eax,20(%rsp)   /* ret %cs */
+       movl    8(%rsp),%eax
+       movl    %eax,16(%rsp)   /* ret %rip -> %eip */
+       popq    %rax
+       addq    $8,%rsp
+       /* Now return to 32bit */
+       pushq   $0x33 /* _ucode32sel UPL */
+       callq   1f
+1:
+       addq    $2f-1b,(%rsp)
+       lretq
+2:
+       /* Back in 32bit mode */
+       .code32
        cmpl    $SYS_vfork,%eax
-       je      2f
+       je      4f
        pushl   %ebp
        movl    %esp,%ebp
        pushl   0x24(%ebp) /* arg 6 */
@@ -101,19 +122,19 @@ lcall_tramp:
        pushl   0x18(%ebp)
        pushl   0x14(%ebp)
        pushl   0x10(%ebp) /* arg 1 */
-       pushl   0xc(%ebp) /* gap */
+       pushl   0xc(%ebp)  /* gap */
        int     $0x80
        leavel
-1:
+3:
        lretl
-2:
+4:
        /*
         * vfork handling is special and relies on the libc stub saving
         * the return ip in %ecx.  If vfork failed, then there is no
         * child which can corrupt the frame created by call gate.
         */
        int     $0x80
-       jb      1b
+       jb      3b
        addl    $8,%esp
        jmpl    *%ecx
 #endif

Modified: head/sys/amd64/ia32/ia32_syscall.c
==============================================================================
--- head/sys/amd64/ia32/ia32_syscall.c  Tue Aug 14 12:11:48 2012        
(r239250)
+++ head/sys/amd64/ia32/ia32_syscall.c  Tue Aug 14 12:13:27 2012        
(r239251)
@@ -244,7 +244,7 @@ setup_lcall_gate(void)
        bzero(ssd, sizeof(*ssd));
        ssd->gd_looffset = lcall_addr;
        ssd->gd_hioffset = lcall_addr >> 16;
-       ssd->gd_selector = _ucode32sel;
+       ssd->gd_selector = _ucodesel;
        ssd->gd_type = SDT_SYSCGT;
        ssd->gd_dpl = SEL_UPL;
        ssd->gd_p = 1;
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to