--- arch/i386/boot/setup.S.12p6	Wed Dec  6 12:49:07 2000
+++ arch/i386/boot/setup.S	Wed Dec  6 15:25:01 2000
@@ -532,6 +532,70 @@
 	movl	%cs:code32_start, %eax
 	movl	%eax, %cs:code32
 
+# Make sure interrupts really are disabled here...
+	cli
+
+#
+# That was painless, now we enable A20, which definitely isn't.
+#
+# First, we try the BIOS (INT 15:2401).
+# Second, try the keyboard controller with a timeout.
+# Third, try the "fast A20 gate" manually.
+#
+# The "fast A20 gate" is dangerous to use manually, because of
+# system and BIOS bugs -- some manufacturers have used it as an
+# extra GPIO pin(!!!!) and some BIOSes fail to save/restore it
+# on Suspend/Wakeup.
+#
+	movw	$0x2401,%ax			# BIOS: Enable A20
+	stc
+	int	$0x15
+	jc	a20_no_bios			# CF=0 if success
+	testb	%ah,%ah				# AH=0 if success
+	jnz	a20_no_bios
+
+	# BIOS reported success, verify that it really did work
+	call	a20_test
+	jnz	a20_done
+
+a20_no_bios:					# Try the KBC next...
+	call	empty_8042
+
+	movb	$0xD1, %al			# command write
+	outb	%al, $0x64
+	call	empty_8042
+
+	movb	$0xDF, %al			# A20 on
+	outb	%al, $0x60
+	call	empty_8042
+#
+# If A20 is enabled here, don't touch port 92
+#
+	call	a20_test
+	jnz	a20_done
+	
+#
+# Either the KBC is really slow, or we need to use fast A20.  Flip
+# fast A20 and then sit in a loop and spin waiting for A20 to come alive.
+#
+# You must preserve the other bits here. Otherwise embarrasing things
+# like laptops powering off on boot happen. Corrected version by Kira
+# Brown from Linux 2.2
+#
+	inb	$0x92, %al			# System Control Port A
+	orb	$02, %al			# Fast A20 enable
+	outb	%al, $0x92
+
+# Wait until a20 really *is* enabled; it can take a fair amount of
+# time on certain systems; Toshiba Tecras are known to have this
+# problem.
+
+a20_wait:
+	call	a20_test
+	jz	a20_wait
+	
+a20_done:	# A20 verified enabled here
+
 # Now we move the system to its rightful place ... but we check if we have a
 # big-kernel. In that case we *must* not move it ...
 	testb	$LOADED_HIGH, %cs:loadflags
@@ -581,6 +645,7 @@
 # We also then need to move the params behind it (commandline)
 # Because we would overwrite the code on the current IP, we move
 # it in two steps, jumping high after the first one.
+
 	movw	%cs, %ax
 	cmpw	$SETUPSEG, %ax
 	je	end_move_self
@@ -621,6 +686,9 @@
 	movw	%ax, %ds
 	movw	%dx, %ss
 end_move_self:					# now we are at the right place
+
+# Set up IDT/GDT for protected-mode use.
+
 	lidt	idt_48				# load idt with 0,0
 	xorl	%eax, %eax			# Compute gdt_base
 	movw	%ds, %ax			# (Convert %ds:gdt to a linear ptr)
@@ -630,41 +698,6 @@
 	lgdt	gdt_48				# load gdt with whatever is
 						# appropriate
 
-# that was painless, now we enable a20
-	call	empty_8042
-
-	movb	$0xD1, %al			# command write
-	outb	%al, $0x64
-	call	empty_8042
-
-	movb	$0xDF, %al			# A20 on
-	outb	%al, $0x60
-	call	empty_8042
-
-#
-#	You must preserve the other bits here. Otherwise embarrasing things
-#	like laptops powering off on boot happen. Corrected version by Kira
-#	Brown from Linux 2.2
-#
-	inb	$0x92, %al			# 
-	orb	$02, %al			# "fast A20" version
-	outb	%al, $0x92			# some chips have only this
-
-# wait until a20 really *is* enabled; it can take a fair amount of
-# time on certain systems; Toshiba Tecras are known to have this
-# problem.  The memory location used here (0x200) is the int 0x80
-# vector, which should be safe to use.
-
-	xorw	%ax, %ax			# segment 0x0000
-	movw	%ax, %fs
-	decw	%ax				# segment 0xffff (HMA)
-	movw	%ax, %gs
-a20_wait:
-	incw	%ax				# unused memory location <0xfff0
-	movw	%ax, %fs:(0x200)		# we use the "int 0x80" vector
-	cmpw	%gs:(0x210), %ax		# and its corresponding HMA addr
-	je	a20_wait			# loop until no longer aliased
-
 # make sure any possible coprocessor is properly reset..
 	xorw	%ax, %ax
 	outb	%al, $0xf0
@@ -859,6 +892,36 @@
 	popl	%ecx
 	ret
 
+# Wait for the A20 gate to become enabled.  Time out reasonably quickly;
+# return with ZF=0 is A20 now live; ZF=1 if A20 still masked.
+# The test location, memory address 0x200, is the INT 0x80 vector which
+# should be safe to use.
+
+a20_test:
+	pushw	%fs
+	pushw	%gs
+	pushw	%ax
+	pushw	%cx
+	xorw	%ax,%ax				# Low 64K
+	movw	%ax,%fs
+	decw	%ax				# HMA = segment 0xFFFF
+	movw	%ax,%gs
+	movw	%fs:(0x200),%ax
+	pushw	%ax				# Paranoia
+	movw	$0x1000,%cx			# Loop counter
+a20_loop:
+	incw	%ax
+	movw	%ax,%fs:(0x200)
+	cmpw	%ax,%gs:(0x210)			# Aliased?
+	loope	a20_loop			# If so, continue
+	# ZF=0 if no alias, ZF=1 if timeout
+	popw	%fs:(0x200)
+	popw	%cx
+	popw	%ax
+	popw	%gs
+	popw	%fs
+	ret
+	
 # Read the cmos clock. Return the seconds in al
 gettime:
 	pushw	%cx
