Browse Source

[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 years ago
parent
commit
2d42d3cff6
1 changed files with 103 additions and 59 deletions
  1. 103
    59
      src/arch/x86/transitions/librm.S

+ 103
- 59
src/arch/x86/transitions/librm.S View File

180
  * to us.
180
  * to us.
181
  ****************************************************************************
181
  ****************************************************************************
182
  */
182
  */
183
-	.section ".bss.rm_sp", "aw", @nobits
183
+	.section ".bss.rm_ss_sp", "aw", @nobits
184
 	.globl rm_sp
184
 	.globl rm_sp
185
 rm_sp:	.word 0
185
 rm_sp:	.word 0
186
-
187
-	.section ".bss.rm_ss", "aw", @nobits
188
 	.globl rm_ss
186
 	.globl rm_ss
189
 rm_ss:	.word 0
187
 rm_ss:	.word 0
190
 
188
 
191
 	.section ".data.pm_esp", "aw", @progbits
189
 	.section ".data.pm_esp", "aw", @progbits
192
 pm_esp:	.long VIRTUAL(_estack)
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
  * Virtual address offsets
227
  * Virtual address offsets
196
  *
228
  *
341
  *
373
  *
342
  * Parameters: 
374
  * Parameters: 
343
  *   %ecx : number of bytes to move from RM stack to PM stack
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
 	/* Add protected-mode return address to length of data to be copied */
394
 	/* Add protected-mode return address to length of data to be copied */
362
 	addw	$4, %cx /* %ecx must be less than 64kB anyway */
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
 	shll	$4, %eax
400
 	shll	$4, %eax
370
-	addr32 leal (%eax,%edx), %esi
401
+	movzwl	%sp, %ebp
402
+	addr32 leal (%eax,%ebp), %esi
371
 	subl	rm_virt_offset, %esi
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
 	/* Load protected-mode global descriptor table */
411
 	/* Load protected-mode global descriptor table */
374
 	data32 lgdt gdtr
412
 	data32 lgdt gdtr
407
 	lidt	VIRTUAL(idtr32)
445
 	lidt	VIRTUAL(idtr32)
408
 
446
 
409
 	/* Record real-mode %ss:sp (after removal of data) */
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
 	/* Move data from RM stack to PM stack */
451
 	/* Move data from RM stack to PM stack */
452
+	subl	%edx, %esp
415
 	subl	%ecx, %esp
453
 	subl	%ecx, %esp
416
 	movl	%esp, %edi
454
 	movl	%esp, %edi
417
 	rep movsb
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
 	/* Return to virtual address */
462
 	/* Return to virtual address */
420
 	ret
463
 	ret
421
 
464
 
435
  *
478
  *
436
  * Parameters: 
479
  * Parameters: 
437
  *   %ecx : number of bytes to move from PM stack to RM stack
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
  *   %esi : real-mode global and interrupt descriptor table registers
482
  *   %esi : real-mode global and interrupt descriptor table registers
439
  *
483
  *
440
  ****************************************************************************
484
  ****************************************************************************
455
 	/* Add return address to data to be moved to RM stack */
499
 	/* Add return address to data to be moved to RM stack */
456
 	addl	$4, %ecx
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
 	shll	$4, %eax
506
 	shll	$4, %eax
464
-	leal	(%eax,%edx), %edi
507
+	movzwl	%bp, %edi
508
+	addl	%eax, %edi
465
 	subl	VIRTUAL(virt_offset), %edi
509
 	subl	VIRTUAL(virt_offset), %edi
466
 
510
 
467
 	/* Move data from PM stack to RM stack */
511
 	/* Move data from PM stack to RM stack */
468
 	movl	%esp, %esi
512
 	movl	%esp, %esi
469
 	rep movsb
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
 	/* Record protected-mode %esp (after removal of data) */
522
 	/* Record protected-mode %esp (after removal of data) */
472
 	movl	%esi, VIRTUAL(pm_esp)
523
 	movl	%esi, VIRTUAL(pm_esp)
473
 
524
 
497
 	movw	%ax, %es
548
 	movw	%ax, %es
498
 	movw	%ax, %fs
549
 	movw	%ax, %fs
499
 	movw	%ax, %gs
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
 	/* Return to real-mode address */
556
 	/* Return to real-mode address */
504
 	data32 ret
557
 	data32 ret
921
  ****************************************************************************
974
  ****************************************************************************
922
  */
975
  */
923
 	.struct	0
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
 VC_OFFSET_IX86:		.space	SIZEOF_I386_ALL_REGS
977
 VC_OFFSET_IX86:		.space	SIZEOF_I386_ALL_REGS
933
 VC_OFFSET_PADDING:	.space	2 /* for alignment */
978
 VC_OFFSET_PADDING:	.space	2 /* for alignment */
934
 VC_OFFSET_RETADDR:	.space	2
979
 VC_OFFSET_RETADDR:	.space	2
941
 	.code16
986
 	.code16
942
 	.globl virt_call
987
 	.globl virt_call
943
 virt_call:
988
 virt_call:
944
-	/* Preserve registers, flags and GDT on external RM stack */
989
+	/* Preserve registers and flags on external RM stack */
945
 	pushw	%ss /* padding */
990
 	pushw	%ss /* padding */
946
 	pushfl
991
 	pushfl
947
 	pushal
992
 	pushal
951
 	pushw	%ds
996
 	pushw	%ds
952
 	pushw	%ss
997
 	pushw	%ss
953
 	pushw	%cs
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
 .if64 ;	/* Preserve control registers, if applicable */
1008
 .if64 ;	/* Preserve control registers, if applicable */
960
 	movl	$MSR_EFER, %ecx
1009
 	movl	$MSR_EFER, %ecx
961
 	rdmsr
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
 	movl	%cr4, %eax
1013
 	movl	%cr4, %eax
965
-	movl	%eax, VC_OFFSET_CR4(%bp)
1014
+	movl	%eax, ( rm_tmpbuf + VC_TMP_CR4 )
966
 	movl	%cr3, %eax
1015
 	movl	%cr3, %eax
967
-	movl	%eax, VC_OFFSET_CR3(%bp)
1016
+	movl	%eax, ( rm_tmpbuf + VC_TMP_CR3 )
968
 .endif
1017
 .endif
969
 	/* For sanity's sake, clear the direction flag as soon as possible */
1018
 	/* For sanity's sake, clear the direction flag as soon as possible */
970
 	cld
1019
 	cld
971
 
1020
 
972
 	/* Switch to protected mode and move register dump to PM stack */
1021
 	/* Switch to protected mode and move register dump to PM stack */
973
 	movl	$VC_OFFSET_END, %ecx
1022
 	movl	$VC_OFFSET_END, %ecx
1023
+	movl	$VC_TMP_END, %edx
974
 	pushl	$VIRTUAL(vc_pmode)
1024
 	pushl	$VIRTUAL(vc_pmode)
975
 vc_jmp:	jmp	real_to_prot
1025
 vc_jmp:	jmp	real_to_prot
976
 	.section ".text.virt_call", "ax", @progbits
1026
 	.section ".text.virt_call", "ax", @progbits
977
 	.code32
1027
 	.code32
978
 vc_pmode:
1028
 vc_pmode:
979
 	/* Call function (in protected mode) */
1029
 	/* Call function (in protected mode) */
980
-	leal	VC_OFFSET_IX86(%esp), %eax
981
-	pushl	%eax
1030
+	pushl	%esp
982
 	call	*(VC_OFFSET_FUNCTION+4)(%esp)
1031
 	call	*(VC_OFFSET_FUNCTION+4)(%esp)
983
 	popl	%eax /* discard */
1032
 	popl	%eax /* discard */
984
 
1033
 
989
 	.code64
1038
 	.code64
990
 
1039
 
991
 	/* Call function (in long mode) */
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
 	callq	*%rax
1043
 	callq	*%rax
996
-	popq	%rdi /* discard */
997
 
1044
 
998
 	/* Switch to protected mode */
1045
 	/* Switch to protected mode */
999
 	call	long_to_prot
1046
 	call	long_to_prot
1001
 .endif
1048
 .endif
1002
 	/* Switch to real mode and move register dump back to RM stack */
1049
 	/* Switch to real mode and move register dump back to RM stack */
1003
 	movl	$VC_OFFSET_END, %ecx
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
 	pushl	$vc_rmode
1053
 	pushl	$vc_rmode
1006
 	jmp	prot_to_real
1054
 	jmp	prot_to_real
1007
 	.section ".text16.virt_call", "ax", @progbits
1055
 	.section ".text16.virt_call", "ax", @progbits
1009
 vc_rmode:
1057
 vc_rmode:
1010
 .if64 ;	/* Restore control registers, if applicable */
1058
 .if64 ;	/* Restore control registers, if applicable */
1011
 	movw	%sp, %bp
1059
 	movw	%sp, %bp
1012
-	movl	VC_OFFSET_CR3(%bp), %eax
1060
+	movl	( rm_tmpbuf + VC_TMP_CR3 ), %eax
1013
 	movl	%eax, %cr3
1061
 	movl	%eax, %cr3
1014
-	movl	VC_OFFSET_CR4(%bp), %eax
1062
+	movl	( rm_tmpbuf + VC_TMP_CR4 ), %eax
1015
 	movl	%eax, %cr4
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
 	movl	$MSR_EFER, %ecx
1066
 	movl	$MSR_EFER, %ecx
1019
 	wrmsr
1067
 	wrmsr
1020
 .endif
1068
 .endif
1021
 	/* Restore registers and flags and return */
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
 	popw	%ds
1071
 	popw	%ds
1024
 	popw	%es
1072
 	popw	%es
1025
 	popw	%fs
1073
 	popw	%fs
1067
 	.struct	0
1115
 	.struct	0
1068
 RC_OFFSET_REGS:		.space	SIZEOF_I386_REGS
1116
 RC_OFFSET_REGS:		.space	SIZEOF_I386_REGS
1069
 RC_OFFSET_REGS_END:
1117
 RC_OFFSET_REGS_END:
1118
+RC_OFFSET_FUNCTION_COPY:.space	4
1070
 .if64
1119
 .if64
1071
 RC_OFFSET_LREGS:	.space	SIZEOF_X86_64_REGS
1120
 RC_OFFSET_LREGS:	.space	SIZEOF_X86_64_REGS
1072
 RC_OFFSET_LREG_RETADDR:	.space	SIZEOF_ADDR
1121
 RC_OFFSET_LREG_RETADDR:	.space	SIZEOF_ADDR
1087
 	.code32
1136
 	.code32
1088
 .endif
1137
 .endif
1089
 	/* Create register dump and function pointer copy on PM stack */
1138
 	/* Create register dump and function pointer copy on PM stack */
1139
+	pushl	( RC_OFFSET_FUNCTION - RC_OFFSET_FUNCTION_COPY - 4 )(%esp)
1090
 	pushal
1140
 	pushal
1091
-	pushl	RC_OFFSET_FUNCTION(%esp)
1092
 
1141
 
1093
 	/* Switch to real mode and move register dump to RM stack  */
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
 	pushl	$rc_rmode
1145
 	pushl	$rc_rmode
1096
 	movl	$VIRTUAL(rm_default_gdtr_idtr), %esi
1146
 	movl	$VIRTUAL(rm_default_gdtr_idtr), %esi
1097
 	jmp	prot_to_real
1147
 	jmp	prot_to_real
1099
 	.code16
1149
 	.code16
1100
 rc_rmode:
1150
 rc_rmode:
1101
 	/* Call real-mode function */
1151
 	/* Call real-mode function */
1102
-	popl	rc_function
1103
 	popal
1152
 	popal
1104
-	call	*rc_function
1153
+	call	*( rm_tmpbuf + RC_TMP_FUNCTION )
1105
 	pushal
1154
 	pushal
1106
 
1155
 
1107
 	/* For sanity's sake, clear the direction flag as soon as possible */
1156
 	/* For sanity's sake, clear the direction flag as soon as possible */
1109
 
1158
 
1110
 	/* Switch to protected mode and move register dump back to PM stack */
1159
 	/* Switch to protected mode and move register dump back to PM stack */
1111
 	movl	$RC_OFFSET_REGS_END, %ecx
1160
 	movl	$RC_OFFSET_REGS_END, %ecx
1161
+	xorl	%edx, %edx
1112
 	pushl	$VIRTUAL(rc_pmode)
1162
 	pushl	$VIRTUAL(rc_pmode)
1113
 	jmp	real_to_prot
1163
 	jmp	real_to_prot
1114
 	.section ".text.real_call", "ax", @progbits
1164
 	.section ".text.real_call", "ax", @progbits
1126
 	ret	$( RC_OFFSET_END - RC_OFFSET_PARAMS )
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
 	/* Default real-mode global and interrupt descriptor table registers */
1179
 	/* Default real-mode global and interrupt descriptor table registers */
1136
 	.section ".data.rm_default_gdtr_idtr", "aw", @progbits
1180
 	.section ".data.rm_default_gdtr_idtr", "aw", @progbits
1137
 rm_default_gdtr_idtr:
1181
 rm_default_gdtr_idtr:

Loading…
Cancel
Save