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 | */ |
---|
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 | } |
---|