idle: handle PXE stacks which improperly disable interrupts
authorH. Peter Anvin <hpa@zytor.com>
Thu, 21 May 2009 17:25:43 +0000 (10:25 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Thu, 21 May 2009 17:25:43 +0000 (10:25 -0700)
At least Etherboot (and all-but-super-recent versions of gPXE) PXE
ROMs improperly disable interrupts when calling an intercepted version
of INT 15h and 1Ah; this is due to the old trick of using "ret 2" to
return... this avoids resetting the flags for status, but it also
doesn't restore the value of the interrupt flag.  Needless to say,
this causes serious issues.

Work around it by adding explicit pushf/popf or STI in places known to
have issues, but also add an STI in reset_idle, and add an error alert
in do_idle if we ever get called with interrupts disabled.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
core/conio.inc
core/highmem.inc
core/idle.inc
core/pxelinux.asm
core/ui.inc

index 2d8c61a..4488c94 100644 (file)
@@ -286,8 +286,8 @@ write_serial_str:
 ; pollchar: check if we have an input character pending (ZF = 0)
 ;
 pollchar:
-               pushad
                call do_idle
+               pushad
                mov ah,11h              ; Poll keyboard
                int 16h
                jnz .done               ; Keyboard response
index 5ae3abe..c7e602f 100644 (file)
@@ -25,6 +25,7 @@
 ;
 highmemsize:
                push es
+               pushfd
                pushad
 
                push cs
@@ -152,6 +153,7 @@ got_highmem:
 %endif
                mov [HighMemSize],eax
                popad
+               popfd
                pop es
                ret                             ; Done!
 
index c2ce134..67a5c3e 100644 (file)
@@ -19,6 +19,7 @@ reset_idle:
                mov ax,[cs:BIOS_timer]
                mov [cs:IdleTimer],ax
                pop ax
+               sti             ; Guard against BIOS/PXE brokenness...
                ret
 
 do_idle:
@@ -28,6 +29,31 @@ do_idle:
                mov ax,cs
                mov ds,ax
                mov es,ax
+               pushf
+               pop ax
+               test ah,2
+               jnz .ok
+               push si
+               push cx
+               mov si,hlt_err
+               call writestr
+               mov si,sp
+               add si,10
+               mov cx,16
+.errloop:
+               ss lodsw
+               call writehex4
+               dec cx
+               jz .endloop
+               mov al,' '
+               call writechr
+               jmp .errloop
+.endloop:
+               call crlf
+               pop cx
+               pop si
+               sti
+.ok:
                mov ax,[BIOS_timer]
                sub ax,[IdleTimer]
                cmp ax,TICKS_TO_IDLE
@@ -43,8 +69,10 @@ do_idle:
 .ret:          ret
 
                section .data
-NoHalt         dw 0
 IdleHook       dw do_idle.ret
+NoHalt         dw 1
+
+hlt_err                db 'ERROR: idle with IF=0', CR, LF, 0 
 
                section .bss
 IdleTimer      resw 1
index ad1686c..9d9d5b4 100644 (file)
@@ -308,10 +308,11 @@ _start1:
 %if USE_PXE_PROVIDED_STACK == 0
                lss sp,[InitStack]
 %endif
-               int 1Ah                                 ; May trash regs
+               int 1Ah                 ; May trash regs
 %if USE_PXE_PROVIDED_STACK == 0
                lss esp,[BaseStack]
 %endif
+               sti                     ; Work around Etherboot bug
 
                jc no_int1a
                cmp ax,564Eh
index a12233c..1b40717 100644 (file)
@@ -532,7 +532,6 @@ getchar_timeout:
                pop ax
                cmp ax,[BIOS_timer]             ; Has the timer advanced?
                je .loop
-               call do_idle
 
                dec dword [ThisKbdTo]
                jz .timeout