pxelinux: workaround for EFI CSM bug - BEV stack overwrite on localboot
authorH. Peter Anvin <hpa@zytor.com>
Fri, 4 Sep 2009 22:09:50 +0000 (15:09 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 4 Sep 2009 22:09:50 +0000 (15:09 -0700)
On at least some machines with an EFI-based BIOS, the Compatibility
Service Module (CSM) puts the BEV stack at an address where the boot
loader is likely to overwrite it.  Implement a workaround which forces
INT 18h instead if an EFI signature is detected in memory.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
core/pxelinux.asm

index 190f4c6..b274514 100644 (file)
@@ -268,6 +268,81 @@ _start1:
                call writestr_early
 
 ;
+; Look to see if we are on an EFI CSM system.  Some EFI
+; CSM systems put the BEV stack in low memory, which means
+; a return to the PXE stack will crash the system.  However,
+; INT 18h works reliably, so in that case hack the stack and
+; point the "return address" to an INT 18h instruction.
+;
+; Hack the stack instead of the much simpler "just invoke INT 18h
+; if we want to reset", so that chainloading other NBPs will work.
+;
+efi_csm_workaround:
+               les bp,[InitStack]      ; GS:SP -> original stack
+               les bx,[es:bp+44]       ; Return address
+               cmp word [es:bx],18CDh  ; Already pointing to INT 18h?
+               je .skip
+
+               ; Search memory from E0000 to FFFFF for a $EFI structure
+               mov bx,0E000h
+.scan_mem:
+               mov es,bx
+               cmp dword [es:0],'IFE$' ; $EFI is byte-reversed...
+               jne .not_here
+               ;
+               ; Verify the table.  We don't check the checksum because
+               ; it seems some CSMs leave it at zero.
+               ;
+               movzx cx,byte [es:5]    ; Table length
+               cmp cx,83               ; 83 bytes is the current length...
+               jae .found_it
+
+.not_here:
+               inc bx
+               jnz .scan_mem
+               jmp .skip               ; No $EFI structure found
+
+               ;
+               ; Found a $EFI structure.  Move down the original stack
+               ; and put an INT 18h instruction there instead.
+               ;
+.found_it:
+%if USE_PXE_PROVIDED_STACK
+               mov cx,efi_csm_hack_size
+               mov si,sp
+               sub sp,cx
+               mov di,sp
+               mov ax,ss
+               mov es,ax
+               sub [InitStack],cx
+               sub [BaseStack],cx
+%else
+               les si,[InitStack]
+               lea di,[si-efi_csm_hack_size]
+               mov [InitStack],di
+%endif
+               lea cx,[bp+52]          ; End of the stack we care about
+               sub cx,si
+               es rep movsb
+               mov [es:di-8],di        ; Clobber the return address
+               mov [es:di-6],es
+               mov si,efi_csm_hack
+               mov cx,efi_csm_hack_size
+               rep movsb
+
+.skip: 
+
+               section .data
+               alignz 4
+efi_csm_hack:
+               int 18h
+               jmp 0F000h:0FFF0h
+               hlt
+efi_csm_hack_size equ $-efi_csm_hack
+
+               section .text
+
+;
 ; Assume API version 2.1, in case we find the !PXE structure without
 ; finding the PXENV+ structure.  This should really look at the Base
 ; Code ROM ID structure in have_pxe, but this is adequate for now --