• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 FILE_LICENCE ( GPL2_OR_LATER );
2 
3 #include <stdio.h>
4 #include <realmode.h>
5 #include <bios.h>
6 #include <gpxe/io.h>
7 #include <gpxe/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  */
empty_8042(void)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  */
gateA20_is_set(int retries)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  */
gateA20_set(void)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 
gateA20_unset(void)174 void gateA20_unset ( void ) {
175 	/* Not currently implemented */
176 }
177