Browse Source

[librm] Use libflat to enable A20 line on each real-to-protected transition

Use the shared code in libflat to perform the A20 transitions
automatically on each transition from real to protected mode.  This
allows us to remove all explicit calls to gateA20_set().

The old warnings about avoiding automatically enabling A20 are
essentially redundant; they date back to the time when we would always
start hammering the keyboard controller without first checking to see
if gate A20 was already enabled (which it almost always is).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 14 years ago
parent
commit
38cd2035ff

+ 0
- 10
src/arch/i386/drivers/net/undiload.c View File

@@ -104,16 +104,6 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
104 104
 			       : "a" ( __from_data16 ( &undi_loader ) )
105 105
 			       : "ebx", "ecx", "edx", "esi", "edi", "ebp" );
106 106
 
107
-	/* UNDI API calls may rudely change the status of A20 and not
108
-	 * bother to restore it afterwards.  Intel is known to be
109
-	 * guilty of this.
110
-	 *
111
-	 * Note that we will return to this point even if A20 gets
112
-	 * screwed up by the UNDI driver, because Etherboot always
113
-	 * resides in an even megabyte of RAM.
114
-	 */	
115
-	gateA20_set();
116
-
117 107
 	if ( exit != PXENV_EXIT_SUCCESS ) {
118 108
 		/* Clear entry point */
119 109
 		memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );

+ 0
- 176
src/arch/i386/firmware/pcbios/gateA20.c View File

