176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; -----------------------------------------------------------------------
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   Copyright 2009 Intel Corporation; author: H. Peter Anvin
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   This program is free software; you can redistribute it and/or modify
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   it under the terms of the GNU General Public License as published by
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   Boston MA 02111-1307, USA; either version 2 of the License, or
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;   (at your option) any later version; incorporated herein by reference.
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; -----------------------------------------------------------------------
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; pm.inc
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; Functions to enter and exit 32-bit protected mode, handle interrupts
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; and cross-mode calls.
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;; PM refers to 32-bit flat protected mode; RM to 16-bit real mode.
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;;
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; _pm_call: call PM routine in low memory from RM
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	on stack	= PM routine to call (a 32-bit address)
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	ECX, ESI, EDI passed to the called function;
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	EAX = EBP in the called function points to the stack frame
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	which includes all registers (which can be changed if desired.)
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	All registers and the flags saved/restored
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	This routine is invoked by the pm_call macro.
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman_pm_call:
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushfd
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushad
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push ds
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push es
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push fs
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push gs
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov bp,sp
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ax,cs
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ebx,.pm
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ds,ax
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp enter_pm
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 32
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .textnr
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.pm:
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; EAX points to the top of the RM stack, which is EFLAGS
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		test RM_FLAGSH,02h		; RM EFLAGS.IF
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jz .no_sti
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		sti
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.no_sti:
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call [ebp+4*2+9*4+2]		; Entrypoint on RM stack
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov bx,.rm
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp enter_rm
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.rm:
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop gs
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop fs
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop es
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop ds
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		popad
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		popfd
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ret 4		; Drop entrypoint
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; enter_pm: Go to PM with interrupt service configured
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	EBX	  = PM entry point
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	EAX = EBP = on exit, points to the RM stack as a 32-bit value
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	ECX, EDX, ESI, EDI preserved across this routine
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	Assumes CS == DS
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; This routine doesn't enable interrupts, but the target routine
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; can enable interrupts by executing STI.
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanenter_pm:
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cli
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor eax,eax
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ds,ax
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ax,ss
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [RealModeSSSP],sp
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [RealModeSSSP+2],ax
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		movzx ebp,sp
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		shl eax,4
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add ebp,eax		; EBP -> top of real-mode stack
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cld
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call enable_a20
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.a20ok:
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov byte [bcopy_gdt.TSS+5],89h	; Mark TSS unbusy
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		lgdt [bcopy_gdt]	; We can use the same GDT just fine
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		lidt [PM_IDT_ptr]	; Set up the IDT
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov eax,cr0
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		or al,1
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cr0,eax		; Enter protected mode
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp PM_CS32:.in_pm
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 32
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .textnr
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.in_pm:
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor eax,eax		; Available for future use...
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov fs,eax
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov gs,eax
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		lldt ax
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov al,PM_DS32		; Set up data segments
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov es,eax
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ds,eax
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ss,eax
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov al,PM_TSS		; Be nice to Intel's VT by
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ltr ax			; giving it a valid TR
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov esp,[PMESP]		; Load protmode %esp
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov eax,ebp		; EAX -> top of real-mode stack
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp ebx			; Go to where we need to go
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; enter_rm: Return to RM from PM
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	BX	= RM entry point (CS = 0)
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	ECX, EDX, ESI, EDI preserved across this routine
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	EAX	clobbered
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;	EBP	reserved
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; This routine doesn't enable interrupts, but the target routine
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; can enable interrupts by executing STI.
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 32
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .textnr
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanenter_rm:
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cli
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cld
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [PMESP],esp		; Save exit %esp
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp PM_CS16:.in_pm16	; Return to 16-bit mode first
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.in_pm16:
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ax,PM_DS16		; Real-mode-like segment
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov es,ax
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ds,ax
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ss,ax
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov fs,ax
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov gs,ax
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		lidt [RM_IDT_ptr]	; Real-mode IDT (rm needs no GDT)
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor dx,dx
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov eax,cr0
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		and al,~1
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cr0,eax
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp 0:.in_rm
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.in_rm:					; Back in real mode
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		lss sp,[cs:RealModeSSSP]	; Restore stack
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		movzx esp,sp		; Make sure the high bits are zero
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ds,dx		; Set up sane segments
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov es,dx
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov fs,dx
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov gs,dx
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp bx			; Go to whereever we need to go...
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .data16
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		alignz 4
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		extern __stack_end
17976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPMESP		dd __stack_end		; Protected-mode ESP
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPM_IDT_ptr:	dw 8*256-1		; Length
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		dd IDT			; Offset
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; This is invoked on getting an interrupt in protected mode.  At
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; this point, we need to context-switch to real mode and invoke
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; the interrupt routine.
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; When this gets invoked, the registers are saved on the stack and
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; AL contains the register number.
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 32
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .textnr
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanpm_irq:
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushad
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		movzx esi,byte [esp+8*4] ; Interrupt number
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		inc dword [CallbackCtr]
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ebx,.rm
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp enter_rm		; Go to real mode
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.rm:
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushf			; Flags on stack
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call far [cs:esi*4]	; Call IVT entry
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ebx,.pm
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp enter_pm		; Go back to PM
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 32
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .textnr
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.pm:
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		dec dword [CallbackCtr]
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz .skip
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call [core_pm_hook]
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.skip:
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		popad
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add esp,4		; Drop interrupt number
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		iretd
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Initially, the core_pm_hook does nothing; it is available for the
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; threaded derivatives to run the scheduler, or examine the result from
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; interrupt routines.
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		global core_pm_null_hook
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmancore_pm_null_hook:
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ret
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .data16
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		alignz 4
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		global core_pm_hook
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmancore_pm_hook:	dd core_pm_null_hook
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		bits 16
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Routines to enable and disable (yuck) A20.  These routines are gathered
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; from tips from a couple of sources, including the Linux kernel and
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; http://www.x86.org/.  The need for the delay to be as large as given here
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; is indicated by Donnie Barnes of RedHat, the problematic system being an
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; IBM ThinkPad 760EL.
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .data16
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		alignz 2
24676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanA20Ptr		dw a20_dunno
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .bss16
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		alignb 4
25076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanA20Test		resd 1			; Counter for testing A20 status
25176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanA20Tries	resb 1			; Times until giving up on A20
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanenable_a20:
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushad
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov byte [cs:A20Tries],255 ; Times to try to make this work
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmantry_enable_a20:
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; First, see if we are on a system with no A20 gate, or the A20 gate
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; is already enabled for us...
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_none:
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call a20_test
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_done
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; Otherwise, see if we had something memorized...
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp word [cs:A20Ptr]
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Next, try the BIOS (INT 15h AX=2401h)
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_dunno:
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_bios:
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov word [cs:A20Ptr], a20_bios
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov ax,2401h
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pushf				; Some BIOSes muck with IF
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		int 15h
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		popf
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call a20_test
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_done
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Enable the keyboard controller A20 gate
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_kbc:
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov dl, 1			; Allow early exit
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call empty_8042
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_done			; A20 live, no need to use KBC
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov word [cs:A20Ptr], a20_kbc	; Starting KBC command sequence
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov al,0D1h			; Write output port
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		out 064h, al
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call empty_8042_uncond
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov al,0DFh			; A20 on
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		out 060h, al
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call empty_8042_uncond
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; Apparently the UHCI spec assumes that A20 toggle
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; ends with a null command (assumed to be for sychronization?)
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; Put it here to see if it helps anything...
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov al,0FFh			; Null command
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		out 064h, al
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call empty_8042_uncond
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; Verify that A20 actually is enabled.  Do that by
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; observing a word in low memory and the same word in
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; the HMA until they are no longer coherent.  Note that
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; we don't do the same check in the disable case, because
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; we don't want to *require* A20 masking (SYSLINUX should
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; work fine without it, if the BIOS does.)
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.kbc_wait:	push cx
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor cx,cx
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.kbc_wait_loop:
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call a20_test
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_done_pop
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		loop .kbc_wait_loop
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop cx
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Running out of options here.  Final attempt: enable the "fast A20 gate"
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_fast:
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov word [cs:A20Ptr], a20_fast
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		in al, 092h
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		or al,02h
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		and al,~01h			; Don't accidentally reset the machine!
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		out 092h, al
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.fast_wait:	push cx
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor cx,cx
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.fast_wait_loop:
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call a20_test
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_done_pop
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		loop .fast_wait_loop
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop cx
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; and report failure to the user.
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		dec byte [cs:A20Tries]
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz a20_dunno		; Did we get the wrong type?
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov si, err_a20
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pm_call pm_writestr
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp kaboom
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .data16
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr_a20		db CR, LF, 'A20 gate not responding!', CR, LF, 0
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; A20 unmasked, proceed...
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_done_pop:	pop cx
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_done:	popad
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ret
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; This routine tests if A20 is enabled (ZF = 0).  This routine
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; must not destroy any register contents.
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; The no-write early out avoids the io_delay in the (presumably common)
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; case of A20 already enabled (e.g. from a previous call.)
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana20_test:
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push es
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push cx
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push eax
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cx,0FFFFh			; HMA = segment 0FFFFh
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov es,cx
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov eax,[cs:A20Test]
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cx,32			; Loop count
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp .test			; First iteration = early out
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.wait:		add eax,0x430aea41		; A large prime number
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [cs:A20Test],eax
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		io_delay			; Serialize, and fix delay
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.test:		cmp eax,[es:A20Test+10h]
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		loopz .wait
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.done:		pop eax
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop cx
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop es
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ret
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; Routine to empty the 8042 KBC controller.  If dl != 0
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; then we will test A20 in the loop and exit if A20 is
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; suddenly enabled.
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanempty_8042_uncond:
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor dl,dl
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanempty_8042:
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		call a20_test
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jz .a20_on
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		and dl,dl
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz .done
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.a20_on:	io_delay
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		in al, 064h		; Status port
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		test al,1
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jz .no_output
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		io_delay
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		in al, 060h		; Read input
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jmp short empty_8042
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.no_output:
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		test al,2
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		jnz empty_8042
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		io_delay
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.done:		ret
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman; This initializes the protected-mode interrupt thunk set
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman;
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanpm_init:
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		xor edi,edi
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov bx,IDT
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov di,IRQStubs
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov eax,7aeb006ah	; push byte .. jmp short ..
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cx,8		; 8 groups of 32 IRQs
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.gloop:
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		push cx
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov cx,32		; 32 entries per group
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman.eloop:
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [bx],di		; IDT offset [15:0]
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov word [bx+2],PM_CS32	; IDT segment
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov dword [bx+4],08e00h	; IDT offset [31:16], 32-bit interrupt
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					; gate, CPL 0 (we don't have a TSS
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					; set up...)
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add bx,8
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		stosd
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; Increment IRQ, decrement jmp short offset
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add eax,(-4 << 24)+(1 << 8)
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		loop .eloop
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; At the end of each group, replace the EBxx with
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; the final E9xxxxxxxx
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add di,3
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov byte [di-5],0E9h	; JMP NEAR
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov edx,pm_irq
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		sub edx,edi
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mov [di-4],edx
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		add eax,(0x80 << 24)	; Proper offset for the next one
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pop cx
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		loop .gloop
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ret
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; pm_init is called before bss clearing, so put these
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		; in .earlybss!
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .earlybss
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		alignb 8
46276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanIDT:		resq 256
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		global RealModeSSSP
46476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanRealModeSSSP	resd 1			; Real-mode SS:SP
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .gentextnr	; Autogenerated 32-bit code
46776d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanIRQStubs:	resb 4*256+3*8
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		section .text16
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman%include "callback.inc"			; Real-mode callbacks
472