2 ;; $Id: memdisk16.asm,v 1.3 2004/12/14 22:46:25 hpa Exp $
3 ;; -----------------------------------------------------------------------
5 ;; Copyright 1994-2004 H. Peter Anvin - All Rights Reserved
7 ;; This program is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
10 ;; Boston MA 02111-1307, USA; either version 2 of the License, or
11 ;; (at your option) any later version; incorporated herein by reference.
13 ;; -----------------------------------------------------------------------
18 ;; Routine to initialize and to trampoline into 32-bit
19 ;; protected memory. This code is derived from bcopy32.inc and
20 ;; com32.inc in the main SYSLINUX distribution.
23 MY_CS equ 0x0800 ; Segment address to use
24 CS_BASE equ (MY_CS << 4) ; Corresponding address
26 ; Low memory bounce buffer
27 BOUNCE_SEG equ (MY_CS+0x1000)
31 %define STACK_HEAP_SIZE (128*1024)
33 section .rodata align=16
34 section .data align=16
37 ;; -----------------------------------------------------------------------
38 ;; Kernel image header
39 ;; -----------------------------------------------------------------------
41 section .text ; Must be first in image
44 cmdline times 497 db 0 ; We put the command line here
54 _start: jmp short start
56 db "HdrS" ; Header signature
57 dw 0x0203 ; Header version number
59 realmode_swtch dw 0, 0 ; default_switch, SETUPSEG
60 start_sys_seg dw 0x1000 ; obsolete
61 version_ptr dw memdisk_version-0x200 ; version string ptr
62 type_of_loader db 0 ; Filled in by boot loader
63 loadflags db 1 ; Please load high
64 setup_move_size dw 0 ; Unused
65 code32_start dd 0x100000 ; 32-bit start address
66 ramdisk_image dd 0 ; Loaded ramdisk image address
67 ramdisk_size dd 0 ; Size of loaded ramdisk
68 bootsect_kludge dw 0, 0
71 cmd_line_ptr dd 0 ; Command line
72 ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address
76 db "MEMDISK ", VERSION, " ", DATE, 0
78 ;; -----------------------------------------------------------------------
79 ;; End kernel image header
80 ;; -----------------------------------------------------------------------
83 ; Move ourselves down into memory to reduce the risk of conflicts;
84 ; then canonicalize CS to match the other segments.
91 movzx cx,byte [setup_sects]
92 inc cx ; Add one for the boot sector
93 shl cx,7 ; Convert to dwords
101 xor esp,esp ; Stack at top of 64K segment
106 ; Copy the command line, if there is one
109 xor di,di ; Bottom of our own segment (= "boot sector")
110 mov eax,[cmd_line_ptr]
112 jz .endcmd ; No command line
114 shr eax,4 ; Convert to segment
115 and si,0x000F ; Starting offset only
117 mov cx,496 ; Max number of bytes
129 ; Now jump to 32-bit code
134 ; When init32 returns, we have been set up, the new boot sector loaded,
135 ; and we should go and and run the newly loaded boot sector
137 ; The setup function returns (in AL) the drive number which should be
143 xor esi,esi ; No partition table involved
144 mov ds,si ; Make all the segments consistent
149 mov esp,0x7C00 ; Good place for SP to start out
153 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
154 ; and then exit. IMPORTANT: This code assumes cs == MY_CS.
156 ; This code is probably excessively anal-retentive in its handling of
157 ; segments, but this stuff is painful enough as it is without having to rely
158 ; on everything happening "as it ought to."
162 ; desc base, limit, flags
164 dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
165 dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
169 call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT
170 .adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction
173 ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K
174 desc CS_BASE, 0xffff, 0x009b
176 ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K
177 desc CS_BASE, 0xffff, 0x0093
179 ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G
180 desc 0, 0xfffff, 0x809b
182 ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G
183 desc 0, 0xfffff, 0xc09b
185 ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G
186 desc 0, 0xfffff, 0xc093
188 call32_gdt_size: equ $-call32_gdt
190 err_a20: db 'ERROR: A20 gate not responding!',13,10,0
194 SavedSSSP resd 1 ; Place to save SS:SP
195 Return resd 1 ; Return value
196 A20Test resw 1 ; Space to test A20
201 Target dd 0 ; Target address
202 Target_Seg dw 20h ; Target CS
204 A20Type dw 0 ; Default = unknown
209 ; Routines to enable and disable (yuck) A20. These routines are gathered
210 ; from tips from a couple of sources, including the Linux kernel and
211 ; http://www.x86.org/. The need for the delay to be as large as given here
212 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
213 ; IBM ThinkPad 760EL.
215 ; We typically toggle A20 twice for every 64K transferred.
217 %define io_delay call _io_delay
218 %define IO_DELAY_PORT 80h ; Invalid port (we hope!)
219 %define disable_wait 32 ; How long to wait for a disable
221 %define A20_DUNNO 0 ; A20 type unknown
222 %define A20_NONE 1 ; A20 always on?
223 %define A20_BIOS 2 ; A20 BIOS enable
224 %define A20_KBC 3 ; A20 through KBC
225 %define A20_FAST 4 ; A20 through port 92h
228 A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
229 A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
230 a20_adjust_cnt equ ($-A20List)/2
232 slow_out: out dx, al ; Fall through
234 _io_delay: out IO_DELAY_PORT,al
240 mov byte [A20Tries],255 ; Times to try to make this work
252 ; If the A20 type is known, jump straight to type
255 add bp,bp ; Convert to word offset
256 .adj4: jmp word [bp+A20List]
259 ; First, see if we are on a system with no A20 gate
263 mov byte [A20Type], A20_NONE
268 ; Next, try the BIOS (INT 15h AX=2401h)
271 mov byte [A20Type], A20_BIOS
273 pushf ; Some BIOSes muck with IF
281 ; Enable the keyboard controller A20 gate
284 mov dl, 1 ; Allow early exit
286 jnz a20_done ; A20 live, no need to use KBC
288 mov byte [A20Type], A20_KBC ; Starting KBC command sequence
290 mov al,0D1h ; Command write
292 call empty_8042_uncond
296 call empty_8042_uncond
298 ; Verify that A20 actually is enabled. Do that by
299 ; observing a word in low memory and the same word in
300 ; the HMA until they are no longer coherent. Note that
301 ; we don't do the same check in the disable case, because
302 ; we don't want to *require* A20 masking (SYSLINUX should
303 ; work fine without it, if the BIOS does.)
313 ; Running out of options here. Final attempt: enable the "fast A20 gate"
316 mov byte [A20Type], A20_FAST ; Haven't used the KBC yet
319 and al,~01h ; Don't accidentally reset the machine!
332 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
333 ; and report failure to the user.
358 ; A20 unmasked, proceed...
365 ; This routine tests if A20 is enabled (ZF = 0). This routine
366 ; must not destroy any register contents.
372 mov cx,0FFFFh ; HMA = segment 0FFFFh
374 mov cx,32 ; Loop count
378 io_delay ; Serialize, and fix delay
379 cmp ax,[es:A20Test+CS_BASE+10h]
396 add bp,bp ; Convert to word offset
397 .adj5: jmp word [bp+A20DList]
401 pushf ; Some BIOSes muck with IF
404 jmp short a20d_snooze
407 ; Disable the "fast A20 gate"
413 jmp short a20d_snooze
416 ; Disable the keyboard controller A20 gate
419 call empty_8042_uncond
421 out 064h, al ; Command write
422 call empty_8042_uncond
423 mov al,0DDh ; A20 off
425 call empty_8042_uncond
426 ; Wait a bit for it to take effect
430 .delayloop: call a20_test
440 ; Routine to empty the 8042 KBC controller. If dl != 0
441 ; then we will test A20 in the loop and exit if A20 is
452 in al, 064h ; Status port
456 in al, 060h ; Read input
465 ; Execute a WBINVD instruction if possible on this CPU
475 PMESP resd 1 ; Protected mode %esp
477 section .idt nobits align=4096
479 pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs
484 pm_entry: equ 0x100000
490 dd pm_idt+CS_BASE ; Address
498 ; This is the main entrypoint in this function
501 mov ebx,call32_call_start+CS_BASE ; Where to go in PM
515 lgdt [call32_gdt] ; Set up GDT
516 lidt [call32_pmidt] ; Set up the IDT
519 mov cr0,eax ; Enter protected mode
520 jmp 20h:dword .in_pm+CS_BASE
524 xor eax,eax ; Available for future use...
528 mov al,28h ; Set up data segments
533 mov esp,[PMESP+CS_BASE] ; Load protmode %esp if available
534 jmp ebx ; Go to where we need to go
537 ; This is invoked before first dispatch of the 32-bit code, in 32-bit mode
541 ; Point the stack into low memory
542 ; We have: this segment, bounce buffer, then stack+heap
544 mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE
548 ; Set up the protmode IDT and the interrupt jump buffers
550 mov edi,pm_idt+CS_BASE
552 ; Form an interrupt gate descriptor
553 ; WARNING: This is broken if pm_idt crosses a 64K boundary;
554 ; however, it can't because of the alignment constraints.
555 mov ebx,pm_idt+CS_BASE+8*256
572 ; Each entry in the interrupt jump buffer contains
573 ; the following instructions:
576 ; 00000001 B0xx mov al,<interrupt#>
577 ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt
580 mov ebx,call32_handle_interrupt+CS_BASE
585 sub [edi-2],cl ; Interrupt #
592 ; Now everything is set up for interrupts...
594 push dword (BOUNCE_SEG << 4) ; Bounce buffer address
595 push dword call32_syscall+CS_BASE ; Syscall entry point
596 sti ; Interrupts OK now
597 call pm_entry-CS_BASE ; Run the program...
600 mov [Return+CS_BASE],eax
602 ; ... fall through to call32_exit ...
605 mov bx,call32_done ; Return to command loop
610 mov [PMESP+CS_BASE],esp ; Save exit %esp
611 xor esp,esp ; Make sure the high bits are zero
612 jmp 08h:.in_pm16 ; Return to 16-bit mode first
616 mov ax,10h ; Real-mode-like segment
623 lidt [call32_rmidt] ; Real-mode IDT (rm needs no GDT)
629 .in_rm: ; Back in real mode
630 mov ax,cs ; Set up sane segments
635 lss sp,[SavedSSSP] ; Restore stack
636 jmp bx ; Go to whereever we need to go...
645 ; 16-bit support code
650 ; 16-bit interrupt-handling code
653 pushf ; Flags on stack
654 push cs ; Return segment
655 push word .cont ; Return address
656 push dword edx ; Segment:offset of IVT entry
657 retf ; Invoke IVT routine
658 .cont: ; ... on resume ...
659 mov ebx,call32_int_resume+CS_BASE
660 jmp call32_enter_pm ; Go back to PM
663 ; 16-bit system call handling code
672 retf ; Invoke routine
680 mov ebx,call32_sys_resume+CS_BASE
684 ; 32-bit support code
689 ; This is invoked on getting an interrupt in protected mode. At
690 ; this point, we need to context-switch to real mode and invoke
691 ; the interrupt routine.
693 ; When this gets invoked, the registers are saved on the stack and
694 ; AL contains the register number.
696 call32_handle_interrupt:
698 xor ebx,ebx ; Actually makes the code smaller
699 mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
701 jmp call32_enter_rm ; Go to real mode
708 ; Syscall invocation. We manifest a structure on the real-mode stack,
709 ; containing the call32sys_t structure from <call32.h> as well as
710 ; the following entries (from low to high address):
714 ; - Return segment (== real mode cs)
718 pushfd ; Save IF among other things...
719 pushad ; We only need to save some, but...
722 movzx edi,word [SavedSSSP+CS_BASE]
723 movzx eax,word [SavedSSSP+CS_BASE+2]
724 sub edi,54 ; Allocate 54 bytes
725 mov [SavedSSSP+CS_BASE],di
727 add edi,eax ; Create linear address
729 mov esi,[esp+11*4] ; Source regs
731 mov cl,11 ; 44 bytes to copy
734 movzx eax,byte [esp+10*4] ; Interrupt number
735 ; ecx == 0 here; adding it to the EA makes the
737 mov eax,[ecx+eax*4] ; Get IVT entry
738 stosd ; Save in stack frame
739 mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs
740 stosd ; Save in stack frame
741 mov eax,[edi-12] ; Return flags
742 and eax,0x200cd7 ; Mask (potentially) unsafe flags
743 mov [edi-12],eax ; Primary flags entry
747 jmp call32_enter_rm ; Go to real mode
749 ; On return, the 44-byte return structure is on the
752 movzx esi,word [SavedSSSP+CS_BASE]
753 movzx eax,word [SavedSSSP+CS_BASE+2]
754 mov edi,[esp+12*4] ; Dest regs
756 add esi,eax ; Create linear address
757 and edi,edi ; NULL pointer?
759 .no_copy: mov edi,esi ; Do a dummy copy-to-self
760 .do_copy: xor ecx,ecx
762 rep movsd ; Copy register block
764 add dword [SavedSSSP+CS_BASE],44 ; Remove from stack
768 ret ; Return to 32-bit program