浏览代码

[librm] Reduce real-mode stack consumption in virt_call()

Some PXE NBPs are known to make PXE API calls with very little space
available on the real-mode stack.  For example, the Rembo-ia32 NBP
from some versions of IBM's Tivoli Provisioning Manager for Operating
System Deployment (TPMfOSD) will issue calls with the real-mode stack
placed at 0000:03d2; this is at the end of the interrupt vector table
and leaves only 498 bytes of stack space available before overwriting
the hardware IRQ vectors.  This limits the amount of state that we can
preserve before transitioning to protected mode.

Work around these challenging conditions by preserving everything
other than the initial register dump in a temporary static buffer
within our real-mode data segment, and copying the contents of this
buffer to the protected-mode stack.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 年前
父节点
当前提交
2d42d3cff6
共有 1 个文件被更改,包括 103 次插入59 次删除
  1. 103
    59
      src/arch/x86/transitions/librm.S

+ 103
- 59
src/arch/x86/transitions/librm.S 查看文件

@@ -180,17 +180,49 @@ gdt_end:
180 180
  * to us.
181 181
  ****************************************************************************
182 182
  */
183
-	.section ".bss.rm_sp", "aw", @nobits
183
+	.section ".bss.rm_ss_sp", "aw", @nobits
184 184
 	.globl rm_sp
185 185
 rm_sp:	.word 0
186
-
187
-	.section ".bss.rm_ss", "aw", @nobits
188 186
 	.globl rm_ss
189 187
 rm_ss:	.word 0
190 188
 
191 189
 	.section ".data.pm_esp", "aw", @progbits
192 190
 pm_esp:	.long VIRTUAL(_estack)
193 191
 
192
+/****************************************************************************
193
+ * Temporary static data buffer
194
+ *
195
+ * This is used to reduce the amount of real-mode stack space consumed
196
+ * during mode transitions, since we are sometimes called with very
197
+ * little real-mode stack space available.
198
+ ****************************************************************************
199
+ */
200
+	/* Temporary static buffer usage by virt_call */
201
+	.struct	0
202
+VC_TMP_GDT:		.space	6
203
+VC_TMP_IDT:		.space	6
204
+VC_TMP_PAD:		.space	4 /* for alignment */
205
+.if64
206
+VC_TMP_CR3:		.space	4
207
+VC_TMP_CR4:		.space	4
208
+VC_TMP_EMER:		.space	8
209
+.endif
210
+VC_TMP_END:
211
+	.previous
212
+
213
+	/* Temporary static buffer usage by real_call */
214
+	.struct 0
215
+RC_TMP_FUNCTION:	.space	4
216
+RC_TMP_END:
217
+	.previous
218
+
219
+	/* Shared temporary static buffer */
220
+	.section ".bss16.rm_tmpbuf", "aw", @nobits
221
+	.align 16
222
+rm_tmpbuf:
223
+	.space	VC_TMP_END
224
+	.size	rm_tmpbuf, . - rm_tmpbuf
225
+
194 226
 /****************************************************************************
195 227
  * Virtual address offsets
196 228
  *
@@ -341,6 +373,7 @@ set_seg_base:
341 373
  *
342 374
  * Parameters: 
343 375
  *   %ecx : number of bytes to move from RM stack to PM stack
376
+ *   %edx : number of bytes to copy from RM temporary buffer to PM stack
344 377
  *
345 378
  ****************************************************************************
346 379
  */
@@ -361,14 +394,19 @@ real_to_prot:
361 394
 	/* Add protected-mode return address to length of data to be copied */
362 395
 	addw	$4, %cx /* %ecx must be less than 64kB anyway */
363 396
 
364
-	/* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
365
-	xorl	%ebp, %ebp
366
-	movw	%ss, %bp
367
-	movzwl	%sp, %edx
368
-	movl	%ebp, %eax
397
+	/* Real-mode %ss:%sp => %ebp and virtual address => %esi */
398
+	xorl	%eax, %eax
399
+	movw	%ss, %ax
369 400
 	shll	$4, %eax
370
-	addr32 leal (%eax,%edx), %esi
401
+	movzwl	%sp, %ebp
402
+	addr32 leal (%eax,%ebp), %esi
371 403
 	subl	rm_virt_offset, %esi
404
+	shll	$12, %eax
405
+	orl	%eax, %ebp
406
+
407
+	/* Real-mode data segment virtual address => %ebx */
408
+	movl	rm_data16, %ebx
409
+.if64 ; subl	rm_virt_offset, %ebx ; .endif
372 410
 