@@ -1,176 +0,0 @@
1
-FILE_LICENCE ( GPL2_OR_LATER );
2
-
3
-#include <stdio.h>
4
-#include <realmode.h>
5
-#include <bios.h>
6
-#include <ipxe/io.h>
7
-#include <ipxe/timer.h>
8
-
9
-#define K_RDWR		0x60		/* keyboard data & cmds (read/write) */
10
-#define K_STATUS	0x64		/* keyboard status */
11
-#define K_CMD		0x64		/* keybd ctlr command (write-only) */
12
-
13
-#define K_OBUF_FUL	0x01		/* output buffer full */
14
-#define K_IBUF_FUL	0x02		/* input buffer full */
15
-
16
-#define KC_CMD_WIN	0xd0		/* read  output port */
17
-#define KC_CMD_WOUT	0xd1		/* write output port */
18
-#define KC_CMD_NULL	0xff		/* null command ("pulse nothing") */
19
-#define KB_SET_A20	0xdf		/* enable A20,
20
-					   enable output buffer full interrupt
21
-					   enable data line
22
-					   disable clock line */
23
-#define KB_UNSET_A20	0xdd		/* enable A20,
24
-					   enable output buffer full interrupt
25
-					   enable data line
26
-					   disable clock line */
27
-
28
-#define SCP_A		0x92		/* System Control Port A */
29
-
30
-enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
31
-	Query_A20_Support = 0x2403 };
32
-
33
-enum a20_methods {
34
-	A20_UNKNOWN = 0,
35
-	A20_INT15,
36
-	A20_KBC,
37
-	A20_SCPA,
38
-};
39
-
40
-#define A20_MAX_RETRIES		32
41
-#define A20_INT15_RETRIES	32
42
-#define A20_KBC_RETRIES		(2^21)
43
-#define A20_SCPA_RETRIES	(2^21)
44
-
45
-/**
46
- * Drain keyboard controller
47
- */
48
-static void empty_8042 ( void ) {
49
-	unsigned long time;
50
-
51
-	time = currticks() + TICKS_PER_SEC;	/* max wait of 1 second */
52
-	while ( ( inb ( K_CMD ) & ( K_IBUF_FUL | K_OBUF_FUL ) ) &&
53
-		currticks() < time ) {
54
-		iodelay();
55
-		( void ) inb_p ( K_RDWR );
56
-		iodelay();
57
-	}
58
-}
59
-
60
-/**
61
- * Fast test to see if gate A20 is already set
62
- *
63
- * @v retries		Number of times to retry before giving up
64
- * @ret set		Gate A20 is set
65
- */
66
-static int gateA20_is_set ( int retries ) {
67
-	static uint32_t test_pattern = 0xdeadbeef;
68
-	physaddr_t test_pattern_phys = virt_to_phys ( &test_pattern );
69
-	physaddr_t verify_pattern_phys = ( test_pattern_phys ^ 0x100000 );
70
-	userptr_t verify_pattern_user = phys_to_user ( verify_pattern_phys );
71
-	uint32_t verify_pattern;
72
-
73
-	do {
74
-		/* Check for difference */
75
-		copy_from_user ( &verify_pattern, verify_pattern_user, 0,
76
-				 sizeof ( verify_pattern ) );
77
-		if ( verify_pattern != test_pattern )
78
-			return 1;
79
-
80
-		/* Avoid false negatives */
81
-		test_pattern++;
82
-
83
-		iodelay();
84
-
85
-		/* Always retry at least once, to avoid false negatives */
86
-	} while ( retries-- >= 0 );
87
-
88
-	/* Pattern matched every time; gate A20 is not set */
89
-	return 0;
90
-}
91
-
92
-/*
93
- * Gate A20 for high memory
94
- *
95
- * Note that this function gets called as part of the return path from
96
- * librm's real_call, which is used to make the int15 call if librm is
97
- * being used.  To avoid an infinite recursion, we make gateA20_set
98
- * return immediately if it is already part of the call stack.
99
- */
100
-void gateA20_set ( void ) {
101
-	static char reentry_guard = 0;
102
-	static int a20_method = A20_UNKNOWN;
103
-	unsigned int discard_a;
104
-	unsigned int scp_a;
105
-	int retries = 0;
106
-
107
-	/* Avoid potential infinite recursion */
108
-	if ( reentry_guard )
109
-		return;
110
-	reentry_guard = 1;
111
-
112
-	/* Fast check to see if gate A20 is already enabled */
113
-	if ( gateA20_is_set ( 0 ) )
114
-		goto out;
115
-
116
-	for ( ; retries < A20_MAX_RETRIES ; retries++ ) {
117
-		switch ( a20_method ) {
118
-		case A20_UNKNOWN:
119
-		case A20_INT15:
120
-			/* Try INT 15 method */
121
-			__asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
122
-					       : "=a" ( discard_a )
123
-					       : "a" ( Enable_A20 ) );
124
-			if ( gateA20_is_set ( A20_INT15_RETRIES ) ) {
125
-				DBG ( "Enabled gate A20 using BIOS\n" );
126
-				a20_method = A20_INT15;
127
-				goto out;
128
-			}
129
-			/* fall through */
130
-		case A20_KBC:
131
-			/* Try keyboard controller method */
132
-			empty_8042();
133
-			outb ( KC_CMD_WOUT, K_CMD );
134
-			empty_8042();
135
-			outb ( KB_SET_A20, K_RDWR );
136
-			empty_8042();
137
-			outb ( KC_CMD_NULL, K_CMD );
138
-			empty_8042();
139
-			if ( gateA20_is_set ( A20_KBC_RETRIES ) ) {
140
-				DBG ( "Enabled gate A20 using "
141
-				      "keyboard controller\n" );
142
-				a20_method = A20_KBC;
143
-				goto out;
144
-			}
145
-			/* fall through */
146
-		case A20_SCPA:
147
-			/* Try "Fast gate A20" method */
148
-			scp_a = inb ( SCP_A );
149
-			scp_a &= ~0x01; /* Avoid triggering a reset */
150
-			scp_a |= 0x02; /* Enable A20 */
151
-			iodelay();
152
-			outb ( scp_a, SCP_A );
153
-			iodelay();
154
-			if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) {
155
-				DBG ( "Enabled gate A20 using "
156
-				      "Fast Gate A20\n" );
157
-				a20_method = A20_SCPA;
158
-				goto out;
159
-			}
160
-		}
161
-	}
162
-
163
-	/* Better to die now than corrupt memory later */
164
-	printf ( "FATAL: Gate A20 stuck\n" );
165
-	while ( 1 ) {}
166
-
167
- out:
168
-	if ( retries )
169
-		DBG ( "%d attempts were required to enable A20\n",
170
-		      ( retries + 1 ) );
171
-	reentry_guard = 0;
172
-}
173
-
174
-void gateA20_unset ( void ) {
175
-	/* Not currently implemented */
176
-}

+ 0
- 7
src/arch/i386/image/nbi.c View File

@@ -1,7 +1,6 @@
1 1
 #include <errno.h>
2 2
 #include <assert.h>
3 3
 #include <realmode.h>
4
-#include <gateA20.h>
5 4
 #include <memsizes.h>
6 5
 #include <basemem_packet.h>
7 6
 #include <ipxe/uaccess.h>
@@ -306,8 +305,6 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
306 305
 	       imgheader->execaddr.segoff.segment,
307 306
 	       imgheader->execaddr.segoff.offset );
308 307
 
309
-	gateA20_unset();
310
-
311 308
 	__asm__ __volatile__ (
312 309
 		REAL_CODE ( "pushw %%ds\n\t"	/* far pointer to bootp data */
313 310
 			    "pushw %%bx\n\t"
@@ -327,8 +324,6 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
327 324
 		  "b" ( __from_data16 ( basemem_packet ) )
328 325
 		: "ecx", "edx", "ebp" );
329 326
 
330
-	gateA20_set();
331
-
332 327
 	return rc;
333 328
 }
334 329
 
@@ -345,8 +340,6 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
345 340
 	DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
346 341
 	       image, imgheader->execaddr.linear );
347 342
 
348
-	/* no gateA20_unset for PM call */
349
-
350 343
 	/* Jump to OS with flat physical addressing */
