david/ipxe
Archived
1
0

[librm] Speed up protected-to-real mode transition under KVM

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>
This commit is contained in:
Michael Brown 2014-05-01 14:58:24 +01:00
parent 03e76c34d8
commit 5a08b63cb7
2 changed files with 20 additions and 22 deletions

View File

@ -165,8 +165,8 @@ extern char *text16;
/* Variables in librm.S, present in the normal data segment */ /* Variables in librm.S, present in the normal data segment */
extern uint16_t rm_sp; extern uint16_t rm_sp;
extern uint16_t rm_ss; extern uint16_t rm_ss;
extern uint16_t __data16 ( rm_cs ); extern uint16_t __text16 ( rm_cs );
#define rm_cs __use_data16 ( rm_cs ) #define rm_cs __use_text16 ( rm_cs )
extern uint16_t __text16 ( rm_ds ); extern uint16_t __text16 ( rm_ds );
#define rm_ds __use_text16 ( rm_ds ) #define rm_ds __use_text16 ( rm_ds )

View File

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