373 411
 	/* Load protected-mode global descriptor table */
374 412
 	data32 lgdt gdtr
@@ -407,15 +445,20 @@ r2p_pmode:
407 445
 	lidt	VIRTUAL(idtr32)
408 446
 
409 447
 	/* Record real-mode %ss:sp (after removal of data) */
410
-	movw	%bp, VIRTUAL(rm_ss)
411
-	addl	%ecx, %edx
412
-	movw	%dx, VIRTUAL(rm_sp)
448
+	addl	%ecx, %ebp
449
+	movl	%ebp, VIRTUAL(rm_sp)
413 450
 
414 451
 	/* Move data from RM stack to PM stack */
452
+	subl	%edx, %esp
415 453
 	subl	%ecx, %esp
416 454
 	movl	%esp, %edi
417 455
 	rep movsb
418 456
 
457
+	/* Copy data from RM temporary buffer to PM stack */
458
+	leal	rm_tmpbuf(%ebx), %esi
459
+	movl	%edx, %ecx
460
+	rep movsb
461
+
419 462
 	/* Return to virtual address */
420 463
 	ret
421 464
 
@@ -435,6 +478,7 @@ r2p_pmode:
435 478
  *
436 479
  * Parameters: 
437 480
  *   %ecx : number of bytes to move from PM stack to RM stack
481
+ *   %edx : number of bytes to move from PM stack to RM temporary buffer
438 482
  *   %esi : real-mode global and interrupt descriptor table registers
439 483
  *
440 484
  ****************************************************************************
@@ -455,19 +499,26 @@ prot_to_real:
455 499
 	/* Add return address to data to be moved to RM stack */
456 500
 	addl	$4, %ecx
457 501
 
458
-	/* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
459
-	movzwl	VIRTUAL(rm_ss), %ebp
460
-	movzwl	VIRTUAL(rm_sp), %edx
461
-	subl	%ecx, %edx
462
-	movl	%ebp, %eax
502
+	/* Real-mode %ss:sp => %ebp and virtual address => %edi */
503
+	movl	VIRTUAL(rm_sp), %ebp
504
+	subl	%ecx, %ebp
505
+	movzwl	VIRTUAL(rm_ss), %eax
463 506
 	shll	$4, %eax
464
-	leal	(%eax,%edx), %edi
507
+	movzwl	%bp, %edi
508
+	addl	%eax, %edi
465 509
 	subl	VIRTUAL(virt_offset), %edi
466 510
 
467 511
 	/* Move data from PM stack to RM stack */
468 512
 	movl	%esp, %esi
469 513
 	rep movsb
470 514
 
515
+	/* Move data from PM stack to RM temporary buffer */
516
+	movl	VIRTUAL(data16), %edi
517
+.if64 ;	subl	VIRTUAL(virt_offset), %edi ; .endif
518
+	addl	$rm_tmpbuf, %edi
519
+	movl	%edx, %ecx
520
+	rep movsb
521
+
471 522
 	/* Record protected-mode %esp (after removal of data) */
472 523
 	movl	%esi, VIRTUAL(pm_esp)
473 524
 
@@ -497,8 +548,10 @@ p2r_ljmp_rm_cs:
497 548
 	movw	%ax, %es
498 549
 	movw	%ax, %fs
499 550
 	movw	%ax, %gs
500
-	movw	%bp, %ss
501
-	movl	%edx, %esp
551
+	movl	%ebp, %eax
552
+	shrl	$16, %eax
553
+	movw	%ax, %ss
554
+	movzwl	%bp, %esp
502 555
 
503 556
 	/* Return to real-mode address */
504 557
 	data32 ret
@@ -921,14 +974,6 @@ long_restore_regs:
921 974
  ****************************************************************************
922 975
  */
923 976
 	.struct	0
924
-VC_OFFSET_GDT:		.space	6
925
-VC_OFFSET_IDT:		.space	6
926
-.if64
927
-VC_OFFSET_PADDING64:	.space	4 /* for alignment */
928
-VC_OFFSET_CR3:		.space	4
929
-VC_OFFSET_CR4:		.space	4
930
-VC_OFFSET_EMER:		.space	8
931
-.endif
932 977
 VC_OFFSET_IX86:		.space	SIZEOF_I386_ALL_REGS
933 978
 VC_OFFSET_PADDING:	.space	2 /* for alignment */
934 979
 VC_OFFSET_RETADDR:	.space	2
