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 ;; Routine to initialize and to trampoline into 32-bit
18 ;; protected memory. This code is derived from bcopy32.inc and
19 ;; com32.inc in the main SYSLINUX distribution.
22 MY_CS
equ 0x0800 ; Segment address to use
23 CS_BASE
equ (MY_CS
<< 4) ; Corresponding address
25 ; Low memory bounce buffer
26 BOUNCE_SEG
equ (MY_CS
+0x1000)
30 %define STACK_HEAP_SIZE
(128*1024)
32 section .rodata
align=16
33 section .data
align=16
36 ;; -----------------------------------------------------------------------
37 ;; Kernel image header
38 ;; -----------------------------------------------------------------------
40 section .text
; Must be first in image
43 cmdline times
497 db 0 ; We put the command line here
53 _start: jmp short start
55 db "HdrS" ; Header signature
56 dw 0x0203 ; Header version number
58 realmode_swtch
dw 0, 0 ; default_switch, SETUPSEG
59 start_sys_seg
dw 0x1000 ; obsolete
60 version_ptr
dw memdisk_version
-0x200 ; version string ptr
61 type_of_loader
db 0 ; Filled in by boot loader
62 loadflags
db 1 ; Please load high
63 setup_move_size
dw 0 ; Unused
64 code32_start
dd 0x100000 ; 32-bit start address
65 ramdisk_image
dd 0 ; Loaded ramdisk image address
66 ramdisk_size
dd 0 ; Size of loaded ramdisk
67 bootsect_kludge
dw 0, 0
70 cmd_line_ptr
dd 0 ; Command line
71 ramdisk_max
dd 0xffffffff ; Highest allowed ramdisk address
74 ; These fields aren't real setup fields, they're poked in by the
77 b_esdi
dd 0 ; ES:DI for boot sector invocation
78 b_edx
dd 0 ; EDX for boot sector invocation
82 db "MEMDISK ", VERSION
, " ", DATE
, 0
84 ;; -----------------------------------------------------------------------
85 ;; End kernel image header
86 ;; -----------------------------------------------------------------------
89 ; Move ourselves down into memory to reduce the risk of conflicts;
90 ; then canonicalize CS to match the other segments.
97 movzx cx,byte [setup_sects
]
98 inc cx ; Add one for the boot sector
99 shl cx,7 ; Convert to dwords
107 xor esp,esp ; Stack at top of 64K segment
112 ; Copy the command line, if there is one
115 xor di,di ; Bottom of our own segment (= "boot sector")
116 mov eax,[cmd_line_ptr
]
118 jz .endcmd
; No command line
120 shr eax,4 ; Convert to segment
121 and si,0x000F ; Starting offset only
123 mov cx,496 ; Max number of bytes
135 ; Now jump to 32-bit code
140 ; When init32 returns, we have been set up, the new boot sector loaded,
141 ; and we should go and and run the newly loaded boot sector.
143 ; The setup function will have poked values into the setup area.
145 movzx edi,word [cs:b_esdi
]
146 mov es,word [cs:b_esdi
+2]
150 xor esi,esi ; No partition table involved
151 mov ds,si ; Make all the segments consistent
155 mov esp,0x7C00 ; Good place for SP to start out
157 int 18h ; A far return -> INT 18h
160 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
161 ; and then exit. IMPORTANT: This code assumes cs == MY_CS.
163 ; This code is probably excessively anal-retentive in its handling of
164 ; segments, but this stuff is painful enough as it is without having to rely
165 ; on everything happening "as it ought to."
169 ; desc base, limit, flags
171 dd (%2 & 0xffff) |
((%1 & 0xffff) << 16)
172 dd (%1 & 0xff000000) |
(%2 & 0xf0000) |
((%3 & 0xf0ff) << 8) |
((%1 & 0x00ff0000) >> 16)
176 call32_gdt: dw call32_gdt_size
-1 ; Null descriptor - contains GDT
177 .
adj1: dd call32_gdt
+CS_BASE
; pointer for LGDT instruction
180 ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K
181 desc CS_BASE
, 0xffff, 0x009b
183 ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K
184 desc CS_BASE
, 0xffff, 0x0093
186 ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G
187 desc
0, 0xfffff, 0x809b
189 ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G
190 desc
0, 0xfffff, 0xc09b
192 ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G
193 desc
0, 0xfffff, 0xc093
195 call32_gdt_size: equ $
-call32_gdt
197 err_a20: db 'ERROR: A20 gate not responding!',13,10,0
201 SavedSSSP resd
1 ; Place to save SS:SP
202 Return resd
1 ; Return value
203 A20Test resw
1 ; Space to test A20
208 Target
dd 0 ; Target address
209 Target_Seg
dw 20h ; Target CS
211 A20Type
dw 0 ; Default = unknown
216 ; Routines to enable and disable (yuck) A20. These routines are gathered
217 ; from tips from a couple of sources, including the Linux kernel and
218 ; http://www.x86.org/. The need for the delay to be as large as given here
219 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
220 ; IBM ThinkPad 760EL.
222 ; We typically toggle A20 twice for every 64K transferred.
224 %define io_delay
call _io_delay
225 %define IO_DELAY_PORT
80h ; Invalid port (we hope!)
226 %define disable_wait
32 ; How long to wait for a disable
228 %define A20_DUNNO
0 ; A20 type unknown
229 %define A20_NONE
1 ; A20 always on?
230 %define A20_BIOS
2 ; A20 BIOS enable
231 %define A20_KBC
3 ; A20 through KBC
232 %define A20_FAST
4 ; A20 through port 92h
235 A20List
dw a20_dunno
, a20_none
, a20_bios
, a20_kbc
, a20_fast
236 A20DList
dw a20d_dunno
, a20d_none
, a20d_bios
, a20d_kbc
, a20d_fast
237 a20_adjust_cnt
equ ($
-A20List
)/2
239 slow_out: out dx, al ; Fall through
241 _io_delay: out IO_DELAY_PORT
,al
247 mov byte [A20Tries
],255 ; Times to try to make this work
259 ; If the A20 type is known, jump straight to type
262 add bp,bp ; Convert to word offset
263 .
adj4: jmp word [bp+A20List
]
266 ; First, see if we are on a system with no A20 gate
270 mov byte [A20Type
], A20_NONE
275 ; Next, try the BIOS (INT 15h AX=2401h)
278 mov byte [A20Type
], A20_BIOS
280 pushf ; Some BIOSes muck with IF
288 ; Enable the keyboard controller A20 gate
291 mov dl, 1 ; Allow early exit
293 jnz a20_done
; A20 live, no need to use KBC
295 mov byte [A20Type
], A20_KBC
; Starting KBC command sequence
297 mov al,0D1h
; Command write
299 call empty_8042_uncond
303 call empty_8042_uncond
305 ; Verify that A20 actually is enabled. Do that by
306 ; observing a word in low memory and the same word in
307 ; the HMA until they are no longer coherent. Note that
308 ; we don't do the same check in the disable case, because
309 ; we don't want to *require* A20 masking (SYSLINUX should
310 ; work fine without it, if the BIOS does.)
320 ; Running out of options here. Final attempt: enable the "fast A20 gate"
323 mov byte [A20Type
], A20_FAST
; Haven't used the KBC yet
326 and al,~
01h ; Don't accidentally reset the machine!
339 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
340 ; and report failure to the user.
365 ; A20 unmasked, proceed...
372 ; This routine tests if A20 is enabled (ZF = 0). This routine
373 ; must not destroy any register contents.
379 mov cx,0FFFFh
; HMA = segment 0FFFFh
381 mov cx,32 ; Loop count
385 io_delay
; Serialize, and fix delay
386 cmp ax,[es:A20Test
+CS_BASE
+10h]
403 add bp,bp ; Convert to word offset
404 .
adj5: jmp word [bp+A20DList
]
408 pushf ; Some BIOSes muck with IF
411 jmp short a20d_snooze
414 ; Disable the "fast A20 gate"
420 jmp short a20d_snooze
423 ; Disable the keyboard controller A20 gate
426 call empty_8042_uncond
428 out 064h, al ; Command write
429 call empty_8042_uncond
430 mov al,0DDh
; A20 off
432 call empty_8042_uncond
433 ; Wait a bit for it to take effect
437 .
delayloop: call a20_test
447 ; Routine to empty the 8042 KBC controller. If dl != 0
448 ; then we will test A20 in the loop and exit if A20 is
459 in al, 064h ; Status port
463 in al, 060h ; Read input
472 ; Execute a WBINVD instruction if possible on this CPU
482 PMESP resd
1 ; Protected mode %esp
484 section .idt nobits
align=4096
486 pm_idt resb
4096 ; Protected-mode IDT, followed by interrupt stubs
491 pm_entry: equ 0x100000
497 dd pm_idt
+CS_BASE
; Address
505 ; This is the main entrypoint in this function
508 mov ebx,call32_call_start
+CS_BASE
; Where to go in PM
522 lgdt [call32_gdt
] ; Set up GDT
523 lidt [call32_pmidt
] ; Set up the IDT
526 mov cr0
,eax ; Enter protected mode
527 jmp 20h:dword .in_pm
+CS_BASE
531 xor eax,eax ; Available for future use...
535 mov al,28h ; Set up data segments
540 mov esp,[PMESP
+CS_BASE
] ; Load protmode %esp if available
541 jmp ebx ; Go to where we need to go
544 ; This is invoked before first dispatch of the 32-bit code, in 32-bit mode
548 ; Point the stack into low memory
549 ; We have: this segment, bounce buffer, then stack+heap
551 mov esp, CS_BASE
+ 0x20000 + STACK_HEAP_SIZE
555 ; Set up the protmode IDT and the interrupt jump buffers
557 mov edi,pm_idt
+CS_BASE
559 ; Form an interrupt gate descriptor
560 ; WARNING: This is broken if pm_idt crosses a 64K boundary;
561 ; however, it can't because of the alignment constraints.
562 mov ebx,pm_idt
+CS_BASE
+8*256
579 ; Each entry in the interrupt jump buffer contains
580 ; the following instructions:
583 ; 00000001 B0xx mov al,<interrupt#>
584 ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt
587 mov ebx,call32_handle_interrupt
+CS_BASE
592 sub [edi-2],cl ; Interrupt #
599 ; Now everything is set up for interrupts...
601 push dword (BOUNCE_SEG
<< 4) ; Bounce buffer address
602 push dword call32_syscall
+CS_BASE
; Syscall entry point
603 sti ; Interrupts OK now
604 call pm_entry
-CS_BASE
; Run the program...
607 mov [Return
+CS_BASE
],eax
609 ; ... fall through to call32_exit ...
612 mov bx,call32_done
; Return to command loop
617 mov [PMESP
+CS_BASE
],esp ; Save exit %esp
618 xor esp,esp ; Make sure the high bits are zero
619 jmp 08h:.in_pm16
; Return to 16-bit mode first
623 mov ax,10h ; Real-mode-like segment
630 lidt [call32_rmidt
] ; Real-mode IDT (rm needs no GDT)
636 .
in_rm: ; Back in real mode
637 mov ax,cs ; Set up sane segments
642 lss sp,[SavedSSSP
] ; Restore stack
643 jmp bx ; Go to whereever we need to go...
652 ; 16-bit support code
657 ; 16-bit interrupt-handling code
660 pushf ; Flags on stack
661 push cs ; Return segment
662 push word .cont
; Return address
663 push dword edx ; Segment:offset of IVT entry
664 retf ; Invoke IVT routine
665 .
cont: ; ... on resume ...
666 mov ebx,call32_int_resume
+CS_BASE
667 jmp call32_enter_pm
; Go back to PM
670 ; 16-bit system call handling code
679 retf ; Invoke routine
687 mov ebx,call32_sys_resume
+CS_BASE
691 ; 32-bit support code
696 ; This is invoked on getting an interrupt in protected mode. At
697 ; this point, we need to context-switch to real mode and invoke
698 ; the interrupt routine.
700 ; When this gets invoked, the registers are saved on the stack and
701 ; AL contains the register number.
703 call32_handle_interrupt:
705 xor ebx,ebx ; Actually makes the code smaller
706 mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
708 jmp call32_enter_rm
; Go to real mode
715 ; Syscall invocation. We manifest a structure on the real-mode stack,
716 ; containing the call32sys_t structure from <call32.h> as well as
717 ; the following entries (from low to high address):
721 ; - Return segment (== real mode cs)
725 pushfd ; Save IF among other things...
726 pushad ; We only need to save some, but...
729 movzx edi,word [SavedSSSP
+CS_BASE
]
730 movzx eax,word [SavedSSSP
+CS_BASE
+2]
731 sub edi,54 ; Allocate 54 bytes
732 mov [SavedSSSP
+CS_BASE
],di
734 add edi,eax ; Create linear address
736 mov esi,[esp+11*4] ; Source regs
738 mov cl,11 ; 44 bytes to copy
741 movzx eax,byte [esp+10*4] ; Interrupt number
742 ; ecx == 0 here; adding it to the EA makes the
744 mov eax,[ecx+eax*4] ; Get IVT entry
745 stosd ; Save in stack frame
746 mov eax,call32_sys_rm.return
+ (MY_CS
<< 16) ; Return seg:offs
747 stosd ; Save in stack frame
748 mov eax,[edi-12] ; Return flags
749 and eax,0x200cd7 ; Mask (potentially) unsafe flags
750 mov [edi-12],eax ; Primary flags entry
754 jmp call32_enter_rm
; Go to real mode
756 ; On return, the 44-byte return structure is on the
759 movzx esi,word [SavedSSSP
+CS_BASE
]
760 movzx eax,word [SavedSSSP
+CS_BASE
+2]
761 mov edi,[esp+12*4] ; Dest regs
763 add esi,eax ; Create linear address
764 and edi,edi ; NULL pointer?
766 .
no_copy: mov edi,esi ; Do a dummy copy-to-self
767 .
do_copy: xor ecx,ecx
769 rep movsd ; Copy register block
771 add dword [SavedSSSP
+CS_BASE
],44 ; Remove from stack
775 ret ; Return to 32-bit program