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>
; 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
;
highmemsize:
push es
+ pushfd
pushad
push cs
%endif
mov [HighMemSize],eax
popad
+ popfd
pop es
ret ; Done!
mov ax,[cs:BIOS_timer]
mov [cs:IdleTimer],ax
pop ax
+ sti ; Guard against BIOS/PXE brokenness...
ret
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
.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
%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
pop ax
cmp ax,[BIOS_timer] ; Has the timer advanced?
je .loop
- call do_idle
dec dword [ThisKbdTo]
jz .timeout