@@ -941,7 +986,7 @@ VC_OFFSET_END:
941 986
 	.code16
942 987
 	.globl virt_call
943 988
 virt_call:
944
-	/* Preserve registers, flags and GDT on external RM stack */
989
+	/* Preserve registers and flags on external RM stack */
945 990
 	pushw	%ss /* padding */
946 991
 	pushfl
947 992
 	pushal
@@ -951,34 +996,38 @@ virt_call:
951 996
 	pushw	%ds
952 997
 	pushw	%ss
953 998
 	pushw	%cs
954
-	subw	$VC_OFFSET_IX86, %sp
955
-	movw	%sp, %bp
956
-	sidt	VC_OFFSET_IDT(%bp)
957
-	sgdt	VC_OFFSET_GDT(%bp)
999
+
1000
+	/* Claim ownership of temporary static buffer */
1001
+	cli
1002
+
1003
+	/* Preserve GDT and IDT in temporary static buffer */
1004
+	movw	%cs:rm_ds, %ds
1005
+	sidt	( rm_tmpbuf + VC_TMP_IDT )
1006
+	sgdt	( rm_tmpbuf + VC_TMP_GDT )
958 1007
 
959 1008
 .if64 ;	/* Preserve control registers, if applicable */
960 1009
 	movl	$MSR_EFER, %ecx
961 1010
 	rdmsr
962
-	movl	%eax, (VC_OFFSET_EMER+0)(%bp)
963
-	movl	%edx, (VC_OFFSET_EMER+4)(%bp)
1011
+	movl	%eax, ( rm_tmpbuf + VC_TMP_EMER + 0 )
1012
+	movl	%edx, ( rm_tmpbuf + VC_TMP_EMER + 4 )
964 1013
 	movl	%cr4, %eax
965
-	movl	%eax, VC_OFFSET_CR4(%bp)
1014
+	movl	%eax, ( rm_tmpbuf + VC_TMP_CR4 )
966 1015
 	movl	%cr3, %eax
967
-	movl	%eax, VC_OFFSET_CR3(%bp)
1016
+	movl	%eax, ( rm_tmpbuf + VC_TMP_CR3 )
968 1017
 .endif
969 1018
 	/* For sanity's sake, clear the direction flag as soon as possible */
970 1019
 	cld
971 1020
 
972 1021
 	/* Switch to protected mode and move register dump to PM stack */
973 1022
 	movl	$VC_OFFSET_END, %ecx
1023
+	movl	$VC_TMP_END, %edx
974 1024
 	pushl	$VIRTUAL(vc_pmode)
975 1025
 vc_jmp:	jmp	real_to_prot
976 1026
 	.section ".text.virt_call", "ax", @progbits
977 1027
 	.code32
978 1028
 vc_pmode:
979 1029
 	/* Call function (in protected mode) */
980
-	leal	VC_OFFSET_IX86(%esp), %eax
981
-	pushl	%eax
1030
+	pushl	%esp
982 1031
 	call	*(VC_OFFSET_FUNCTION+4)(%esp)
983 1032
 	popl	%eax /* discard */
984 1033
 
@@ -989,11 +1038,9 @@ vc_lmode:
989 1038
 	.code64
990 1039
 
991 1040
 	/* Call function (in long mode) */
992
-	leaq	VC_OFFSET_IX86(%rsp), %rdi
993
-	pushq	%rdi
994
-	movslq	(VC_OFFSET_FUNCTION+8)(%rsp), %rax
1041
+	movq	%rsp, %rdi
1042
+	movslq	VC_OFFSET_FUNCTION(%rsp), %rax
995 1043
 	callq	*%rax
996
-	popq	%rdi /* discard */
997 1044
 
998 1045
 	/* Switch to protected mode */
999 1046
 	call	long_to_prot
@@ -1001,7 +1048,8 @@ vc_lmode:
1001 1048
 .endif
1002 1049
 	/* Switch to real mode and move register dump back to RM stack */
1003 1050
 	movl	$VC_OFFSET_END, %ecx
1004
-	movl	%esp, %esi
1051
+	movl	$VC_TMP_END, %edx
1052
+	leal	VC_TMP_GDT(%esp, %ecx), %esi
1005 1053
 	pushl	$vc_rmode
1006 1054
 	jmp	prot_to_real
1007 1055
 	.section ".text16.virt_call", "ax", @progbits
@@ -1009,17 +1057,17 @@ vc_lmode:
1009 1057
 vc_rmode:
