[prefix] Add A20-enabling code in libflat
[people/pcmattman/gpxe.git] / src / arch / i386 / transitions / libflat.S
1 /*
2  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER )
21
22         .arch i386
23
24 #define CR0_PE 1
25
26 /****************************************************************************
27  * flatten_real_mode
28  *
29  * Set up 4GB segment limits
30  *
31  * Parameters:
32  *   none
33  * Returns:
34  *   none
35  * Corrupts:
36  *   none
37  ****************************************************************************
38  */
39         /* GDT for protected-mode calls */
40         .section ".text16.early.data", "aw", @progbits
41         .align 16
42 flatten_gdt:
43 flatten_gdt_limit:      .word flatten_gdt_length - 1
44 flatten_gdt_base:       .long 0
45                         .word 0 /* padding */
46 flatten_cs:     /* 16-bit protected-mode flat code segment */
47         .equ    FLAT_CS, flatten_cs - flatten_gdt
48         .word   0xffff, 0
49         .byte   0, 0x9b, 0x8f, 0
50 flatten_ss:     /* 16-bit protected-mode flat stack segment */
51         .equ    FLAT_SS, flatten_ss - flatten_gdt
52         .word   0xffff, 0
53         .byte   0, 0x93, 0x8f, 0
54 flatten_gdt_end:
55         .equ    flatten_gdt_length, . - flatten_gdt
56         .size   flatten_gdt, . - flatten_gdt
57
58         .section ".text16.early.data", "aw", @progbits
59         .align 16
60 flatten_saved_gdt:
61         .long   0, 0
62         .size   flatten_saved_gdt, . - flatten_saved_gdt
63
64         .section ".text16.early", "awx", @progbits
65         .code16
66 flatten_real_mode:
67         /* Preserve registers and flags */
68         pushfl
69         pushl   %eax
70         pushw   %si
71         pushw   %gs
72         pushw   %fs
73         pushw   %es
74         pushw   %ds
75         pushw   %ss
76
77         /* Set %ds for access to .text16.early.data variables */
78         pushw   %cs
79         popw    %ds
80
81         /* Preserve original GDT */
82         sgdt flatten_saved_gdt
83
84         /* Set up GDT bases */
85         xorl    %eax, %eax
86         movw    %cs, %ax
87         shll    $4, %eax
88         addl    $flatten_gdt, %eax
89         movl    %eax, flatten_gdt_base
90         movw    %cs, %ax
91         movw    $flatten_cs, %si
92         call    set_seg_base
93         movw    %ss, %ax
94         movw    $flatten_ss, %si
95         call    set_seg_base
96
97         /* Switch temporarily to protected mode and set segment registers */
98         pushw   %cs
99         pushw   $2f
100         cli
101         data32 lgdt flatten_gdt
102         movl    %cr0, %eax
103         orb     $CR0_PE, %al
104         movl    %eax, %cr0
105         ljmp    $FLAT_CS, $1f
106 1:      movw    $FLAT_SS, %ax
107         movw    %ax, %ss
108         movw    %ax, %ds
109         movw    %ax, %es
110         movw    %ax, %fs
111         movw    %ax, %gs
112         movl    %cr0, %eax
113         andb    $0!CR0_PE, %al
114         movl    %eax, %cr0
115         lret
116 2:      /* lret will ljmp to here */
117
118         /* Restore GDT, registers and flags */
119         data32 lgdt flatten_saved_gdt
120         popw    %ss
121         popw    %ds
122         popw    %es
123         popw    %fs
124         popw    %gs
125         popw    %si
126         popl    %eax
127         popfl
128         ret
129         .size flatten_real_mode, . - flatten_real_mode
130
131         .section ".text16.early", "awx", @progbits
132         .code16
133 set_seg_base:
134         rolw    $4, %ax
135         movw    %ax, 2(%si)
136         andw    $0xfff0, 2(%si)
137         movb    %al, 4(%si)
138         andb    $0x0f, 4(%si)
139         ret
140         .size set_seg_base, . - set_seg_base
141
142 /****************************************************************************
143  * test_a20_short, test_a20_long
144  *
145  * Check to see if A20 line is enabled
146  *
147  * Parameters:
148  *   none
149  * Returns:
150  *   CF set if A20 line is not enabled
151  * Corrupts:
152  *   none
153  ****************************************************************************
154  */
155 #define TEST_A20_SHORT_MAX_RETRIES 0x20
156 #define TEST_A20_LONG_MAX_RETRIES 0x200000
157         .section ".text16.early", "awx", @progbits
158         .code16
159 test_a20_short:
160         pushl   %ecx
161         movl    $TEST_A20_SHORT_MAX_RETRIES, %ecx
162         jmp     1f
163         .size   test_a20_short, . - test_a20_short
164 test_a20_long:
165         pushl   %ecx
166         movl    $TEST_A20_LONG_MAX_RETRIES, %ecx
167 1:      pushw   %ax
168
169         /* Flatten real mode so we can access the test pattern's 1MB offset */
170         call    flatten_real_mode
171
172 2:      /* Modify and check test pattern; succeed if we see a difference */
173         incw    %cs:test_a20_data
174         addr32 movw %cs:(test_a20_data + 0x100000 ), %ax
175         cmpw    %cs:test_a20_data, %ax
176         clc
177         jnz     99f
178
179         /* Delay and retry */
180         outb    %al, $0x80
181         addr32 loop 2b
182         stc
183
184 99:     /* Restore registers and return */
185         popw    %ax
186         popl    %ecx
187         ret
188         .size   test_a20_long, . - test_a20_long
189
190         .section ".text16.early.data", "aw", @progbits
191         .align  2
192 test_a20_data:
193         .word   0xdead
194         .size   test_a20_data, . - test_a20_data
195
196 /****************************************************************************
197  * enable_a20_bios
198  *
199  * Try enabling A20 line via BIOS
200  *
201  * Parameters:
202  *   none
203  * Returns:
204  *   CF set if A20 line is not enabled
205  * Corrupts:
206  *   none
207  ****************************************************************************
208  */
209         .section ".text16.early", "awx", @progbits
210         .code16
211 enable_a20_bios:
212         /* Preserve registers */
213         pushw   %ax
214
215         /* Attempt INT 15,2401 */
216         movw    $0x2401, %ax
217         int     $0x15
218         jc      99f
219
220         /* Check that success was really successful */
221         call    test_a20_short
222
223 99:     /* Restore registers and return */
224         popw    %ax
225         ret
226         .size   enable_a20_bios, . - enable_a20_bios
227
228 /****************************************************************************
229  * enable_a20_kbc
230  *
231  * Try enabling A20 line via keyboard controller
232  *
233  * Parameters:
234  *   none
235  * Returns:
236  *   CF set if A20 line is not enabled
237  * Corrupts:
238  *   none
239  ****************************************************************************
240  */
241 #define KC_RDWR         0x60
242 #define KC_RDWR_SET_A20         0xdf
243 #define KC_CMD          0x64
244 #define KC_CMD_WOUT             0xd1
245 #define KC_CMD_NULL             0xff
246 #define KC_STATUS       0x64
247 #define KC_STATUS_OBUF_FULL     0x01
248 #define KC_STATUS_IBUF_FULL     0x02
249 #define KC_MAX_RETRIES  100000
250         .section ".text16.early", "awx", @progbits
251         .code16
252 enable_a20_kbc:
253         /* Preserve registers */
254         pushw   %ax
255
256         /* Try keyboard controller */
257         call    empty_kbc
258         movb    $KC_CMD_WOUT, %al
259         outb    %al, $KC_CMD
260         call    empty_kbc
261         movb    $KC_RDWR_SET_A20, %al
262         outb    %al, $KC_RDWR
263         call    empty_kbc
264         movb    $KC_CMD_NULL, %al
265         outb    %al, $KC_CMD
266         call    empty_kbc
267
268         /* Check to see if it worked */
269         call    test_a20_long
270
271         /* Restore registers and return */
272         popw    %ax
273         ret
274         .size   enable_a20_kbc, . - enable_a20_kbc
275
276         .section ".text16.early", "awx", @progbits
277         .code16
278 empty_kbc:
279         /* Preserve registers */
280         pushl   %ecx
281         pushw   %ax
282
283         /* Wait for KBC to become empty */
284         movl    $KC_MAX_RETRIES, %ecx
285 1:      outb    %al, $0x80
286         inb     $KC_STATUS, %al
287         testb   $( KC_STATUS_OBUF_FULL | KC_STATUS_IBUF_FULL ), %al
288         jz      99f
289         testb   $KC_STATUS_OBUF_FULL, %al
290         jz      2f
291         outb    %al, $0x80
292         inb     $KC_RDWR, %al
293 2:      addr32 loop 1b
294
295 99:     /* Restore registers and return */
296         popw    %ax
297         popl    %ecx
298         ret
299         .size   empty_kbc, . - empty_kbc
300
301 /****************************************************************************
302  * enable_a20_fast
303  *
304  * Try enabling A20 line via "Fast Gate A20"
305  *
306  * Parameters:
307  *   none
308  * Returns:
309  *   CF set if A20 line is not enabled
310  * Corrupts:
311  *   none
312  ****************************************************************************
313  */
314 #define SCP_A 0x92
315         .section ".text16.early", "awx", @progbits
316         .code16
317 enable_a20_fast:
318         /* Preserve registers */
319         pushw   %ax
320
321         /* Try "Fast Gate A20" */
322         inb     $SCP_A, %al
323         orb     $0x02, %al
324         andb    $~0x01, %al
325         outb    %al, $SCP_A
326
327         /* Check to see if it worked */
328         call    test_a20_long
329
330         /* Restore registers and return */
331         popw    %ax
332         ret
333         .size   enable_a20_fast, . - enable_a20_fast
334
335 /****************************************************************************
336  * enable_a20
337  *
338  * Try enabling A20 line via any available method
339  *
340  * Parameters:
341  *   none
342  * Returns:
343  *   CF set if A20 line is not enabled
344  * Corrupts:
345  *   none
346  ****************************************************************************
347  */
348 #define ENABLE_A20_RETRIES 255
349         .section ".text16.early", "awx", @progbits
350         .code16
351 enable_a20:
352         /* Preserve registers */
353         pushl   %ecx
354         pushw   %ax
355
356         /* Check to see if A20 is already enabled */
357         call    test_a20_short
358         jnc     99f
359
360         /* Use known working method, if we have one */
361         movw    %cs:enable_a20_method, %ax
362         testw   %ax, %ax
363         jz      1f
364         call    *%ax
365         jmp     99f
366 1:
367         /* Try all methods in turn until one works */
368         movl    $ENABLE_A20_RETRIES, %ecx
369 2:      movw    $enable_a20_bios, %ax
370         movw    %ax, %cs:enable_a20_method
371         call    *%ax
372         jnc     99f
373         movw    $enable_a20_kbc, %ax
374         movw    %ax, %cs:enable_a20_method
375         call    *%ax
376         jnc     99f
377         movw    $enable_a20_fast, %ax
378         movw    %ax, %cs:enable_a20_method
379         call    *%ax
380         jnc     99f
381         addr32 loop 2b
382         /* Failure; exit with carry set */
383         movw    $0, %cs:enable_a20_method
384         stc
385
386 99:     /* Restore registers and return */
387         popw    %ax
388         popl    %ecx
389         ret
390
391         .section ".text16.early.data", "aw", @progbits
392         .align  2
393 enable_a20_method:
394         .word   0
395         .size   enable_a20_method, . - enable_a20_method
396
397 /****************************************************************************
398  * access_highmem (real mode far call)
399  *
400  * Open up access to high memory in flat real mode with A20 enabled
401  *
402  * Parameters:
403  *   none
404  * Returns:
405  *   CF set if high memory could not be accessed
406  * Corrupts:
407  *   none
408  ****************************************************************************
409  */
410         .section ".text16.early", "awx", @progbits
411         .code16
412         .globl  access_highmem
413 access_highmem:
414         /* Enable A20 line */
415         call    enable_a20
416         /* CPU will be in flat real mode as a result of this call */
417         lret
418         .size   access_highmem, . - access_highmem