351 344
 	__asm__ __volatile__ (
352 345
 		PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */

+ 0
- 7
src/arch/i386/include/gateA20.h View File

@@ -1,7 +0,0 @@
1
-#ifndef GATEA20_H
2
-#define GATEA20_H
3
-
4
-extern void gateA20_set ( void );
5
-extern void gateA20_unset ( void );
6
-
7
-#endif /* GATEA20_H */

+ 0
- 5
src/arch/i386/include/librm.h View File

@@ -157,11 +157,6 @@ extern uint16_t __data16 ( rm_cs );
157 157
 extern uint16_t __text16 ( rm_ds );
158 158
 #define rm_ds __use_text16 ( rm_ds )
159 159
 
160
-/* Functions that librm expects to be able to link to.  Included here
161
- * so that the compiler will catch prototype mismatches.
162
- */
163
-extern void gateA20_set ( void );
164
-
165 160
 /**
166 161
  * Convert segment:offset address to user buffer
167 162
  *

+ 0
- 10
src/arch/i386/interface/pxeparent/pxeparent.c View File

@@ -147,16 +147,6 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
147 147
 			         "D" ( __from_data16 ( &pxeparent_params ) )
148 148
 			       : "ecx", "edx", "esi", "ebp" );
149 149
 
150
-	/* PXE API calls may rudely change the status of A20 and not
151
-	 * bother to restore it afterwards.  Intel is known to be
152
-	 * guilty of this.
153
-	 *
154
-	 * Note that we will return to this point even if A20 gets
155
-	 * screwed up by the parent PXE stack, because Etherboot always
156
-	 * resides in an even megabyte of RAM.
157
-	 */
158
-	gateA20_set();
159
-
160 150
 	/* Determine return status code based on PXENV_EXIT and
161 151
 	 * PXENV_STATUS
162 152
 	 */

+ 1
- 0
src/arch/i386/transitions/libflat.S View File

@@ -348,6 +348,7 @@ enable_a20_fast:
348 348
 #define ENABLE_A20_RETRIES 255
349 349
 	.section ".text16.early", "awx", @progbits
350 350
 	.code16
351
+	.globl	enable_a20
351 352
 enable_a20:
352 353
 	/* Preserve registers */
353 354
 	pushl	%ecx

+ 11
- 12
src/arch/i386/transitions/librm.S View File

@@ -170,10 +170,18 @@ idt_init: /* Reuse the return opcode here */
170 170
 	.section ".text16", "ax", @progbits
171 171
 	.code16
172 172
 real_to_prot:
173
+	/* Enable A20 line */
174
+	call	enable_a20
175
+	/* A failure at this point is fatal, and there's nothing we
176
+	 * can do about it other than lock the machine to make the
177
+	 * problem immediately visible.
178
+	 */
179
+1:	jc	1b
180
+
173 181
 	/* Make sure we have our data segment available */
174 182
 	movw	%cs:rm_ds, %ax
175 183
 	movw	%ax, %ds
176
-	
184
+
177 185
 	/* Add _virt_offset, _text16 and _data16 to stack to be
178 186
 	 * copied, and also copy the return address.
179 187
 	 */
@@ -181,7 +189,7 @@ real_to_prot:
181 189
 	pushl	_text16
182 190
 	pushl	_data16
183 191
 	addw	$16, %cx /* %ecx must be less than 64kB anyway */
184
-	
192
+
185 193
 	/* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
186 194
 	xorl	%ebp, %ebp
187 195
 	movw	%ss, %bp
@@ -396,9 +404,6 @@ prot_call:
396 404
 	.section ".text", "ax", @progbits
397 405
 	.code32
398 406
 1:
399
-	/* Set up environment expected by C code */
400
-	call	gateA20_set
401
-
402 407
 	/* Call function */
403 408
 	leal	PC_OFFSET_IX86(%esp), %eax
404 409
 	pushl	%eax
@@ -442,13 +447,7 @@ prot_call:
442 447
  * function will be passed back to the protected-mode caller.  A
443 448
  * result of this is that this routine cannot be called directly from
444 449
  * C code, since it clobbers registers that the C ABI expects the
445
- * callee to preserve.  Gate A20 will *not* be automatically
446
- * re-enabled.  Since we always run from an even megabyte of memory,
447
- * we are guaranteed to return successfully to the protected-mode
448
- * code, which should then call gateA20_set() if it suspects that gate
449
- * A20 may have been disabled.  Note that enabling gate A20 is a
450
- * potentially slow operation that may also cause keyboard input to be
451
- * lost; this is why it is not done automatically.
450
+ * callee to preserve.
452 451
  *
453 452
  * librm.h defines a convenient macro REAL_CODE() for using real_call.
454 453
  * See librm.h and realmode.h for details and examples.

Loading…
Cancel
Save