1010 1058
 .if64 ;	/* Restore control registers, if applicable */
1011 1059
 	movw	%sp, %bp
1012
-	movl	VC_OFFSET_CR3(%bp), %eax
1060
+	movl	( rm_tmpbuf + VC_TMP_CR3 ), %eax
1013 1061
 	movl	%eax, %cr3
1014
-	movl	VC_OFFSET_CR4(%bp), %eax
1062
+	movl	( rm_tmpbuf + VC_TMP_CR4 ), %eax
1015 1063
 	movl	%eax, %cr4
1016
-	movl	(VC_OFFSET_EMER+0)(%bp), %eax
1017
-	movl	(VC_OFFSET_EMER+4)(%bp), %edx
1064
+	movl	( rm_tmpbuf + VC_TMP_EMER + 0 ), %eax
1065
+	movl	( rm_tmpbuf + VC_TMP_EMER + 4 ), %edx
1018 1066
 	movl	$MSR_EFER, %ecx
1019 1067
 	wrmsr
1020 1068
 .endif
1021 1069
 	/* Restore registers and flags and return */
1022
-	addw	$( VC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp
1070
+	popl	%eax /* skip %cs and %ss */
1023 1071
 	popw	%ds
1024 1072
 	popw	%es
1025 1073
 	popw	%fs
@@ -1067,6 +1115,7 @@ vc_rmode:
1067 1115
 	.struct	0
1068 1116
 RC_OFFSET_REGS:		.space	SIZEOF_I386_REGS
1069 1117
 RC_OFFSET_REGS_END:
1118
+RC_OFFSET_FUNCTION_COPY:.space	4
1070 1119
 .if64
1071 1120
 RC_OFFSET_LREGS:	.space	SIZEOF_X86_64_REGS
1072 1121
 RC_OFFSET_LREG_RETADDR:	.space	SIZEOF_ADDR
@@ -1087,11 +1136,12 @@ real_call:
1087 1136
 	.code32
1088 1137
 .endif
1089 1138
 	/* Create register dump and function pointer copy on PM stack */
1139
+	pushl	( RC_OFFSET_FUNCTION - RC_OFFSET_FUNCTION_COPY - 4 )(%esp)
1090 1140
 	pushal
1091
-	pushl	RC_OFFSET_FUNCTION(%esp)
1092 1141
 
1093 1142
 	/* Switch to real mode and move register dump to RM stack  */
1094
-	movl	$( RC_OFFSET_REGS_END + 4 /* function pointer copy */ ), %ecx
1143
+	movl	$RC_OFFSET_REGS_END, %ecx
1144
+	movl	$RC_TMP_END, %edx
1095 1145
 	pushl	$rc_rmode
1096 1146
 	movl	$VIRTUAL(rm_default_gdtr_idtr), %esi
1097 1147
 	jmp	prot_to_real
@@ -1099,9 +1149,8 @@ real_call:
1099 1149
 	.code16
1100 1150
 rc_rmode:
1101 1151
 	/* Call real-mode function */
1102
-	popl	rc_function
1103 1152
 	popal
1104
-	call	*rc_function
1153
+	call	*( rm_tmpbuf + RC_TMP_FUNCTION )
1105 1154
 	pushal
1106 1155
 
1107 1156
 	/* For sanity's sake, clear the direction flag as soon as possible */
@@ -1109,6 +1158,7 @@ rc_rmode:
1109 1158
 
1110 1159
 	/* Switch to protected mode and move register dump back to PM stack */
1111 1160
 	movl	$RC_OFFSET_REGS_END, %ecx
1161
+	xorl	%edx, %edx
1112 1162
 	pushl	$VIRTUAL(rc_pmode)
1113 1163
 	jmp	real_to_prot
1114 1164
 	.section ".text.real_call", "ax", @progbits
@@ -1126,12 +1176,6 @@ rc_pmode:
1126 1176
 	ret	$( RC_OFFSET_END - RC_OFFSET_PARAMS )
1127 1177
 
1128 1178
 
1129
-	/* Function vector, used because "call xx(%sp)" is not a valid
1130
-	 * 16-bit expression.
1131
-	 */
1132
-	.section ".bss16.rc_function", "aw", @nobits
1133
-rc_function:	.word 0, 0
1134
-
1135 1179
 	/* Default real-mode global and interrupt descriptor table registers */
1136 1180
 	.section ".data.rm_default_gdtr_idtr", "aw", @progbits
1137 1181
 rm_default_gdtr_idtr:

正在加载...
取消
保存