Browse Source

[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>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
5a08b63cb7
2 changed files with 20 additions and 22 deletions
  1. 2
    2
      src/arch/i386/include/librm.h
  2. 18
    20
      src/arch/i386/transitions/librm.S

+ 2
- 2
src/arch/i386/include/librm.h View File

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

+ 18
- 20
src/arch/i386/transitions/librm.S View File

@@ -72,7 +72,7 @@ real_cs: 	/* 16 bit real mode code segment */
72 72
 
73 73
 	.org	gdt + REAL_DS	
74 74
 real_ds:	/* 16 bit real mode data segment */
75
-	.word	0xffff, 0
75
+	.word	0xffff, ( REAL_DS << 4 )
76 76
 	.byte	0, 0x93, 0x00, 0
77 77
 
78 78
 gdt_end:
@@ -111,20 +111,18 @@ init_librm:
111 111
 	/* Store rm_cs and text16, set up real_cs segment */
112 112
 	xorl	%eax, %eax
113 113
 	movw	%cs, %ax
114
-	movw	%ax, rm_cs
114
+	movw	%ax, %cs:rm_cs
115 115
 	shll	$4, %eax
116 116
 	movw	$real_cs, %bx
117 117
 	call	set_seg_base
118 118
 	addr32 leal	(%eax, %edi), %ebx
119 119
 	movl	%ebx, rm_text16
120 120
 
121
-	/* Store rm_ds and data16, set up real_ds segment */
121
+	/* Store rm_ds and data16 */
122 122
 	xorl	%eax, %eax
123 123
 	movw	%ds, %ax
124 124
 	movw	%ax, %cs:rm_ds
125 125
 	shll	$4, %eax
126
-	movw	$real_ds, %bx
127
-	call	set_seg_base
128 126
 	addr32 leal	(%eax, %edi), %ebx
129 127
 	movl	%ebx, rm_data16
130 128
 
@@ -241,7 +239,7 @@ r2p_pmode:
241 239
 	ret
242 240
 
243 241
 	/* Default real-mode interrupt descriptor table */
244
-	.section ".data16", "aw", @progbits
242
+	.section ".data", "aw", @progbits
245 243
 rm_idtr:
246 244
 	.word 0xffff /* limit */
247 245
 	.long 0 /* base */
@@ -287,6 +285,9 @@ prot_to_real:
287 285
 	/* Record protected-mode %esp (after removal of data) */
288 286
 	movl	%esi, pm_esp
289 287
 
288
+	/* Reset IDTR to the real-mode defaults */
289
+	lidt	rm_idtr
290
+
290 291
 	/* Load real-mode segment limits */
291 292
 	movw	$REAL_DS, %ax
292 293
 	movw	%ax, %ds
@@ -302,9 +303,9 @@ p2r_rmode:
302 303
 	movl	%cr0, %eax
303 304
 	andb	$0!CR0_PE, %al
304 305
 	movl	%eax, %cr0
305
-	ljmp	*p2r_jump_vector
306
-p2r_jump_target:
307
-
306
+p2r_ljmp_rm_cs:
307
+	ljmp	$0, $1f
308
+1:
308 309
 	/* Set up real-mode data segments and stack pointer */
309 310
 	movw	%cs:rm_ds, %ax
310 311
 	movw	%ax, %ds
@@ -314,26 +315,23 @@ p2r_jump_target:
314 315
 	movw	%bp, %ss
315 316
 	movl	%edx, %esp
316 317
 
317
-	/* Reset IDTR to the real-mode defaults */
318
-	data32 lidt rm_idtr
319
-
320 318
 	/* Return to real-mode address */
321 319
 	data32 ret
322 320
 
323 321
 
324 322
 	/* Real-mode code and data segments.  Assigned by the call to
325 323
 	 * init_librm.  rm_cs doubles as the segment part of the jump
326
-	 * vector used by prot_to_real.  rm_ds is located in .text16
327
-	 * rather than .data16 because code needs to be able to locate
328
-	 * the data segment.
324
+	 * instruction used by prot_to_real.  Both are located in
325
+	 * .text16 rather than .data16: rm_cs since it forms part of
326
+	 * the jump instruction within the code segment, and rm_ds
327
+	 * since real-mode code needs to be able to locate the data
328
+	 * segment with no other reference available.
329 329
 	 */
330
-	.section ".data16", "aw", @progbits
331
-p2r_jump_vector:
332
-	.word	p2r_jump_target
333 330
 	.globl rm_cs
334
-rm_cs:	.word 0
335
-	.globl rm_ds
331
+	.equ	rm_cs, ( p2r_ljmp_rm_cs + 3 )
332
+
336 333
 	.section ".text16.data", "aw", @progbits
334
+	.globl rm_ds
337 335
 rm_ds:	.word 0
338 336
 
339 337
 /****************************************************************************

Loading…
Cancel
Save