1 ;; $Id: bcopy32.inc,v 1.16 2005/01/06 22:34:06 hpa Exp $
2 ;; -----------------------------------------------------------------------
4 ;; Copyright 1994-2005 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 ; NOTE: this code is relocated into low memory, just after the .earlybss
31 ; segment, in order to support to "bcopy over self" operation.
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 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
59 ; 32-bit copy, overlap safe
62 ; ESI - source pointer
63 ; EDI - target pointer
68 ; ESI - first byte after source
69 ; EDI - first byte after target
76 pushf ; Saves, among others, the IF flag
83 o32 lgdt [cs:bcopy_gdt]
86 mov cr0,eax ; Enter protected mode
89 .in_pm: mov ax,10h ; Data segment selector
93 ; Don't mess with ss, fs, and gs. They are never changed
94 ; and should be able to make it back out of protected mode.
95 ; This works because (and only because) we don't take
96 ; interrupt in protected mode.
98 cmp esi,edi ; If source > destination, we might
99 ja .reverse ; have to copy backwards
102 mov al,cl ; Save low bits
104 shr ecx,2 ; Convert to dwords
105 a32 rep movsd ; Do our business
106 ; At this point ecx == 0
108 mov cl,al ; Copy any fractional dword
114 lea esi,[esi+ecx-1] ; Point to final byte
121 ; Change ESI/EDI to point to the last dword, instead
131 mov ax,18h ; "Real-mode-like" data segment
137 mov cr0,eax ; Disable protected mode
140 .in_rm: ; Back in real mode
145 popf ; Re-enables interrupts
155 ; Routines to enable and disable (yuck) A20. These routines are gathered
156 ; from tips from a couple of sources, including the Linux kernel and
157 ; http://www.x86.org/. The need for the delay to be as large as given here
158 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
159 ; IBM ThinkPad 760EL.
161 ; We typically toggle A20 twice for every 64K transferred.
163 %define io_delay call _io_delay
164 %define IO_DELAY_PORT 80h ; Invalid port (we hope!)
165 %define disable_wait 32 ; How long to wait for a disable
167 ; Note the skip of 2 here
168 %define A20_DUNNO 0 ; A20 type unknown
169 %define A20_NONE 2 ; A20 always on?
170 %define A20_BIOS 4 ; A20 BIOS enable
171 %define A20_KBC 6 ; A20 through KBC
172 %define A20_FAST 8 ; A20 through port 92h
174 slow_out: out dx, al ; Fall through
176 _io_delay: out IO_DELAY_PORT,al
182 mov byte [cs:A20Tries],255 ; Times to try to make this work
193 ; If the A20 type is known, jump straight to type
196 jmp word [cs:bp+A20List]
199 ; First, see if we are on a system with no A20 gate
203 mov byte [cs:A20Type], A20_NONE
208 ; Next, try the BIOS (INT 15h AX=2401h)
211 mov byte [cs:A20Type], A20_BIOS
213 pushf ; Some BIOSes muck with IF
221 ; Enable the keyboard controller A20 gate
224 mov dl, 1 ; Allow early exit
226 jnz a20_done ; A20 live, no need to use KBC
228 mov byte [cs:A20Type], A20_KBC ; Starting KBC command sequence
230 mov al,0D1h ; Command write
232 call empty_8042_uncond
236 call empty_8042_uncond
238 ; Verify that A20 actually is enabled. Do that by
239 ; observing a word in low memory and the same word in
240 ; the HMA until they are no longer coherent. Note that
241 ; we don't do the same check in the disable case, because
242 ; we don't want to *require* A20 masking (SYSLINUX should
243 ; work fine without it, if the BIOS does.)
253 ; Running out of options here. Final attempt: enable the "fast A20 gate"
256 mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
259 and al,~01h ; Don't accidentally reset the machine!
272 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
273 ; and report failure to the user.
277 dec byte [cs:A20Tries]
283 ; A20 unmasked, proceed...
290 ; This routine tests if A20 is enabled (ZF = 0). This routine
291 ; must not destroy any register contents.
297 mov cx,0FFFFh ; HMA = segment 0FFFFh
299 mov cx,32 ; Loop count
303 io_delay ; Serialize, and fix delay
304 cmp ax,[es:A20Test+10h]
321 jmp word [cs:bp+A20DList]
325 pushf ; Some BIOSes muck with IF
328 jmp short a20d_snooze
331 ; Disable the "fast A20 gate"
337 jmp short a20d_snooze
340 ; Disable the keyboard controller A20 gate
343 call empty_8042_uncond
345 out 064h, al ; Command write
346 call empty_8042_uncond
347 mov al,0DDh ; A20 off
349 call empty_8042_uncond
350 ; Wait a bit for it to take effect
354 .delayloop: call a20_test
364 ; Routine to empty the 8042 KBC controller. If dl != 0
365 ; then we will test A20 in the loop and exit if A20 is
376 in al, 064h ; Status port
380 in al, 060h ; Read input
389 ; Execute a WBINVD instruction if possible on this CPU
400 ; This routine is used to shuffle memory around, followed by
401 ; invoking an entry point somewhere in low memory. This routine
402 ; can clobber any memory above 7C00h, we therefore have to move
403 ; necessary code into the trackbuf area before doing the copy,
404 ; and do adjustments to anything except BSS area references.
406 ; NOTE: Since PXELINUX relocates itself, put all these
407 ; references in the ".earlybss" segment.
409 ; After performing the copy, this routine resets the stack and
410 ; jumps to the specified entrypoint.
412 ; IMPORTANT: This routine does not canonicalize the stack or the
413 ; SS register. That is the responsibility of the caller.
416 ; DS:BX -> Pointer to list of (dst, src, len) pairs
417 ; AX -> Number of list entries
418 ; [CS:EntryPoint] -> CS:IP to jump to
419 ; On stack - initial state (fd, ad, ds, es, fs, gs)
440 jmp far [cs:EntryPoint]
443 A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
444 A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
445 a20_adjust_cnt equ ($-A20List)/2
447 A20Type dw A20_NONE ; A20 type
449 ; Total size of .bcopy32 section
450 alignb 4, db 0 ; Even number of dwords
451 __bcopy_size equ $-__bcopy_start
455 EntryPoint resd 1 ; CS:IP for shuffle_and_boot
456 SavedSSSP resd 1 ; Saved real mode SS:SP
457 A20Test resw 1 ; Counter for testing status of A20
458 A20Tries resb 1 ; Times until giving up on A20