1 ;; -----------------------------------------------------------------------
3 ;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
4 ;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
6 ;; This program is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 ;; Boston MA 02111-1307, USA; either version 2 of the License, or
10 ;; (at your option) any later version; incorporated herein by reference.
12 ;; -----------------------------------------------------------------------
17 ;; Functions to enter and exit 32-bit protected mode, handle interrupts
18 ;; and cross-mode calls.
20 ;; PM refers to 32-bit flat protected mode; RM to 16-bit real mode.
26 ; _pm_call: call PM routine in low memory from RM
28 ; on stack = PM routine to call (a 32-bit address)
30 ; ECX, ESI, EDI passed to the called function;
31 ; EAX = EBP in the called function points to the stack frame
32 ; which includes all registers (which can be changed if desired.)
34 ; All registers and the flags saved/restored
36 ; This routine is invoked by the pm_call macro.
54 ; EAX points to the top of the RM stack, which is EFLAGS
55 test RM_FLAGSH,02h ; RM EFLAGS.IF
59 call [ebp+4*2+9*4+2] ; Entrypoint on RM stack
72 ret 4 ; Drop entrypoint
75 ; enter_pm: Go to PM with interrupt service configured
76 ; EBX = PM entry point
77 ; EAX = EBP = on exit, points to the RM stack as a 32-bit value
78 ; ECX, EDX, ESI, EDI preserved across this routine
82 ; This routine doesn't enable interrupts, but the target routine
83 ; can enable interrupts by executing STI.
93 mov [RealModeSSSP+2],ax
96 add ebp,eax ; EBP -> top of real-mode stack
101 mov byte [bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
103 lgdt [bcopy_gdt] ; We can use the same GDT just fine
104 lidt [PM_IDT_ptr] ; Set up the IDT
107 mov cr0,eax ; Enter protected mode
113 xor eax,eax ; Available for future use...
118 mov al,PM_DS32 ; Set up data segments
123 mov al,PM_TSS ; Be nice to Intel's VT by
124 ltr ax ; giving it a valid TR
126 mov esp,[PMESP] ; Load protmode %esp
127 mov eax,ebp ; EAX -> top of real-mode stack
128 jmp ebx ; Go to where we need to go
131 ; enter_rm: Return to RM from PM
133 ; BX = RM entry point (CS = 0)
134 ; ECX, EDX, ESI, EDI preserved across this routine
138 ; This routine doesn't enable interrupts, but the target routine
139 ; can enable interrupts by executing STI.
146 mov [PMESP],esp ; Save exit %esp
147 jmp PM_CS16:.in_pm16 ; Return to 16-bit mode first
152 mov ax,PM_DS16 ; Real-mode-like segment
159 lidt [RM_IDT_ptr] ; Real-mode IDT (rm needs no GDT)
166 .in_rm: ; Back in real mode
167 lss sp,[cs:RealModeSSSP] ; Restore stack
168 movzx esp,sp ; Make sure the high bits are zero
169 mov ds,dx ; Set up sane segments
173 jmp bx ; Go to whereever we need to go...
179 PMESP dd __stack_end ; Protected-mode ESP
181 PM_IDT_ptr: dw 8*256-1 ; Length
185 ; This is invoked on getting an interrupt in protected mode. At
186 ; this point, we need to context-switch to real mode and invoke
187 ; the interrupt routine.
189 ; When this gets invoked, the registers are saved on the stack and
190 ; AL contains the register number.
196 movzx esi,byte [esp+8*4] ; Interrupt number
197 inc dword [CallbackCtr]
199 jmp enter_rm ; Go to real mode
204 pushf ; Flags on stack
205 call far [cs:esi*4] ; Call IVT entry
207 jmp enter_pm ; Go back to PM
212 dec dword [CallbackCtr]
217 add esp,4 ; Drop interrupt number
221 ; Initially, the core_pm_hook does nothing; it is available for the
222 ; threaded derivatives to run the scheduler, or examine the result from
223 ; interrupt routines.
225 global core_pm_null_hook
232 core_pm_hook: dd core_pm_null_hook
237 ; Routines to enable and disable (yuck) A20. These routines are gathered
238 ; from tips from a couple of sources, including the Linux kernel and
239 ; http://www.x86.org/. The need for the delay to be as large as given here
240 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
241 ; IBM ThinkPad 760EL.
250 A20Test resd 1 ; Counter for testing A20 status
251 A20Tries resb 1 ; Times until giving up on A20
256 mov byte [cs:A20Tries],255 ; Times to try to make this work
261 ; First, see if we are on a system with no A20 gate, or the A20 gate
262 ; is already enabled for us...
267 ; Otherwise, see if we had something memorized...
271 ; Next, try the BIOS (INT 15h AX=2401h)
275 mov word [cs:A20Ptr], a20_bios
277 pushf ; Some BIOSes muck with IF
285 ; Enable the keyboard controller A20 gate
288 mov dl, 1 ; Allow early exit
290 jnz a20_done ; A20 live, no need to use KBC
292 mov word [cs:A20Ptr], a20_kbc ; Starting KBC command sequence
294 mov al,0D1h ; Write output port
296 call empty_8042_uncond
300 call empty_8042_uncond
302 ; Apparently the UHCI spec assumes that A20 toggle
303 ; ends with a null command (assumed to be for sychronization?)
304 ; Put it here to see if it helps anything...
305 mov al,0FFh ; Null command
307 call empty_8042_uncond
309 ; Verify that A20 actually is enabled. Do that by
310 ; observing a word in low memory and the same word in
311 ; the HMA until they are no longer coherent. Note that
312 ; we don't do the same check in the disable case, because
313 ; we don't want to *require* A20 masking (SYSLINUX should
314 ; work fine without it, if the BIOS does.)
324 ; Running out of options here. Final attempt: enable the "fast A20 gate"
327 mov word [cs:A20Ptr], a20_fast
330 and al,~01h ; Don't accidentally reset the machine!
343 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
344 ; and report failure to the user.
346 dec byte [cs:A20Tries]
347 jnz a20_dunno ; Did we get the wrong type?
354 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
358 ; A20 unmasked, proceed...
365 ; This routine tests if A20 is enabled (ZF = 0). This routine
366 ; must not destroy any register contents.
368 ; The no-write early out avoids the io_delay in the (presumably common)
369 ; case of A20 already enabled (e.g. from a previous call.)
375 mov cx,0FFFFh ; HMA = segment 0FFFFh
378 mov cx,32 ; Loop count
379 jmp .test ; First iteration = early out
380 .wait: add eax,0x430aea41 ; A large prime number
382 io_delay ; Serialize, and fix delay
383 .test: cmp eax,[es:A20Test+10h]
391 ; Routine to empty the 8042 KBC controller. If dl != 0
392 ; then we will test A20 in the loop and exit if A20 is
403 in al, 064h ; Status port
407 in al, 060h ; Read input
416 ; This initializes the protected-mode interrupt thunk set
424 mov eax,7aeb006ah ; push byte .. jmp short ..
426 mov cx,8 ; 8 groups of 32 IRQs
429 mov cx,32 ; 32 entries per group
431 mov [bx],di ; IDT offset [15:0]
432 mov word [bx+2],PM_CS32 ; IDT segment
433 mov dword [bx+4],08e00h ; IDT offset [31:16], 32-bit interrupt
434 ; gate, CPL 0 (we don't have a TSS
439 ; Increment IRQ, decrement jmp short offset
440 add eax,(-4 << 24)+(1 << 8)
444 ; At the end of each group, replace the EBxx with
445 ; the final E9xxxxxxxx
447 mov byte [di-5],0E9h ; JMP NEAR
452 add eax,(0x80 << 24) ; Proper offset for the next one
458 ; pm_init is called before bss clearing, so put these
464 RealModeSSSP resd 1 ; Real-mode SS:SP
466 section .gentextnr ; Autogenerated 32-bit code
467 IRQStubs: resb 4*256+3*8
471 %include "callback.inc" ; Real-mode callbacks