]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[librm] Speed up protected-to-real mode transition under KVM
authorMichael Brown <mcb30@ipxe.org>
Thu, 1 May 2014 13:58:24 +0000 (14:58 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 2 May 2014 14:23:20 +0000 (15:23 +0100)
On an Intel CPU supporting VMX, KVM will emulate instructions while
the CPU state remains "invalid".  In real mode, the CPU state is
defined to be "invalid" if any segment register has a base which is
not equal to (sreg<<4) or a limit which is not equal to 64kB.

We don't actually use the base stored in the REAL_DS descriptor for
any significant purpose.  Change the base stored in this descriptor to
be equal to (REAL_DS<<4).  A segment register loaded with REAL_DS is
then automatically valid in both real and protected modes.  This
allows KVM to stop emulating instructions much sooner.

The only use of REAL_DS for memory accesses currently occurs in the
indirect ljmp within prot_to_real.  Change this to a direct ljmp,
storing rm_cs in .text16 as part of the ljmp instruction.  This
removes the only memory access via REAL_DS (thereby allowing for the
above descriptor base address hack), and also simplifies the ljmp
instruction (which will still have to be emulated).

Load the real-mode interrupt descriptor table register before
switching to real mode, since this avoids triggering an EXCEPTION_NMI
and corresponding VM exit.

This reduces the time taken by prot_to_real under KVM by around 65%.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/i386/include/librm.h
src/arch/i386/transitions/librm.S

index 4a4e61aa1e79b03616e2efa69dbcf426d46ca0e8..c8ba72b5364d550638d49547eaaa28092c15c507 100644 (file)
@@ -165,8 +165,8 @@ extern char *text16;
 /* Variables in librm.S, present in the normal data segment */
 extern uint16_t rm_sp;
 extern uint16_t rm_ss;
-extern uint16_t __data16 ( rm_cs );
-#define rm_cs __use_data16 ( rm_cs )
+extern uint16_t __text16 ( rm_cs );
+#define rm_cs __use_text16 ( rm_cs )
 extern uint16_t __text16 ( rm_ds );
 #define rm_ds __use_text16 ( rm_ds )
 
index 18ceb0d6159bf5913d24b6f0204facdc910fda05..0d8110ac1ab48c6a8d051acd2eb916300e34f584 100644 (file)
@@ -72,7 +72,7 @@ real_cs:      /* 16 bit real mode code segment */
 
        .org    gdt + REAL_DS   
 real_ds:       /* 16 bit real mode data segment */
-       .word   0xffff, 0
+       .word   0xffff, ( REAL_DS << 4 )
        .byte   0, 0x93, 0x00, 0
 
 gdt_end:
@@ -111,20 +111,18 @@ init_librm:
        /* Store rm_cs and text16, set up real_cs segment */
        xorl    %eax, %eax
        movw    %cs, %ax
-       movw    %ax, rm_cs
+       movw    %ax, %cs:rm_cs
        shll    $4, %eax
        movw    $real_cs, %bx
        call    set_seg_base
        addr32 leal     (%eax, %edi), %ebx
        movl    %ebx, rm_text16
 
-       /* Store rm_ds and data16, set up real_ds segment */
+       /* Store rm_ds and data16 */
        xorl    %eax, %eax
        movw    %ds, %ax
        movw    %ax, %cs:rm_ds
        shll    $4, %eax
-       movw    $real_ds, %bx
-       call    set_seg_base
        addr32 leal     (%eax, %edi), %ebx
        movl    %ebx, rm_data16
 
@@ -241,7 +239,7 @@ r2p_pmode:
        ret
 
        /* Default real-mode interrupt descriptor table */
-       .section ".data16", "aw", @progbits
+       .section ".data", "aw", @progbits
 rm_idtr:
        .word 0xffff /* limit */
        .long 0 /* base */
@@ -287,6 +285,9 @@ prot_to_real:
        /* Record protected-mode %esp (after removal of data) */
        movl    %esi, pm_esp
 
+       /* Reset IDTR to the real-mode defaults */
+       lidt    rm_idtr
+
        /* Load real-mode segment limits */
        movw    $REAL_DS, %ax
        movw    %ax, %ds
@@ -302,9 +303,9 @@ p2r_rmode:
        movl    %cr0, %eax
        andb    $0!CR0_PE, %al
        movl    %eax, %cr0
-       ljmp    *p2r_jump_vector
-p2r_jump_target:
-
+p2r_ljmp_rm_cs:
+       ljmp    $0, $1f
+1:
        /* Set up real-mode data segments and stack pointer */
        movw    %cs:rm_ds, %ax
        movw    %ax, %ds
@@ -314,26 +315,23 @@ p2r_jump_target:
        movw    %bp, %ss
        movl    %edx, %esp
 
-       /* Reset IDTR to the real-mode defaults */
-       data32 lidt rm_idtr
-
        /* Return to real-mode address */
        data32 ret
 
 
        /* Real-mode code and data segments.  Assigned by the call to
         * init_librm.  rm_cs doubles as the segment part of the jump
-        * vector used by prot_to_real.  rm_ds is located in .text16
-        * rather than .data16 because code needs to be able to locate
-        * the data segment.
+        * instruction used by prot_to_real.  Both are located in
+        * .text16 rather than .data16: rm_cs since it forms part of
+        * the jump instruction within the code segment, and rm_ds
+        * since real-mode code needs to be able to locate the data
+        * segment with no other reference available.
         */
-       .section ".data16", "aw", @progbits
-p2r_jump_vector:
-       .word   p2r_jump_target
        .globl rm_cs
-rm_cs: .word 0
-       .globl rm_ds
+       .equ    rm_cs, ( p2r_ljmp_rm_cs + 3 )
+
        .section ".text16.data", "aw", @progbits
+       .globl rm_ds
 rm_ds: .word 0
 
 /****************************************************************************