2 ;; -----------------------------------------------------------------------
4 ;; Copyright 1994-2004 H. Peter Anvin - All Rights Reserved
6 ;; This program is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 ;; Boston MA 02111-1307, USA; either version 2 of the License, or
10 ;; (at your option) any later version; incorporated herein by reference.
12 ;; -----------------------------------------------------------------------
17 ;; 32-bit bcopy routine for real mode
21 ; 32-bit bcopy routine for real mode
23 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
24 ; and then exit. IMPORTANT: This code assumes cs == 0.
26 ; This code is probably excessively anal-retentive in its handling of
27 ; segments, but this stuff is painful enough as it is without having to rely
28 ; on everything happening "as it ought to."
30 ; IMPORTANT: This code must be capable of operating when copied to the
31 ; trackbuf area (1000h). The routine bcopy_over_self handles this mode
32 ; of operation, including any necessary adjustments.
38 ; This is in the .text segment since it needs to be
39 ; contiguous with the rest of the bcopy stuff
41 bcopy_gdt: dw bcopy_gdt_size-1 ; Null descriptor - contains GDT
42 .adj1: dd bcopy_gdt ; pointer for LGDT instruction
44 dd 0000ffffh ; Code segment, use16, readable,
45 dd 00009b00h ; present, dpl 0, cover 64K
46 dd 0000ffffh ; Data segment, use16, read/write,
47 dd 008f9300h ; present, dpl 0, cover all 4G
48 dd 0000ffffh ; Data segment, use16, read/write,
49 dd 00009300h ; present, dpl 0, cover 64K
50 ; The rest are used for COM32 only
51 dd 0000ffffh ; Code segment, use32, readable,
52 dd 00cf9b00h ; present, dpl 0, cover all 4G
53 dd 0000ffffh ; Data segment, use32, read/write,
54 dd 00cf9300h ; present, dpl 0, cover all 4G
55 bcopy_gdt_size: equ $-bcopy_gdt
62 ; ESI - source pointer
63 ; EDI - target pointer
68 ; ESI - first byte after source
69 ; EDI - first byte after target
73 pushf ; Saves, among others, the IF flag
79 mov [cs:SavedSSSP+2],ss
84 .adj2: o32 lgdt [cs:bcopy_gdt]
87 mov cr0,eax ; Enter protected mode
88 .adj3a: jmp 08h:.in_pm
90 .in_pm: mov ax,10h ; Data segment selector
94 mov al,18h ; "Real-mode-like" data segment
99 mov al,cl ; Save low bits
100 shr ecx,2 ; Convert to dwords
101 a32 rep movsd ; Do our business
102 ; At this point ecx == 0
104 mov cl,al ; Copy any fractional dword
108 mov al,18h ; "Real-mode-like" data segment
114 mov cr0,eax ; Disable protected mode
117 .in_rm: ; Back in real mode
118 lss sp,[cs:SavedSSSP]
125 popf ; Re-enables interrupts
130 ; Routines to enable and disable (yuck) A20. These routines are gathered
131 ; from tips from a couple of sources, including the Linux kernel and
132 ; http://www.x86.org/. The need for the delay to be as large as given here
133 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
134 ; IBM ThinkPad 760EL.
136 ; We typically toggle A20 twice for every 64K transferred.
138 %define io_delay call _io_delay
139 %define IO_DELAY_PORT 80h ; Invalid port (we hope!)
140 %define disable_wait 32 ; How long to wait for a disable
142 %define A20_DUNNO 0 ; A20 type unknown
143 %define A20_NONE 1 ; A20 always on?
144 %define A20_BIOS 2 ; A20 BIOS enable
145 %define A20_KBC 3 ; A20 through KBC
146 %define A20_FAST 4 ; A20 through port 92h
149 A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
150 A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
151 a20_adjust_cnt equ ($-A20List)/2
153 slow_out: out dx, al ; Fall through
155 _io_delay: out IO_DELAY_PORT,al
161 mov byte [cs:A20Tries],255 ; Times to try to make this work
172 ; If the A20 type is known, jump straight to type
175 add bp,bp ; Convert to word offset
176 .adj4: jmp word [cs:bp+A20List]
179 ; First, see if we are on a system with no A20 gate
183 mov byte [cs:A20Type], A20_NONE
188 ; Next, try the BIOS (INT 15h AX=2401h)
191 mov byte [cs:A20Type], A20_BIOS
193 pushf ; Some BIOSes muck with IF
201 ; Enable the keyboard controller A20 gate
204 mov dl, 1 ; Allow early exit
206 jnz a20_done ; A20 live, no need to use KBC
208 mov byte [cs:A20Type], A20_KBC ; Starting KBC command sequence
210 mov al,0D1h ; Command write
212 call empty_8042_uncond
216 call empty_8042_uncond
218 ; Verify that A20 actually is enabled. Do that by
219 ; observing a word in low memory and the same word in
220 ; the HMA until they are no longer coherent. Note that
221 ; we don't do the same check in the disable case, because
222 ; we don't want to *require* A20 masking (SYSLINUX should
223 ; work fine without it, if the BIOS does.)
233 ; Running out of options here. Final attempt: enable the "fast A20 gate"
236 mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
239 and al,~01h ; Don't accidentally reset the machine!
252 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
253 ; and report failure to the user.
257 dec byte [cs:A20Tries]
263 ; A20 unmasked, proceed...
270 ; This routine tests if A20 is enabled (ZF = 0). This routine
271 ; must not destroy any register contents.
277 mov cx,0FFFFh ; HMA = segment 0FFFFh
279 mov cx,32 ; Loop count
283 io_delay ; Serialize, and fix delay
284 cmp ax,[es:A20Test+10h]
301 add bp,bp ; Convert to word offset
302 .adj5: jmp word [cs:bp+A20DList]
306 pushf ; Some BIOSes muck with IF
309 jmp short a20d_snooze
312 ; Disable the "fast A20 gate"
318 jmp short a20d_snooze
321 ; Disable the keyboard controller A20 gate
324 call empty_8042_uncond
326 out 064h, al ; Command write
327 call empty_8042_uncond
328 mov al,0DDh ; A20 off
330 call empty_8042_uncond
331 ; Wait a bit for it to take effect
335 .delayloop: call a20_test
345 ; Routine to empty the 8042 KBC controller. If dl != 0
346 ; then we will test A20 in the loop and exit if A20 is
357 in al, 064h ; Status port
361 in al, 060h ; Read input
370 ; Execute a WBINVD instruction if possible on this CPU
381 ; This routine is used to copy large blocks of code on top of
382 ; conventional memory (to 0:7c00). We therefore have to move
383 ; necessary code into the trackbuf area before doing the copy,
384 ; and do adjustments to anything except BSS area references.
386 ; After performing the copy, this routine resets the stack and
389 ; IMPORTANT: This routine does not canonicalize the stack or the
390 ; SS register. That is the responsibility of the caller.
393 ; ESI, EDI, ECX - same as bcopy
394 ; On stack - initial state (fd, ad, ds, es, fs, gs)
396 ADJUST equ (__bcopy_start - $$) + 7C00h - BSS_START
399 adjlist dw bcopy_gdt.adj1 - ADJUST
400 dw bcopy.adj2 + 5 - ADJUST
401 dw bcopy.adj3a + 1 - ADJUST
402 dw bcopy.adj3b + 1 - ADJUST
403 dw try_enable_a20.adj4 + 3 - ADJUST
404 dw disable_a20.adj5 + 3 - ADJUST
405 adjlist_cnt equ ($-adjlist)/2
417 mov di,trackbuf ; == BSS_START
418 mov cx,(__bcopy_end - __bcopy_start + 3) >> 2
421 mov si,A20List - ADJUST
422 mov cx,a20_adjust_cnt
424 sub word [si], ADJUST
434 sub word [di], ADJUST
454 A20Test resw 1 ; Counter for testing status of A20
455 A20Type resw 1 ; A20 type
456 A20Tries resb 1 ; Times until giving up on A20