More factoring of common code
authorhpa <hpa>
Sat, 27 Apr 2002 23:41:49 +0000 (23:41 +0000)
committerhpa <hpa>
Sat, 27 Apr 2002 23:41:49 +0000 (23:41 +0000)
bcopy32.inc [new file with mode: 0644]
comboot.inc [new file with mode: 0644]
isolinux.asm
ldlinux.asm
loadhigh.inc [new file with mode: 0644]
pxelinux.asm
runkernel.inc [new file with mode: 0644]

diff --git a/bcopy32.inc b/bcopy32.inc
new file mode 100644 (file)
index 0000000..daa061e
--- /dev/null
@@ -0,0 +1,414 @@
+;; $Id$
+;; -----------------------------------------------------------------------
+;;   
+;;   Copyright 1994-2002 H. Peter Anvin - All Rights Reserved
+;;
+;;   This program is free software; you can redistribute it and/or modify
+;;   it under the terms of the GNU General Public License as published by
+;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;;   Bostom MA 02111-1307, USA; either version 2 of the License, or
+;;   (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; bcopy32.inc
+;; 
+;; 32-bit bcopy routine for real mode
+;;
+
+;
+; 32-bit bcopy routine for real mode
+;
+; We enter protected mode, set up a flat 32-bit environment, run rep movsd
+; and then exit.  IMPORTANT: This code assumes cs == 0.
+;
+; This code is probably excessively anal-retentive in its handling of
+; segments, but this stuff is painful enough as it is without having to rely
+; on everything happening "as it ought to."
+;
+; IMPORTANT: This code must be capable of operating when copied to the
+; trackbuf area (1000h).  The routine bcopy_over_self handles this mode
+; of operation, including any necessary adjustments.
+;
+               align 4
+__bcopy_start:
+
+bcopy_gdt:     dw bcopy_gdt_size-1     ; Null descriptor - contains GDT
+.adj1:         dd bcopy_gdt            ; pointer for LGDT instruction
+               dw 0
+               dd 0000ffffh            ; Code segment, use16, readable,
+               dd 00009b00h            ; present, dpl 0, cover 64K
+               dd 0000ffffh            ; Data segment, use16, read/write,
+               dd 008f9300h            ; present, dpl 0, cover all 4G
+               dd 0000ffffh            ; Data segment, use16, read/write,
+               dd 00009300h            ; present, dpl 0, cover 64K
+bcopy_gdt_size:        equ $-bcopy_gdt
+
+bcopy:         push eax
+               pushf                   ; Saves, among others, the IF flag
+               push gs
+               push fs
+               push ds
+               push es
+               mov [cs:SavedSSSP],sp
+               mov ax,ss
+               mov [cs:SavedSSSP+2],ax
+
+               cli
+               call enable_a20
+
+.adj2:         o32 lgdt [cs:bcopy_gdt]
+               mov eax,cr0
+               or al,1
+               mov cr0,eax             ; Enter protected mode
+               jmp 08h:.in_pm
+
+.in_pm:                mov ax,10h              ; Data segment selector
+               mov es,ax
+               mov ds,ax
+
+               mov al,18h              ; "Real-mode-like" data segment
+               mov ss,ax
+               mov fs,ax
+               mov gs,ax       
+       
+               a32 rep movsd           ; Do our business
+               
+               mov es,ax               ; Set to "real-mode-like"
+               mov ds,ax
+       
+               mov eax,cr0
+               and al,~1
+               mov cr0,eax             ; Disable protected mode
+.adj3:         jmp 0:.in_rm
+
+.in_rm:                ; Back in real mode
+               lss sp,[cs:SavedSSSP]
+               pop es
+               pop ds
+               pop fs
+               pop gs
+               call disable_a20
+
+               popf                    ; Re-enables interrupts
+               pop eax
+               ret
+
+;
+; Routines to enable and disable (yuck) A20.  These routines are gathered
+; from tips from a couple of sources, including the Linux kernel and
+; http://www.x86.org/.  The need for the delay to be as large as given here
+; is indicated by Donnie Barnes of RedHat, the problematic system being an
+; IBM ThinkPad 760EL.
+;
+; We typically toggle A20 twice for every 64K transferred.
+; 
+%define        io_delay        call _io_delay
+%define IO_DELAY_PORT  80h             ; Invalid port (we hope!)
+%define disable_wait   32              ; How long to wait for a disable
+
+%define A20_DUNNO      0               ; A20 type unknown
+%define A20_NONE       1               ; A20 always on?
+%define A20_BIOS       2               ; A20 BIOS enable
+%define A20_KBC                3               ; A20 through KBC
+%define A20_FAST       4               ; A20 through port 92h
+
+               align 2
+A20List                dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
+A20DList       dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
+a20_adjust_cnt equ ($-A20List)/2
+A20Type                dw A20_DUNNO            ; A20 type unknown
+
+slow_out:      out dx, al              ; Fall through
+
+_io_delay:     out IO_DELAY_PORT,al
+               out IO_DELAY_PORT,al
+               ret
+
+enable_a20:
+               pushad
+               mov byte [cs:A20Tries],255 ; Times to try to make this work
+
+try_enable_a20:
+;
+; Flush the caches
+;
+;              call try_wbinvd
+
+;
+; If the A20 type is known, jump straight to type
+;
+               mov bp,[cs:A20Type]
+               add bp,bp                       ; Convert to word offset
+.adj4:         jmp word [cs:bp+A20List]
+
+;
+; First, see if we are on a system with no A20 gate
+;
+a20_dunno:
+a20_none:
+               mov byte [cs:A20Type], A20_NONE
+               call a20_test
+               jnz a20_done
+
+;
+; Next, try the BIOS (INT 15h AX=2401h)
+;
+a20_bios:
+               mov byte [cs:A20Type], A20_BIOS
+               mov ax,2401h
+               pushf                           ; Some BIOSes muck with IF
+               int 15h
+               popf
+
+               call a20_test
+               jnz a20_done
+
+;
+; Enable the keyboard controller A20 gate
+;
+a20_kbc:
+               mov dl, 1                       ; Allow early exit
+               call empty_8042
+               jnz a20_done                    ; A20 live, no need to use KBC
+
+               mov byte [cs:A20Type], A20_KBC  ; Starting KBC command sequence
+
+               mov al,0D1h                     ; Command write
+               out 064h, al
+               call empty_8042_uncond
+
+               mov al,0DFh                     ; A20 on
+               out 060h, al
+               call empty_8042_uncond
+
+               ; Verify that A20 actually is enabled.  Do that by
+               ; observing a word in low memory and the same word in
+               ; the HMA until they are no longer coherent.  Note that
+               ; we don't do the same check in the disable case, because
+               ; we don't want to *require* A20 masking (SYSLINUX should
+               ; work fine without it, if the BIOS does.)
+.kbc_wait:     push cx
+               xor cx,cx
+.kbc_wait_loop:
+               call a20_test
+               jnz a20_done_pop
+               loop .kbc_wait_loop
+
+               pop cx
+;
+; Running out of options here.  Final attempt: enable the "fast A20 gate"
+;
+a20_fast:
+               mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
+               in al, 092h
+               or al,02h
+               and al,~01h                     ; Don't accidentally reset the machine!
+               out 092h, al
+
+.fast_wait:    push cx
+               xor cx,cx
+.fast_wait_loop:
+               call a20_test
+               jnz a20_done_pop
+               loop .fast_wait_loop
+
+               pop cx
+
+;
+; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
+; and report failure to the user.
+;
+
+
+               dec byte [cs:A20Tries]
+               jnz try_enable_a20
+
+               mov si, err_a20
+               jmp abort_load
+;
+; A20 unmasked, proceed...
+;
+a20_done_pop:  pop cx
+a20_done:      popad
+               ret
+
+;
+; This routine tests if A20 is enabled (ZF = 0).  This routine
+; must not destroy any register contents.
+;
+a20_test:
+               push es
+               push cx
+               push ax
+               mov cx,0FFFFh           ; HMA = segment 0FFFFh
+               mov es,cx
+               mov cx,32               ; Loop count
+               mov ax,[cs:A20Test]
+.a20_wait:     inc ax
+               mov [cs:A20Test],ax
+               io_delay                ; Serialize, and fix delay
+               cmp ax,[es:A20Test+10h]
+               loopz .a20_wait
+.a20_done:     pop ax
+               pop cx
+               pop es
+               ret
+
+disable_a20:
+               pushad
+;
+; Flush the caches
+;
+;              call try_wbinvd
+
+               mov bp,[cs:A20Type]
+               add bp,bp                       ; Convert to word offset
+.adj5:         jmp word [cs:bp+A20DList]
+
+a20d_bios:
+               mov ax,2400h
+               pushf                           ; Some BIOSes muck with IF
+               int 15h
+               popf
+               jmp short a20d_snooze
+
+;
+; Disable the "fast A20 gate"
+;
+a20d_fast:
+               in al, 092h
+               and al,~03h
+               out 092h, al
+               jmp short a20d_snooze
+
+;
+; Disable the keyboard controller A20 gate
+;
+a20d_kbc:
+               call empty_8042_uncond
+               mov al,0D1h
+               out 064h, al            ; Command write
+               call empty_8042_uncond
+               mov al,0DDh             ; A20 off
+               out 060h, al
+               call empty_8042_uncond
+               ; Wait a bit for it to take effect
+a20d_snooze:
+               push cx
+               mov cx, disable_wait
+.delayloop:    call a20_test
+               jz .disabled
+               loop .delayloop
+.disabled:     pop cx
+a20d_dunno:
+a20d_none:
+               popad
+               ret
+
+;
+; Routine to empty the 8042 KBC controller.  If dl != 0
+; then we will test A20 in the loop and exit if A20 is
+; suddenly enabled.
+;
+empty_8042_uncond:
+               xor dl,dl
+empty_8042:
+               call a20_test
+               jz .a20_on
+               and dl,dl
+               jnz .done
+.a20_on:       io_delay
+               in al, 064h             ; Status port
+               test al,1
+               jz .no_output
+               io_delay
+               in al, 060h             ; Read input
+               jmp short empty_8042
+.no_output:
+               test al,2
+               jnz empty_8042
+               io_delay
+.done:         ret     
+
+;
+; WBINVD instruction; gets auto-eliminated on 386 CPUs
+;
+try_wbinvd:
+               wbinvd
+               ret
+
+;
+; bcopy_over_self:
+;
+; This routine is used to copy large blocks of code on top of
+; conventional memory (to 0:7c00).  We therefore have to move
+; necessary code into the trackbuf area before doing the copy,
+; and do adjustments to anything except BSS area references.
+;
+; After performing the copy, this routine resets the stack and
+; jumps to 0:7c00.
+;
+; Inputs:
+;      ESI, EDI, ECX   - same as bcopy
+;      EDX             - edx on invocation
+;      EAX             - esi on invocation
+;
+%define ADJUST (__bcopy_start - trackbuf)
+
+               align 2
+adjlist                dw bcopy_gdt.adj1 - ADJUST
+               dw bcopy.adj2 + 5 - ADJUST
+               dw bcopy.adj3 + 1 - ADJUST
+               dw try_enable_a20.adj4 + 3 - ADJUST
+               dw disable_a20.adj5 + 3 - ADJUST
+adjlist_cnt    equ ($-adjlist)/2
+
+bcopy_over_self:
+               cli
+               cld
+               xor bx,bx
+               mov ds,bx
+               mov es,bx
+               mov ss,bx
+               mov sp,7c00h
+
+               push eax
+               push edx
+               push esi        
+               push edi
+               push ecx
+
+               mov si,__bcopy_start
+               mov di,trackbuf
+               mov cx,(__bcopy_end - __bcopy_start + 3) >> 2
+               rep movsd
+
+               mov si,A20List - ADJUST
+               mov cx,a20_adjust_cnt
+.adjust1:
+               sub word [si], ADJUST
+               inc si
+               inc si
+               loop .adjust1
+
+               mov si, adjlist
+               mov cx, adjlist_cnt
+.adjust2:
+               lodsw
+               xchg di,ax
+               sub word [di], ADJUST
+               loop .adjust2
+
+               jmp .next-ADJUST
+.next:
+               pop ecx
+               pop edi
+               pop esi
+               call bcopy
+               
+               pop edx
+               pop esi
+               jmp 0:7c00h
+__bcopy_end:
diff --git a/comboot.inc b/comboot.inc
new file mode 100644 (file)
index 0000000..db19914
--- /dev/null
@@ -0,0 +1,116 @@
+;; $Id$
+;; -----------------------------------------------------------------------
+;;   
+;;   Copyright 1994-2002 H. Peter Anvin - All Rights Reserved
+;;
+;;   This program is free software; you can redistribute it and/or modify
+;;   it under the terms of the GNU General Public License as published by
+;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;;   Bostom MA 02111-1307, USA; either version 2 of the License, or
+;;   (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; comboot.inc
+;; 
+;; Common code for running a COMBOOT image
+;;
+
+;
+; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
+; except that it may, of course, not contain any DOS system calls.  We
+; do, however, allow the execution of INT 20h to return to SYSLINUX.
+;
+is_comboot_image:
+               and dx,dx
+               jnz near comboot_too_large
+               cmp ax,0ff00h           ; Max size in bytes
+               jae comboot_too_large
+
+               ;
+               ; Set up the DOS vectors in the IVT (INT 20h-3fh)
+               ;
+               mov dword [4*0x20],comboot_return       ; INT 20h vector
+               mov eax,comboot_bogus
+               mov di,4*0x21
+               mov cx,31               ; All remaining DOS vectors
+               rep stosd
+       
+               mov cx,comboot_seg
+               mov es,cx
+
+               mov bx,100h             ; Load at <seg>:0100h
+
+               mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
+               call getfssec
+
+               xor di,di
+               mov cx,64               ; 256 bytes (size of PSP)
+               xor eax,eax             ; Clear PSP
+               rep stosd
+
+               mov word [es:0], 020CDh ; INT 20h instruction
+               ; First non-free paragraph
+               mov word [es:02h], comboot_seg+1000h
+
+               ; Copy the command line from high memory
+               mov cx,125              ; Max cmdline len (minus space and CR)
+               mov si,[CmdOptPtr]
+               mov di,081h             ; Offset in PSP for command line
+               mov al,' '              ; DOS command lines begin with a space
+               stosb
+
+comboot_cmd_cp:        lodsb
+               and al,al
+               jz comboot_end_cmd
+               stosb
+               loop comboot_cmd_cp
+comboot_end_cmd: mov al,0Dh            ; CR after last character
+               stosb
+               mov al,126              ; Include space but not CR
+               sub al,cl
+               mov [es:80h], al        ; Store command line length
+
+               mov [SavedSSSP],sp
+               mov ax,ss               ; Save away SS:SP
+               mov [SavedSSSP+2],ax
+
+               call vgaclearmode       ; Reset video
+
+               mov ax,es
+               mov ds,ax
+               mov ss,ax
+               xor sp,sp
+               push word 0             ; Return to address 0 -> exit
+
+               jmp comboot_seg:100h    ; Run it
+
+; Looks like a COMBOOT image but too large
+comboot_too_large:
+               mov si,err_comlarge
+               call cwritestr
+cb_enter:      jmp enter_command
+
+; Proper return vector
+comboot_return:        cli                     ; Don't trust anyone
+               lss sp,[cs:SavedSSSP]
+               mov ds,ax
+               mov es,ax
+               sti
+               cld
+               jmp short cb_enter
+
+; Attempted to execute DOS system call
+comboot_bogus: cli                     ; Don't trust anyone
+               lss sp,[cs:SavedSSSP]
+               mov ds,ax
+               mov es,ax
+               sti
+               cld
+               mov si,KernelCName
+               call cwritestr
+               mov si,err_notdos
+               call cwritestr
+               jmp short cb_enter
+
index 48f8d25..f560e11 100644 (file)
@@ -33,8 +33,9 @@ my_id         equ isolinux_id
 max_cmd_len    equ 255                 ; Must be odd; 255 is the kernel limit
 FILENAME_MAX_LG2 equ 8                 ; log2(Max filename size Including final null)
 FILENAME_MAX   equ (1 << FILENAME_MAX_LG2)
+NULLFILE       equ 0                   ; Zero byte == null file name
 HIGHMEM_MAX    equ 037FFFFFFh          ; DEFAULT highest address for an initrd
-HIGHMEM_SLOP   equ 128*1024            ; Avoid this much memory near the top
+%assign HIGHMEM_SLOP 128*1024          ; Avoid this much memory near the top
 DEFAULT_BAUD   equ 9600                ; Default baud rate for serial port
 BAUD_DIVISOR   equ 115200              ; Serial port parameter
 MAX_OPEN_LG2   equ 6                   ; log2(Max number of open files)
@@ -153,9 +154,9 @@ HiLoadAddr      resd 1                      ; Address pointer for high load loop
 HighMemSize    resd 1                  ; End of memory pointer (bytes)
 RamdiskMax     resd 1                  ; Highest address for a ramdisk
 KernelSize     resd 1                  ; Size of kernel (bytes)
+SavedSSSP      resd 1                  ; Our SS:SP while running a COMBOOT image
 RootDir                resb dir_t_size         ; Root directory
 CurDir         resb dir_t_size         ; Current directory
-SavedSSSP      resd 1                  ; Our SS:SP while running a COMBOOT image
 KernelClust    resd 1                  ; Kernel size in clusters
 InitStack      resd 1                  ; Initial stack pointer (SS:SP)
 FirstSecSum    resd 1                  ; Checksum of bytes 64-2048
@@ -1238,655 +1239,16 @@ kernel_good:
                cmp ecx,'.bs'
                je near is_bootsector
                ; Otherwise Linux kernel
-;
-; A Linux kernel consists of three parts: boot sector, setup code, and
-; kernel code. The boot sector is never executed when using an external
-; booting utility, but it contains some status bytes that are necessary.
-;
-; First check that our kernel is at least 1K and less than 8M (if it is
-; more than 8M, we need to change the logic for loading it anyway...)
-;
-; We used to require the kernel to be 64K or larger, but it has gotten
-; popular to use the Linux kernel format for other things, which may
-; not be so large.
-;
-is_linux_kernel:
-                cmp dx,80h                     ; 8 megs
-               ja kernel_corrupt
-               and dx,dx
-               jnz kernel_sane
-               cmp ax,1024                     ; Bootsect + 1 setup sect
-               jb near kernel_corrupt
-kernel_sane:   push ax
-               push dx
-               push si
-               mov si,loading_msg
-                call cwritestr
-;
-; Now start transferring the kernel
-;
-               push word real_mode_seg
-               pop es
-
-               movzx eax,ax                    ; Fix this by using a 32-bit
-               shl edx,16                      ; register for the kernel size
-               or eax,edx
-               mov [KernelSize],eax
-               xor edx,edx
-               div dword [ClustSize]           ; # of clusters total
-               ; Round up...
-               add edx,byte -1                 ; Sets CF if EDX >= 1
-               adc eax,byte 0                  ; Add 1 to EAX if CF set
-                mov [KernelClust],eax
-
-;
-; Now, if we transfer these straight, we'll hit 64K boundaries.         Hence we
-; have to see if we're loading more than 64K, and if so, load it step by
-; step.
-;
-
-;
-; Start by loading the bootsector/setup code, to see if we need to
-; do something funky.  It should fit in the first 32K (loading 64K won't
-; work since we might have funny stuff up near the end of memory).
-; If we have larger than 32K clusters, yes, we're hosed.
-;
-               call abort_check                ; Check for abort key
-               mov ecx,[ClustPerMoby]
-               shr ecx,1                       ; Half a moby
-               cmp ecx,[KernelClust]
-               jna .normalkernel
-               mov ecx,[KernelClust]
-.normalkernel:
-               sub [KernelClust],ecx
-               xor bx,bx
-                pop si                          ; Cluster pointer on stack
-               call getfssec
-                cmp word [es:bs_bootsign],0AA55h
-               jne near kernel_corrupt         ; Boot sec signature missing
-;
-; Get the BIOS' idea of what the size of high memory is.
-;
-               push si                         ; Save our cluster pointer!
-;
-; First, try INT 15:E820 (get BIOS memory map)
-;
-get_e820:
-               push es
-               xor ebx,ebx                     ; Start with first record
-               mov es,bx                       ; Need ES = DS = 0 for now
-               jmp short .do_e820              ; Skip "at end" check first time!
-.int_loop:     and ebx,ebx                     ; If we're back at beginning...
-               jz no_e820                      ; ... bail; nothing found
-.do_e820:      mov eax,0000E820h
-               mov edx,534D4150h               ; "SMAP" backwards
-               mov ecx,20
-               mov di,E820Buf
-               int 15h
-               jc no_e820
-               cmp eax,534D4150h
-               jne no_e820
-;
-; Look for a memory block starting at <= 1 MB and continuing upward
-;
-               cmp dword [E820Buf+4], byte 0
-               ja .int_loop                    ; Start >= 4 GB?
-               mov edx, (1 << 20)
-               sub edx, [E820Buf]
-               jb .int_loop                    ; Start >= 1 MB?
-               mov eax, 0FFFFFFFFh
-               cmp dword [E820Buf+12], byte 0
-               ja .huge                        ; Size >= 4 GB
-               mov eax, [E820Buf+8]
-.huge:         sub eax, edx                    ; Adjust size to start at 1 MB
-               jbe .int_loop                   ; Completely below 1 MB?
-
-               ; Now EAX contains the size of memory 1 MB...up
-               cmp dword [E820Buf+16], byte 1
-               jne near err_nohighmem          ; High memory isn't usable memory!!!!
-
-               ; We're good!
-               pop es
-               jmp short got_highmem_add1mb    ; Still need to add low 1 MB
-
-;
-; INT 15:E820 failed.  Try INT 15:E801.
-;
-no_e820:       pop es
-
-               mov ax,0e801h                   ; Query high memory (semi-recent)
-               int 15h
-               jc no_e801
-               cmp ax,3c00h
-               ja no_e801                      ; > 3C00h something's wrong with this call
-               jb e801_hole                    ; If memory hole we can only use low part
-
-               mov ax,bx
-               shl eax,16                      ; 64K chunks
-               add eax,(16 << 20)              ; Add first 16M
-               jmp short got_highmem                           
-
-;
-; INT 15:E801 failed.  Try INT 15:88.
-;
-no_e801:
-               mov ah,88h                      ; Query high memory (oldest)
-               int 15h
-               cmp ax,14*1024                  ; Don't trust memory >15M
-               jna e801_hole
-               mov ax,14*1024
-e801_hole:
-               and eax,0ffffh
-               shl eax,10                      ; Convert from kilobytes
-got_highmem_add1mb:
-               add eax,(1 << 20)               ; First megabyte
-got_highmem:
-               sub eax,HIGHMEM_SLOP
-               mov [HighMemSize],eax
-
-;
-; Construct the command line (append options have already been copied)
-;
-construct_cmdline:
-               mov di,[CmdLinePtr]
-                mov si,boot_image              ; BOOT_IMAGE=
-                mov cx,boot_image_len
-                rep movsb
-                mov si,KernelCName             ; Unmangled kernel name
-                mov cx,[KernelCNameLen]
-                rep movsb
-                mov al,' '                      ; Space
-                stosb
-
-.noipappend:
-                mov si,[CmdOptPtr]              ; Options from user input
-               mov cx,(kern_cmd_len+3) >> 2
-               rep movsd
-;
-; Scan through the command line for anything that looks like we might be
-; interested in.  The original version of this code automatically assumed
-; the first option was BOOT_IMAGE=, but that is no longer certain.
-;
-               mov si,cmd_line_here
-                mov byte [initrd_flag],0
-                push es                                ; Set DS <- real_mode_seg
-                pop ds
-get_next_opt:   lodsb
-               and al,al
-               jz near cmdline_end
-               cmp al,' '
-               jbe get_next_opt
-               dec si
-                mov eax,[si]
-                cmp eax,'vga='
-               je is_vga_cmd
-                cmp eax,'mem='
-               je is_mem_cmd
-                push es                         ; Save ES -> real_mode_seg
-                push cs
-                pop es                          ; Set ES <- normal DS
-                mov di,initrd_cmd
-               mov cx,initrd_cmd_len
-               repe cmpsb
-                jne not_initrd
-               mov di,InitRD
-                push si                         ; mangle_dir mangles si
-                call mangle_name                ; Mangle ramdisk name
-                pop si
-               cmp byte [es:InitRD],0          ; Null filename?
-                seta byte [es:initrd_flag]     ; Set flag if not
-not_initrd:    pop es                          ; Restore ES -> real_mode_seg
-skip_this_opt:  lodsb                           ; Load from command line
-                cmp al,' '
-                ja skip_this_opt
-                dec si
-                jmp short get_next_opt
-is_vga_cmd:
-                add si,byte 4
-                mov eax,[si]
-                mov bx,-1
-                cmp eax, 'norm'                 ; vga=normal
-                je vc0
-                and eax,0ffffffh               ; 3 bytes
-                mov bx,-2
-                cmp eax, 'ext'                  ; vga=ext
-                je vc0
-                mov bx,-3
-                cmp eax, 'ask'                  ; vga=ask
-                je vc0
-                call parseint                   ; vga=<number>
-               jc skip_this_opt                ; Not an integer
-vc0:           mov [bs_vidmode],bx             ; Set video mode
-               jmp short skip_this_opt
-is_mem_cmd:
-                add si,byte 4
-                call parseint
-               jc skip_this_opt                ; Not an integer
-               sub ebx,HIGHMEM_SLOP
-               mov [cs:HighMemSize],ebx
-               jmp short skip_this_opt
-cmdline_end:
-                push cs                         ; Restore standard DS
-                pop ds
-               sub si,cmd_line_here
-               mov [CmdLineLen],si             ; Length including final null
-;
-; Now check if we have a large kernel, which needs to be loaded high
-;
-               mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
-               cmp dword [es:su_header],HEADER_ID      ; New setup code ID
-               jne near old_kernel             ; Old kernel, load low
-               cmp word [es:su_version],0200h  ; Setup code version 2.0
-               jb near old_kernel              ; Old kernel, load low
-                cmp word [es:su_version],0201h ; Version 2.01+?
-                jb new_kernel                   ; If 2.00, skip this step
-                mov word [es:su_heapend],linux_stack   ; Set up the heap
-                or byte [es:su_loadflags],80h  ; Let the kernel know we care
-               cmp word [es:su_version],0203h  ; Version 2.03+?
-               jb new_kernel                   ; Not 2.03+
-               mov eax,[es:su_ramdisk_max]
-               mov [RamdiskMax],eax            ; Set the ramdisk limit
-
-;
-; We definitely have a new-style kernel.  Let the kernel know who we are,
-; and that we are clueful
-;
-new_kernel:
-               mov byte [es:su_loader],my_id   ; Show some ID
-               movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
-               mov [SetupSecs],ax
-;
-; About to load the kernel.  This is a modern kernel, so use the boot flags
-; we were provided.
-;
-                mov al,[es:su_loadflags]
-               mov [LoadFlags],al
-;
-; Load the kernel.  We always load it at 100000h even if we're supposed to
-; load it "low"; for a "low" load we copy it down to low memory right before
-; jumping to it.
-;
-read_kernel:
-                mov si,KernelCName             ; Print kernel name part of
-                call cwritestr                  ; "Loading" message
-                mov si,dotdot_msg              ; Print dots
-                call cwritestr
-
-                mov eax,[HighMemSize]
-               sub eax,100000h                 ; Load address
-               cmp eax,[KernelSize]
-               jb near no_high_mem             ; Not enough high memory
-;
-; Move the stuff beyond the setup code to high memory at 100000h
-;
-               movzx esi,word [SetupSecs]      ; Setup sectors
-               inc esi                         ; plus 1 boot sector
-                shl esi,9                      ; Convert to bytes
-                mov ecx,8000h                  ; 32K
-               sub ecx,esi                     ; Number of bytes to copy
-               push ecx
-               shr ecx,2                       ; Convert to dwords
-               add esi,(real_mode_seg << 4)    ; Pointer to source
-                mov edi,100000h                 ; Copy to address 100000h
-                call bcopy                     ; Transfer to high memory
-
-               ; On exit EDI -> where to load the rest
-
-                mov si,dot_msg                 ; Progress report
-                call cwritestr
-                call abort_check
-
-               pop ecx                         ; Number of bytes in the initial portion
-               pop si                          ; Restore file handle/cluster pointer
-               mov eax,[KernelSize]
-               sub eax,ecx                     ; Amount of kernel left over
-               jbe high_load_done              ; Zero left (tiny kernel)
-
-               call load_high                  ; Copy the file
-
-high_load_done:
-                mov ax,real_mode_seg           ; Set to real mode seg
-                mov es,ax
-
-                mov si,dot_msg
-                call cwritestr
-
-;
-; Now see if we have an initial RAMdisk; if so, do requisite computation
-; We know we have a new kernel; the old_kernel code already will have objected
-; if we tried to load initrd using an old kernel
-;
-load_initrd:
-                test byte [initrd_flag],1
-                jz near nk_noinitrd
-                push es                         ; ES->real_mode_seg
-                push ds
-                pop es                          ; We need ES==DS
-                mov si,InitRD
-                mov di,InitRDCName
-                call unmangle_name              ; Create human-readable name
-                sub di,InitRDCName
-                mov [InitRDCNameLen],di
-                mov di,InitRD
-                call searchdir                  ; Look for it in directory
-                pop es
-               jz initrd_notthere
-               mov [es:su_ramdisklen1],ax      ; Ram disk length
-               mov [es:su_ramdisklen2],dx
-               mov edx,[HighMemSize]           ; End of memory
-               dec edx
-               mov eax,[RamdiskMax]            ; Highest address allowed by kernel
-               cmp edx,eax
-               jna memsize_ok
-               mov edx,eax                     ; Adjust to fit inside limit
-memsize_ok:
-               inc edx
-                xor dx,dx                      ; Round down to 64K boundary
-               sub edx,[es:su_ramdisklen]      ; Subtract size of ramdisk
-                xor dx,dx                      ; Round down to 64K boundary
-                mov [es:su_ramdiskat],edx      ; Load address
-               call loadinitrd                 ; Load initial ramdisk
-               jmp short initrd_end
-
-initrd_notthere:
-                mov si,err_noinitrd
-                call cwritestr
-                mov si,InitRDCName
-                call cwritestr
-                mov si,crlf_msg
-                jmp abort_load
-
-no_high_mem:    mov si,err_nohighmem           ; Error routine
-                jmp abort_load
-
-initrd_end:
-nk_noinitrd:
-;
-; Abandon hope, ye that enter here!  We do no longer permit aborts.
-;
-                call abort_check               ; Last chance!!
-
-               mov si,ready_msg
-               call cwritestr
-
-               call vgaclearmode               ; We can't trust ourselves after this
-
-               cli
-               xor ax,ax
-               mov ss,ax
-               mov sp,7C00h                    ; Set up a more normal stack
-               
-;
-; Now, if we were supposed to load "low", copy the kernel down to 10000h
-; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
-; capable of starting their setup from a different address.
-;
-               mov ax,real_mode_seg
-               mov fs,ax
-
-;
-; Copy command line.  Unfortunately, the kernel boot protocol requires
-; the command line to exist in the 9xxxxh range even if the rest of the
-; setup doesn't.
-;
-               cli                             ; In case of hooked interrupts
-               test byte [LoadFlags],LOAD_HIGH
-               jz need_high_cmdline
-               cmp word [fs:su_version],0202h  ; Support new cmdline protocol?
-               jb need_high_cmdline
-               ; New cmdline protocol
-               ; Store 32-bit (flat) pointer to command line
-               mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
-               jmp short in_proper_place
-
-need_high_cmdline:
-;
-; Copy command line up to 90000h
-;
-               mov ax,9000h
-               mov es,ax
-               mov si,cmd_line_here
-               mov di,si
-               mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
-               mov [fs:kern_cmd_offset],di     ; Store pointer
-
-               mov cx,[CmdLineLen]
-               add cx,byte 3
-               shr cx,2                        ; Convert to dwords
-               fs rep movsd
-
-               push fs
-               pop es
-
-               test byte [LoadFlags],LOAD_HIGH
-               jnz in_proper_place             ; If high load, we're done
 
 ;
-; Loading low; we can't assume it's safe to run in place.
-;
-; Copy real_mode stuff up to 90000h
-;
-               mov ax,9000h
-               mov es,ax
-               mov cx,[SetupSecs]
-               inc cx                          ; Setup + boot sector
-               shl cx,7                        ; Sectors -> dwords
-               xor si,si
-               xor di,di
-               fs rep movsd                    ; Copy setup + boot sector
-;
-; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
-; setup sectors, but the boot protocol had not yet been defined.  They
-; rely on a signature to figure out if they need to copy stuff from
-; the "protected mode" kernel area.  Unfortunately, we used that area
-; as a transfer buffer, so it's going to find the signature there.
-; Hence, zero the low 32K beyond the setup area.
-;
-               mov di,[SetupSecs]
-               inc di                          ; Setup + boot sector
-               mov cx,32768/512                ; Sectors/32K
-               sub cx,di                       ; Remaining sectors
-               shl di,9                        ; Sectors -> bytes
-               shl cx,7                        ; Sectors -> dwords
-               xor eax,eax
-               rep stosd                       ; Clear region
-;
-; Copy the kernel down to the "low" location
-;
-               mov ecx,[KernelSize]
-               add ecx,3                       ; Round upwards
-               shr ecx,2                       ; Bytes -> dwords
-               mov esi,100000h
-               mov edi,10000h
-               call bcopy
-
-;
-; Now everything is where it needs to be...
-;
-; When we get here, es points to the final segment, either
-; 9000h or real_mode_seg
-;
-in_proper_place:
-
-;
-; If the default root device is set to FLOPPY (0000h), change to
-; /dev/fd0 (0200h)
-;
-               cmp word [es:bs_rootdev],byte 0
-               jne root_not_floppy
-               mov word [es:bs_rootdev],0200h
-root_not_floppy:
-;
-; Copy the disk table to high memory, then re-initialize the floppy
-; controller
-;
-; This needs to be moved before the copy
-;
-%if 0
-               push ds
-               push bx
-               lds si,[fdctab]
-               mov di,linux_fdctab
-               mov cx,3                        ; 12 bytes
-               push di
-               rep movsd
-               pop di
-               mov [fdctab1],di                ; Save new floppy tab pos
-               mov [fdctab2],es
-               xor ax,ax
-               xor dx,dx
-               int 13h
-               pop bx
-               pop ds
-%endif
-;
-; Linux wants the floppy motor shut off before starting the kernel,
-; at least bootsect.S seems to imply so
-;
-kill_motor:
-               mov dx,03F2h
-               xor al,al
-               call slow_out
-;
-; If we're debugging, wait for a keypress so we can read any debug messages
-;
-%ifdef debug
-                xor ax,ax
-                int 16h
-%endif
-;
-; Set up segment registers and the Linux real-mode stack
-; Note: es == the real mode segment
-;
-               cli
-               mov bx,es
-               mov ds,bx
-               mov fs,bx
-               mov gs,bx
-               mov ss,bx
-               mov sp,linux_stack
-;
-; We're done... now RUN THAT KERNEL!!!!
-; Setup segment == real mode segment + 020h; we need to jump to offset
-; zero in the real mode segment.
+; Linux kernel loading code is common.
 ;
-               add bx,020h
-               push bx
-               push word 0h
-               retf
+%include "runkernel.inc"
 
 ;
-; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
-; initrd, and are always loaded low.
+; COMBOOT-loading code
 ;
-old_kernel:
-                test byte [initrd_flag],1      ; Old kernel can't have initrd
-                jz load_old_kernel
-                mov si,err_oldkernel
-                jmp abort_load
-load_old_kernel:
-               mov word [SetupSecs],4          ; Always 4 setup sectors
-               mov byte [LoadFlags],0          ; Always low
-               jmp read_kernel
-
-;
-; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
-; except that it may, of course, not contain any DOS system calls.  We
-; do, however, allow the execution of INT 20h to return to SYSLINUX.
-;
-is_comboot_image:
-               and dx,dx
-               jnz near comboot_too_large
-               cmp ax,0ff00h           ; Max size in bytes
-               jae comboot_too_large
-
-               ;
-               ; Set up the DOS vectors in the IVT (INT 20h-3fh)
-               ;
-               mov dword [4*0x20],comboot_return       ; INT 20h vector
-               mov eax,comboot_bogus
-               mov di,4*0x21
-               mov cx,31               ; All remaining DOS vectors
-               rep stosd
-       
-               mov cx,comboot_seg
-               mov es,cx
-
-               mov bx,100h             ; Load at <seg>:0100h
-
-               mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
-               call getfssec
-
-               xor di,di
-               mov cx,64               ; 256 bytes (size of PSP)
-               xor eax,eax             ; Clear PSP
-               rep stosd
-
-               mov word [es:0], 020CDh ; INT 20h instruction
-               ; First non-free paragraph
-               mov word [es:02h], comboot_seg+1000h
-
-               ; Copy the command line from high memory
-               mov cx,125              ; Max cmdline len (minus space and CR)
-               mov si,[CmdOptPtr]
-               mov di,081h             ; Offset in PSP for command line
-               mov al,' '              ; DOS command lines begin with a space
-               stosb
-
-comboot_cmd_cp:        lodsb
-               and al,al
-               jz comboot_end_cmd
-               stosb
-               loop comboot_cmd_cp
-comboot_end_cmd: mov al,0Dh            ; CR after last character
-               stosb
-               mov al,126              ; Include space but not CR
-               sub al,cl
-               mov [es:80h], al        ; Store command line length
-
-               mov [SavedSSSP],sp
-               mov ax,ss               ; Save away SS:SP
-               mov [SavedSSSP+2],ax
-
-               call vgaclearmode       ; Reset video
-
-               mov ax,es
-               mov ds,ax
-               mov ss,ax
-               xor sp,sp
-               push word 0             ; Return to address 0 -> exit
-
-               jmp comboot_seg:100h    ; Run it
-
-; Looks like a COMBOOT image but too large
-comboot_too_large:
-               mov si,err_comlarge
-               call cwritestr
-cb_enter:      jmp enter_command
-
-; Proper return vector
-comboot_return:        cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ds,ax
-               mov es,ax
-               lss sp,[SavedSSSP]
-               sti
-               cld
-               jmp short cb_enter
-
-; Attempted to execute DOS system call
-comboot_bogus: cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ds,ax
-               mov es,ax
-               lss sp,[SavedSSSP]
-               sti
-               cld
-               mov si,KernelCName
-               call cwritestr
-               mov si,err_notdos
-               call cwritestr
-               jmp short cb_enter
+%include "comboot.inc"
 
 ;
 ; Load a boot sector
@@ -2068,424 +1430,6 @@ local_boot:
                jmp kaboom                      ; If we returned, oh boy...
 
 ;
-; 32-bit bcopy routine for real mode
-;
-; We enter protected mode, set up a flat 32-bit environment, run rep movsd
-; and then exit.  IMPORTANT: This code assumes cs == 0.
-;
-; This code is probably excessively anal-retentive in its handling of
-; segments, but this stuff is painful enough as it is without having to rely
-; on everything happening "as it ought to."
-;
-               align 4
-bcopy_gdt:     dw bcopy_gdt_size-1     ; Null descriptor - contains GDT
-               dd bcopy_gdt            ; pointer for LGDT instruction
-               dw 0
-               dd 0000ffffh            ; Code segment, use16, readable,
-               dd 00009b00h            ; present, dpl 0, cover 64K
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 008f9300h            ; present, dpl 0, cover all 4G
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 00009300h            ; present, dpl 0, cover 64K
-bcopy_gdt_size:        equ $-bcopy_gdt
-
-bcopy:         push eax
-               pushf                   ; Saves, among others, the IF flag
-               push gs
-               push fs
-               push ds
-               push es
-               mov [cs:SavedSSSP],sp
-               mov ax,ss
-               mov [cs:SavedSSSP+2],ax
-
-               cli
-               call enable_a20
-
-               o32 lgdt [cs:bcopy_gdt]
-               mov eax,cr0
-               or al,1
-               mov cr0,eax             ; Enter protected mode
-               jmp 08h:.in_pm
-
-.in_pm:                mov ax,10h              ; Data segment selector
-               mov es,ax
-               mov ds,ax
-
-               mov al,18h              ; "Real-mode-like" data segment
-               mov ss,ax
-               mov fs,ax
-               mov gs,ax       
-       
-               a32 rep movsd           ; Do our business
-               
-               mov es,ax               ; Set to "real-mode-like"
-               mov ds,ax
-       
-               mov eax,cr0
-               and al,~1
-               mov cr0,eax             ; Disable protected mode
-               jmp 0:.in_rm
-
-.in_rm:                ; Back in real mode
-               lss sp,[cs:SavedSSSP]
-               pop es
-               pop ds
-               pop fs
-               pop gs
-               call disable_a20
-
-               popf                    ; Re-enables interrupts
-               pop eax
-               ret
-
-;
-; Routines to enable and disable (yuck) A20.  These routines are gathered
-; from tips from a couple of sources, including the Linux kernel and
-; http://www.x86.org/.  The need for the delay to be as large as given here
-; is indicated by Donnie Barnes of RedHat, the problematic system being an
-; IBM ThinkPad 760EL.
-;
-; We typically toggle A20 twice for every 64K transferred.
-; 
-%define        io_delay        call _io_delay
-%define IO_DELAY_PORT  80h             ; Invalid port (we hope!)
-%define disable_wait   32              ; How long to wait for a disable
-
-%define A20_DUNNO      0               ; A20 type unknown
-%define A20_NONE       1               ; A20 always on?
-%define A20_BIOS       2               ; A20 BIOS enable
-%define A20_KBC                3               ; A20 through KBC
-%define A20_FAST       4               ; A20 through port 92h
-
-slow_out:      out dx, al              ; Fall through
-
-_io_delay:     out IO_DELAY_PORT,al
-               out IO_DELAY_PORT,al
-               ret
-
-enable_a20:
-               pushad
-               mov byte [cs:A20Tries],255 ; Times to try to make this work
-
-try_enable_a20:
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-;
-; If the A20 type is known, jump straight to type
-;
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20List]
-
-;
-; First, see if we are on a system with no A20 gate
-;
-a20_dunno:
-a20_none:
-               mov byte [cs:A20Type], A20_NONE
-               call a20_test
-               jnz a20_done
-
-;
-; Next, try the BIOS (INT 15h AX=2401h)
-;
-a20_bios:
-               mov byte [cs:A20Type], A20_BIOS
-               mov ax,2401h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-
-               call a20_test
-               jnz a20_done
-
-;
-; Enable the keyboard controller A20 gate
-;
-a20_kbc:
-               mov dl, 1                       ; Allow early exit
-               call empty_8042
-               jnz a20_done                    ; A20 live, no need to use KBC
-
-               mov byte [cs:A20Type], A20_KBC  ; Starting KBC command sequence
-
-               mov al,0D1h                     ; Command write
-               out 064h, al
-               call empty_8042_uncond
-
-               mov al,0DFh                     ; A20 on
-               out 060h, al
-               call empty_8042_uncond
-
-               ; Verify that A20 actually is enabled.  Do that by
-               ; observing a word in low memory and the same word in
-               ; the HMA until they are no longer coherent.  Note that
-               ; we don't do the same check in the disable case, because
-               ; we don't want to *require* A20 masking (SYSLINUX should
-               ; work fine without it, if the BIOS does.)
-.kbc_wait:     push cx
-               xor cx,cx
-.kbc_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .kbc_wait_loop
-
-               pop cx
-;
-; Running out of options here.  Final attempt: enable the "fast A20 gate"
-;
-a20_fast:
-               mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
-               in al, 092h
-               or al,02h
-               and al,~01h                     ; Don't accidentally reset the machine!
-               out 092h, al
-
-.fast_wait:    push cx
-               xor cx,cx
-.fast_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .fast_wait_loop
-
-               pop cx
-
-;
-; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
-; and report failure to the user.
-;
-
-
-               dec byte [cs:A20Tries]
-               jnz try_enable_a20
-
-               mov si, err_a20
-               jmp abort_load
-;
-; A20 unmasked, proceed...
-;
-a20_done_pop:  pop cx
-a20_done:      popad
-               ret
-
-;
-; This routine tests if A20 is enabled (ZF = 0).  This routine
-; must not destroy any register contents.
-;
-a20_test:
-               push es
-               push cx
-               push ax
-               mov cx,0FFFFh           ; HMA = segment 0FFFFh
-               mov es,cx
-               mov cx,32               ; Loop count
-               mov ax,[cs:A20Test]
-.a20_wait:     inc ax
-               mov [cs:A20Test],ax
-               io_delay                ; Serialize, and fix delay
-               cmp ax,[es:A20Test+10h]
-               loopz .a20_wait
-.a20_done:     pop ax
-               pop cx
-               pop es
-               ret
-
-disable_a20:
-               pushad
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20DList]
-
-a20d_bios:
-               mov ax,2400h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-               jmp short a20d_snooze
-
-;
-; Disable the "fast A20 gate"
-;
-a20d_fast:
-               in al, 092h
-               and al,~03h
-               out 092h, al
-               jmp short a20d_snooze
-
-;
-; Disable the keyboard controller A20 gate
-;
-a20d_kbc:
-               call empty_8042_uncond
-               mov al,0D1h
-               out 064h, al            ; Command write
-               call empty_8042_uncond
-               mov al,0DDh             ; A20 off
-               out 060h, al
-               call empty_8042_uncond
-               ; Wait a bit for it to take effect
-a20d_snooze:
-               push cx
-               mov cx, disable_wait
-.delayloop:    call a20_test
-               jz .disabled
-               loop .delayloop
-.disabled:     pop cx
-a20d_dunno:
-a20d_none:
-               popad
-               ret
-
-;
-; Routine to empty the 8042 KBC controller.  If dl != 0
-; then we will test A20 in the loop and exit if A20 is
-; suddenly enabled.
-;
-empty_8042_uncond:
-               xor dl,dl
-empty_8042:
-               call a20_test
-               jz .a20_on
-               and dl,dl
-               jnz .done
-.a20_on:       io_delay
-               in al, 064h             ; Status port
-               test al,1
-               jz .no_output
-               io_delay
-               in al, 060h             ; Read input
-               jmp short empty_8042
-.no_output:
-               test al,2
-               jnz empty_8042
-               io_delay
-.done:         ret     
-
-;
-; WBINVD instruction; gets auto-eliminated on 386 CPUs
-;
-try_wbinvd:
-               wbinvd
-               ret
-
-;
-; Load RAM disk into high memory
-;
-; Need to be set:
-;      su_ramdiskat    - Where in memory to load
-;      su_ramdisklen   - Size of file
-;      SI              - initrd filehandle/cluster pointer
-;
-loadinitrd:
-                push es                         ; Save ES on entry
-               mov ax,real_mode_seg
-                mov es,ax
-                mov edi,[es:su_ramdiskat]      ; initrd load address
-               push si
-               mov si,crlfloading_msg          ; Write "Loading "
-               call cwritestr
-                mov si,InitRDCName             ; Write ramdisk name
-                call cwritestr
-                mov si,dotdot_msg              ; Write dots
-                call cwritestr
-               pop si
-
-               mov eax,[es:su_ramdisklen]
-               call load_high                  ; Load the file
-
-               call crlf
-                pop es                          ; Restore original ES
-                ret
-
-;
-; load_high:   loads (the remainder of) a file into high memory.
-;              This routine prints dots for each 64K transferred, and
-;              calls abort_check periodically.
-; 
-;              The xfer_buf_seg is used as a bounce buffer.
-;
-;              The input address (EDI) should be dword aligned, and the final
-;              dword written is padded with zeroes if necessary.
-;
-; Inputs:      SI  = file handle/cluster pointer
-;              EDI = target address in high memory
-;              EAX = size of remaining file in bytes
-;
-; Outputs:     SI  = file handle/cluster pointer
-;              EDI = first untouched address (not including padding)
-;
-load_high:
-               push es
-
-               mov bx,xfer_buf_seg
-               mov es,bx
-
-.read_loop:
-               and si,si                       ; If SI == 0 then we have end of file
-               jz .eof
-               push si
-               mov si,dot_msg
-               call cwritestr
-               pop si
-               call abort_check
-
-               push eax                        ; <A> Total bytes to transfer
-               cmp eax,(1 << 16)               ; Max 64K in one transfer
-               jna .size_ok
-               mov eax,(1 << 16)
-.size_ok:
-               xor edx,edx
-               push eax                        ; <B> Bytes transferred this chunk
-               movzx ecx,word [ClustSize]
-               div ecx                         ; Convert to clusters
-               ; Round up...
-               add edx,byte -1                 ; Sets CF if EDX >= 1
-               adc eax,byte 0                  ; Add 1 to EAX if CF set
-
-               ; Now (e)ax contains the number of clusters to get
-               push edi                        ; <C> Target buffer
-               mov cx,ax
-               xor bx,bx                       ; ES:0
-               call getfssec                   ; Load the data into xfer_buf_seg
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               push ecx                        ; <B> Byte count this round 
-               push edi                        ; <C> Target buffer
-.fix_slop:
-               test cl,3
-               jz .noslop
-               ; The last dword fractional - pad with zeroes
-               ; Zero-padding is critical for multi-file initramfs.
-               mov byte [es:ecx],0
-               inc ecx
-               jmp short .fix_slop
-.noslop:
-               shr ecx,2                       ; Convert to dwords
-               push esi                        ; <D> File handle/cluster pointer
-               mov esi,(xfer_buf_seg << 4)     ; Source address
-               call bcopy                      ; Copy to high memory
-               pop esi                         ; <D> File handle/cluster pointer
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               pop eax                         ; <A> Total bytes to transfer
-               add edi,ecx
-               sub eax,ecx
-               jnz .read_loop                  ; More to read...
-               
-.eof:
-               pop es
-               ret
-
-;
 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
 ;
 abort_check:
@@ -2924,6 +1868,8 @@ getfssec:
 %include "conio.inc"           ; Console I/O
 %include "parseconfig.inc"     ; High-level config file handling
 %include "parsecmd.inc"                ; Low-level config file handling
+%include "bcopy32.inc"         ; 32-bit bcopy
+%include "loadhigh.inc"                ; Load a file into high memory
 %include "font.inc"            ; VGA font stuff
 %include "graphics.inc"                ; VGA graphics
 
@@ -3080,9 +2026,6 @@ ForcePrompt       dw 0                    ; Force prompt
 AllowImplicit   dw 1                    ; Allow implicit kernels
 SerialPort     dw 0                    ; Serial port base (or 0 for no serial port)
 NextSocket     dw 49152                ; Counter for allocating socket numbers
-A20List                dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
-A20DList       dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
-A20Type                dw A20_DUNNO            ; A20 type unknown
 VGAFontSize    dw 16                   ; Defaults to 16 byte font
 UserFont       db 0                    ; Using a user-specified font
 ScrollAttribute        db 07h                  ; White on black (for text mode)
index cd22f42..af4243a 100644 (file)
@@ -40,8 +40,10 @@ my_id                equ syslinux_id
 max_cmd_len    equ 255                 ; Must be odd; 255 is the kernel limit
 FILENAME_MAX_LG2 equ 4                 ; log2(Max filename size Including final null)
 FILENAME_MAX   equ 11                  ; Max mangled filename size
+NULLFILE       equ ' '                 ; First char space == null filename
 retry_count    equ 6                   ; How patient are we with the disk?
 HIGHMEM_MAX    equ 037FFFFFFh          ; DEFAULT highest address for an initrd
+%assign HIGHMEM_SLOP 0                 ; Avoid this much memory near the top
 DEFAULT_BAUD   equ 9600                ; Default baud rate for serial port
 BAUD_DIVISOR   equ 115200              ; Serial port parameter
 ;
@@ -133,6 +135,7 @@ HiLoadAddr      resd 1                      ; Address pointer for high load loop
 HighMemSize    resd 1                  ; End of memory pointer (bytes)
 RamdiskMax     resd 1                  ; Highest address for a ramdisk
 KernelSize     resd 1                  ; Size of kernel (bytes)
+SavedSSSP      resd 1                  ; Our SS:SP while running a COMBOOT image
 KernelName      resb 12                        ; Mangled name for kernel
                                        ; (note the spare byte after!)
 RootDir                equ $                   ; Location of root directory
@@ -166,7 +169,6 @@ KernelCNameLen  resw 1                      ; Length of unmangled kernel name
 InitRDCNameLen  resw 1                 ; Length of unmangled initrd name
 NextCharJump    resw 1                 ; Routine to interpret next print char
 SetupSecs      resw 1                  ; Number of setup sectors
-SavedSP                resw 1                  ; Our SP while running a COMBOOT image
 A20Test                resw 1                  ; Counter for testing status of A20
 CmdLineLen     resw 1                  ; Length of command line including null
 GraphXSize     resw 1                  ; Width of splash screen file
@@ -1369,667 +1371,16 @@ kernel_good:
                cmp ecx,'BSS'
                je near is_bss_sector
                ; Otherwise Linux kernel
-;
-; A Linux kernel consists of three parts: boot sector, setup code, and
-; kernel code. The boot sector is never executed when using an external
-; booting utility, but it contains some status bytes that are necessary.
-; The boot sector and setup code together form exactly 5 sectors that
-; should be loaded at 9000:0.  The subsequent code should be loaded
-; at 1000:0.  For simplicity, we load the whole thing at 0F60:0, and
-; copy the latter stuff afterwards.
-;
-; NOTE: In the previous code I have avoided making any assumptions regarding
-; the size of a sector, in case this concept ever gets extended to other
-; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT
-; filesystem, of course).  However, a "sector" when it comes to Linux booting
-; stuff means 512 bytes *no matter what*, so here I am using that piece
-; of knowledge.
-;
-; First check that our kernel is at least 1K and less than 8M (if it is
-; more than 8M, we need to change the logic for loading it anyway...)
-;
-; We used to require the kernel to be 64K or larger, but it has gotten
-; popular to use the Linux kernel format for other things, which may
-; not be so large.
-;
-is_linux_kernel:
-                cmp dx,80h                     ; 8 megs
-               ja kernel_corrupt
-               and dx,dx
-               jnz kernel_sane
-               cmp ax,1024                     ; Bootsect + 1 setup sect
-               jb kernel_corrupt
-kernel_sane:   push ax
-               push dx
-               push si
-               mov si,loading_msg
-                call cwritestr
-;
-; Now start transferring the kernel
-;
-               push word real_mode_seg
-               pop es
-
-               push ax
-               push dx
-               div word [ClustSize]            ; # of clusters total
-               and dx,dx                       ; Round up
-               setnz dl
-               movzx dx,dl
-               add ax,dx
-                mov [KernelClust],ax
-               pop dx
-               pop ax
-               mov [KernelSize],ax
-               mov [KernelSize+2],dx
-;
-; Now, if we transfer these straight, we'll hit 64K boundaries.         Hence we
-; have to see if we're loading more than 64K, and if so, load it step by
-; step.
-;
-               mov dx,1                        ; 10000h
-               xor ax,ax
-               div word [ClustSize]
-               mov [ClustPerMoby],ax           ; Clusters/64K
-;
-; Start by loading the bootsector/setup code, to see if we need to
-; do something funky.  It should fit in the first 32K (loading 64K won't
-; work since we might have funny stuff up near the end of memory).
-; If we have larger than 32K clusters, yes, we're hosed.
-;
-               call abort_check                ; Check for abort key
-               mov cx,[ClustPerMoby]
-               shr cx,1                        ; Half a moby
-               cmp cx,[KernelClust]
-               jna .normalkernel
-               mov cx,[KernelClust]
-.normalkernel:
-               sub [KernelClust],cx
-               xor bx,bx
-                pop si                          ; Cluster pointer on stack
-               call getfssec
-                cmp word [es:bs_bootsign],0AA55h
-               jne near kernel_corrupt         ; Boot sec signature missing
-;
-; Get the BIOS' idea of what the size of high memory is.
-;
-               push si                         ; Save our cluster pointer!
-;
-; First, try INT 15:E820 (get BIOS memory map)
-;
-get_e820:
-               push es
-               xor ebx,ebx                     ; Start with first record
-               mov es,bx                       ; Need ES = DS = 0 for now
-               jmp short .do_e820              ; Skip "at end" check first time!
-.int_loop:     and ebx,ebx                     ; If we're back at beginning...
-               jz no_e820                      ; ... bail; nothing found
-.do_e820:      mov eax,0000E820h
-               mov edx,534D4150h               ; "SMAP" backwards
-               mov ecx,20
-               mov di,E820Buf
-               int 15h
-               jc no_e820
-               cmp eax,534D4150h
-               jne no_e820
-;
-; Look for a memory block starting at <= 1 MB and continuing upward
-;
-               cmp dword [E820Buf+4], byte 0
-               ja .int_loop                    ; Start >= 4 GB?
-               mov edx, (1 << 20)
-               sub edx, [E820Buf]
-               jb .int_loop                    ; Start >= 1 MB?
-               mov eax, 0FFFFFFFFh
-               cmp dword [E820Buf+12], byte 0
-               ja .huge                        ; Size >= 4 GB
-               mov eax, [E820Buf+8]
-.huge:         sub eax, edx                    ; Adjust size to start at 1 MB
-               jbe .int_loop                   ; Completely below 1 MB?
-
-               ; Now EAX contains the size of memory 1 MB...up
-               cmp dword [E820Buf+16], byte 1
-               jne near err_nohighmem          ; High memory isn't usable memory!!!!
-
-               ; We're good!
-               pop es
-               jmp short got_highmem_add1mb    ; Still need to add low 1 MB
-
-;
-; INT 15:E820 failed.  Try INT 15:E801.
-;
-no_e820:       pop es
-
-               mov ax,0e801h                   ; Query high memory (semi-recent)
-               int 15h
-               jc no_e801
-               cmp ax,3c00h
-               ja no_e801                      ; > 3C00h something's wrong with this call
-               jb e801_hole                    ; If memory hole we can only use low part
-
-               mov ax,bx
-               shl eax,16                      ; 64K chunks
-               add eax,(16 << 20)              ; Add first 16M
-               jmp short got_highmem                           
-
-;
-; INT 15:E801 failed.  Try INT 15:88.
-;
-no_e801:
-               mov ah,88h                      ; Query high memory (oldest)
-               int 15h
-               cmp ax,14*1024                  ; Don't trust memory >15M
-               jna e801_hole
-               mov ax,14*1024
-e801_hole:
-               and eax,0ffffh
-               shl eax,10                      ; Convert from kilobytes
-got_highmem_add1mb:
-               add eax,(1 << 20)               ; First megabyte
-got_highmem:
-               mov [HighMemSize],eax
-
-;
-; Construct the command line (append options have already been copied)
-;
-               mov di,[CmdLinePtr]
-                mov si,boot_image              ; BOOT_IMAGE=
-                mov cx,boot_image_len
-                rep movsb
-                mov si,KernelCName             ; Unmangled kernel name
-                mov cx,[KernelCNameLen]
-                rep movsb
-                mov al,' '                      ; Space
-                stosb
-                mov si,[CmdOptPtr]              ; Options from user input
-               mov cx,(kern_cmd_len+3) >> 2
-               rep movsd
-;
-%ifdef debug
-                push ds                         ; DEBUG DEBUG DEBUG
-                push es
-                pop ds
-                mov si,cmd_line_here
-                call cwritestr
-                pop ds
-               call crlf
-%endif
-;
-; Scan through the command line for anything that looks like we might be
-; interested in.  The original version of this code automatically assumed
-; the first option was BOOT_IMAGE=, but that is no longer certain.
-;
-               mov si,cmd_line_here
-                mov byte [initrd_flag],0
-                push es                                ; Set DS <- real_mode_seg
-                pop ds
-get_next_opt:   lodsb
-               and al,al
-               jz near cmdline_end
-               cmp al,' '
-               jbe get_next_opt
-               dec si
-                mov eax,[si]
-                cmp eax,'vga='
-               je is_vga_cmd
-                cmp eax,'mem='
-               je is_mem_cmd
-                push es                         ; Save ES -> real_mode_seg
-                push cs
-                pop es                          ; Set ES <- normal DS
-                mov di,initrd_cmd
-               mov cx,initrd_cmd_len
-               repe cmpsb
-                jne not_initrd
-               mov di,InitRD
-                push si                         ; mangle_dir mangles si
-                call mangle_name                ; Mangle ramdisk name
-                pop si
-               cmp byte [es:InitRD],' '        ; Null filename?
-                seta byte [es:initrd_flag]     ; Set flag if not
-not_initrd:    pop es                          ; Restore ES -> real_mode_seg
-skip_this_opt:  lodsb                           ; Load from command line
-                cmp al,' '
-                ja skip_this_opt
-                dec si
-                jmp short get_next_opt
-is_vga_cmd:
-                add si,byte 4
-                mov eax,[si]
-                mov bx,-1
-                cmp eax, 'norm'                 ; vga=normal
-                je vc0
-                and eax,0ffffffh               ; 3 bytes
-                mov bx,-2
-                cmp eax, 'ext'                  ; vga=ext
-                je vc0
-                mov bx,-3
-                cmp eax, 'ask'                  ; vga=ask
-                je vc0
-                call parseint                   ; vga=<number>
-               jc skip_this_opt                ; Not an integer
-vc0:           mov [bs_vidmode],bx             ; Set video mode
-               jmp short skip_this_opt
-is_mem_cmd:
-                add si,byte 4
-                call parseint
-               jc skip_this_opt                ; Not an integer
-               mov [cs:HighMemSize],ebx
-               jmp short skip_this_opt
-cmdline_end:
-                push cs                         ; Restore standard DS
-                pop ds
-               sub si,cmd_line_here
-               mov [CmdLineLen],si             ; Length including final null
-;
-; Now check if we have a large kernel, which needs to be loaded high
-;
-               mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
-               cmp dword [es:su_header],HEADER_ID      ; New setup code ID
-               jne near old_kernel             ; Old kernel, load low
-               cmp word [es:su_version],0200h  ; Setup code version 2.0
-               jb near old_kernel              ; Old kernel, load low
-                cmp word [es:su_version],0201h ; Version 2.01+?
-                jb new_kernel                   ; If 2.00, skip this step
-                mov word [es:su_heapend],linux_stack   ; Set up the heap
-                or byte [es:su_loadflags],80h  ; Let the kernel know we care
-               cmp word [es:su_version],0203h  ; Version 2.03+?
-               jb new_kernel                   ; Not 2.03+
-               mov eax,[es:su_ramdisk_max]
-               mov [RamdiskMax],eax            ; Set the ramdisk limit
-
-;
-; We definitely have a new-style kernel.  Let the kernel know who we are,
-; and that we are clueful
-;
-new_kernel:
-               mov byte [es:su_loader],my_id   ; Show some ID
-               movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
-               mov [SetupSecs],ax
-;
-; About to load the kernel.  This is a modern kernel, so use the boot flags
-; we were provided.
-;
-                mov al,[es:su_loadflags]
-               mov [LoadFlags],al
-;
-; Load the kernel.  We always load it at 100000h even if we're supposed to
-; load it "low"; for a "low" load we copy it down to low memory right before
-; jumping to it.
-;
-read_kernel:
-                mov si,KernelCName             ; Print kernel name part of
-                call cwritestr                  ; "Loading" message
-                mov si,dotdot_msg              ; Print dots
-                call cwritestr
-
-                mov eax,[HighMemSize]
-               sub eax,100000h                 ; Load address
-               cmp eax,[KernelSize]
-               jb near no_high_mem             ; Not enough high memory
-;
-; Move the stuff beyond the setup code to high memory at 100000h
-;
-               movzx esi,word [SetupSecs]      ; Setup sectors
-               inc esi                         ; plus 1 boot sector
-                shl esi,9                      ; Convert to bytes
-                mov ecx,8000h                  ; 32K
-               sub ecx,esi                     ; Number of bytes to copy
-               push ecx
-               shr ecx,2                       ; Convert to dwords
-               add esi,(real_mode_seg << 4)    ; Pointer to source
-                mov edi,100000h                 ; Copy to address 100000h
-                call bcopy                     ; Transfer to high memory
-
-               ; On exit EDI -> where to load the rest
-
-                mov si,dot_msg                 ; Progress report
-                call cwritestr
-                call abort_check
-
-               pop ecx                         ; Number of bytes in the initial portion
-               pop si                          ; Restore file handle/cluster pointer
-               mov eax,[KernelSize]
-               sub eax,ecx                     ; Amount of kernel left over
-               jbe high_load_done              ; Zero left (tiny kernel)
-
-               call load_high                  ; Copy the file
-
-high_load_done:
-                mov ax,real_mode_seg           ; Set to real mode seg
-                mov es,ax
-
-                mov si,dot_msg
-                call cwritestr
-
-;
-; Now see if we have an initial RAMdisk; if so, do requisite computation
-; We know we have a new kernel; the old_kernel code already will have objected
-; if we tried to load initrd using an old kernel
-;
-load_initrd:
-                test byte [initrd_flag],1
-                jz near nk_noinitrd
-                push es                         ; ES->real_mode_seg
-                push ds
-                pop es                          ; We need ES==DS
-                mov si,InitRD
-                mov di,InitRDCName
-                call unmangle_name              ; Create human-readable name
-                sub di,InitRDCName
-                mov [InitRDCNameLen],di
-                mov di,InitRD
-                call searchdir                  ; Look for it in directory
-                pop es
-               jz initrd_notthere
-               mov [es:su_ramdisklen1],ax      ; Ram disk length
-               mov [es:su_ramdisklen2],dx
-               mov edx,[HighMemSize]           ; End of memory
-               dec edx
-               mov eax,[RamdiskMax]            ; Highest address allowed by kernel
-               cmp edx,eax
-               jna memsize_ok
-               mov edx,eax                     ; Adjust to fit inside limit
-memsize_ok:
-               inc edx
-                xor dx,dx                      ; Round down to 64K boundary
-               sub edx,[es:su_ramdisklen]      ; Subtract size of ramdisk
-                xor dx,dx                      ; Round down to 64K boundary
-                mov [es:su_ramdiskat],edx      ; Load address
-               call loadinitrd                 ; Load initial ramdisk
-               jmp short initrd_end
-
-initrd_notthere:
-                mov si,err_noinitrd
-                call cwritestr
-                mov si,InitRDCName
-                call cwritestr
-                mov si,crlf_msg
-                jmp abort_load
-
-no_high_mem:    mov si,err_nohighmem           ; Error routine
-                jmp abort_load
-
-initrd_end:
-nk_noinitrd:
-;
-; Abandon hope, ye that enter here!  We do no longer permit aborts.
-;
-                call abort_check               ; Last chance!!
-
-               mov si,ready_msg
-               call cwritestr
-
-               call vgaclearmode               ; We can't trust ourselves after this
-;
-; Now, if we were supposed to load "low", copy the kernel down to 10000h
-; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
-; capable of starting their setup from a different address.
-;
-               mov ax,real_mode_seg
-               mov fs,ax
-
-;
-; Copy command line.  Unfortunately, the kernel boot protocol requires
-; the command line to exist in the 9xxxxh range even if the rest of the
-; setup doesn't.
-;
-               cli                             ; In case of hooked interrupts
-               test byte [LoadFlags],LOAD_HIGH
-               jz need_high_cmdline
-               cmp word [fs:su_version],0202h  ; Support new cmdline protocol?
-               jb need_high_cmdline
-               ; New cmdline protocol
-               ; Store 32-bit (flat) pointer to command line
-               mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
-               jmp short in_proper_place
 
-need_high_cmdline:
 ;
-; Copy command line up to 90000h
+; Linux kernel loading code is common.
 ;
-               mov ax,9000h
-               mov es,ax
-               mov si,cmd_line_here
-               mov di,si
-               mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
-               mov [fs:kern_cmd_offset],di     ; Store pointer
-
-               mov cx,[CmdLineLen]
-               add cx,byte 3
-               shr cx,2                        ; Convert to dwords
-               fs rep movsd
-
-               push fs
-               pop es
-
-               test byte [LoadFlags],LOAD_HIGH
-               jnz in_proper_place             ; If high load, we're done
+%include "runkernel.inc"
 
 ;
-; Loading low; we can't assume it's safe to run in place.
-;
-; Copy real_mode stuff up to 90000h
-;
-               mov ax,9000h
-               mov es,ax
-               mov cx,[SetupSecs]
-               inc cx                          ; Setup + boot sector
-               shl cx,7                        ; Sectors -> dwords
-               xor si,si
-               xor di,di
-               fs rep movsd                    ; Copy setup + boot sector
-;
-; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
-; setup sectors, but the boot protocol had not yet been defined.  They
-; rely on a signature to figure out if they need to copy stuff from
-; the "protected mode" kernel area.  Unfortunately, we used that area
-; as a transfer buffer, so it's going to find the signature there.
-; Hence, zero the low 32K beyond the setup area.
-;
-               mov di,[SetupSecs]
-               inc di                          ; Setup + boot sector
-               mov cx,32768/512                ; Sectors/32K
-               sub cx,di                       ; Remaining sectors
-               shl di,9                        ; Sectors -> bytes
-               shl cx,7                        ; Sectors -> dwords
-               xor eax,eax
-               rep stosd                       ; Clear region
+; COMBOOT-loading code
 ;
-; Copy the kernel down to the "low" location
-;
-               mov ecx,[KernelSize]
-               add ecx,3                       ; Round upwards
-               shr ecx,2                       ; Bytes -> dwords
-               mov esi,100000h
-               mov edi,10000h
-               call bcopy
-
-;
-; Now everything is where it needs to be...
-;
-; When we get here, es points to the final segment, either
-; 9000h or real_mode_seg
-;
-in_proper_place:
-
-;
-; If the default root device is set to FLOPPY (0000h), change to
-; /dev/fd0 (0200h)
-;
-               cmp word [es:bs_rootdev],byte 0
-               jne root_not_floppy
-               mov word [es:bs_rootdev],0200h
-root_not_floppy:
-;
-; Copy the disk table to high memory, then re-initialize the floppy
-; controller
-;
-; This needs to be moved before the copy
-;
-%if 0
-               push ds
-               push bx
-               lds si,[fdctab]
-               mov di,linux_fdctab
-               mov cx,3                        ; 12 bytes
-               push di
-               rep movsd
-               pop di
-               mov [fdctab1],di                ; Save new floppy tab pos
-               mov [fdctab2],es
-               xor ax,ax
-               xor dx,dx
-               int 13h
-               pop bx
-               pop ds
-%endif
-;
-; Linux wants the floppy motor shut off before starting the kernel,
-; at least bootsect.S seems to imply so
-;
-kill_motor:
-               mov dx,03F2h
-               xor al,al
-               call slow_out
-;
-; If we're debugging, wait for a keypress so we can read any debug messages
-;
-%ifdef debug
-                xor ax,ax
-                int 16h
-%endif
-;
-; Set up segment registers and the Linux real-mode stack
-; Note: es == the real mode segment
-;
-               cli
-               mov bx,es
-               mov ds,bx
-               mov fs,bx
-               mov gs,bx
-               mov ss,bx
-               mov sp,linux_stack
-;
-; We're done... now RUN THAT KERNEL!!!!
-; Setup segment == real mode segment + 020h; we need to jump to offset
-; zero in the real mode segment.
-;
-               add bx,020h
-               push bx
-               push word 0h
-               retf
-
-;
-; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
-; initrd, and are always loaded low.
-;
-old_kernel:
-                test byte [initrd_flag],1      ; Old kernel can't have initrd
-                jz load_old_kernel
-                mov si,err_oldkernel
-                jmp abort_load
-load_old_kernel:
-               mov word [SetupSecs],4          ; Always 4 setup sectors
-               mov byte [LoadFlags],0          ; Always low
-               jmp read_kernel
-
-;
-; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
-; except that it may, of course, not contain any DOS system calls.  We
-; do, however, allow the execution of INT 20h to return to SYSLINUX.
-;
-is_comboot_image:
-               and dx,dx
-               jnz comboot_too_large
-               cmp ax,0ff00h           ; Max size in bytes
-               jae comboot_too_large
-
-               ;
-               ; Set up the DOS vectors in the IVT (INT 20h-3fh)
-               ;
-               mov dword [4*0x20],comboot_return       ; INT 20h vector
-               mov eax,comboot_bogus
-               mov di,4*0x21
-               mov cx,31               ; All remaining DOS vectors
-               rep stosd
-       
-               mov cx,comboot_seg
-               mov es,cx
-
-               mov bx,100h             ; Load at <seg>:0100h
-
-               mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
-               call getfssec
-
-               xor di,di
-               mov cx,64               ; 256 bytes (size of PSP)
-               xor eax,eax             ; Clear PSP
-               rep stosd
-
-               mov word [es:0], 020CDh ; INT 20h instruction
-               ; First non-free paragraph
-               mov word [es:02h], comboot_seg+1000h
-
-               ; Copy the command line from high memory
-               mov cx,125              ; Max cmdline len (minus space and CR)
-               mov si,[CmdOptPtr]
-               mov di,081h             ; Offset in PSP for command line
-               mov al,' '              ; DOS command lines begin with a space
-               stosb
-
-comboot_cmd_cp:        lodsb
-               and al,al
-               jz comboot_end_cmd
-               stosb
-               loop comboot_cmd_cp
-comboot_end_cmd: mov al,0Dh            ; CR after last character
-               stosb
-               mov al,126              ; Include space but not CR
-               sub al,cl
-               mov [es:80h], al        ; Store command line length
-
-               call vgaclearmode       ; Reset video
-
-               mov ax,es
-               mov ds,ax
-               mov ss,ax
-               xor sp,sp
-               push word 0             ; Return to address 0 -> exit
-
-               jmp comboot_seg:100h    ; Run it
-
-; Looks like a COMBOOT image but too large
-comboot_too_large:
-               mov si,err_comlarge
-               call cwritestr
-cb_enter:      jmp enter_command
-
-; Proper return vector
-comboot_return:        cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ss,ax
-               mov sp,[ss:SavedSP]
-               mov ds,ax
-               mov es,ax
-               sti
-               cld
-               jmp short cb_enter
-
-; Attempted to execute DOS system call
-comboot_bogus: cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ss,ax
-               mov sp,[ss:SavedSP]
-               mov ds,ax
-               mov es,ax
-               sti
-               cld
-               mov si,KernelCName
-               call cwritestr
-               mov si,err_notdos
-               call cwritestr
-               jmp short cb_enter
+%include "comboot.inc"
 
 ;
 ; Load a boot sector
@@ -2093,421 +1444,6 @@ bad_bootsec:
                jmp enter_command
 
 ;
-; 32-bit bcopy routine for real mode
-;
-; We enter protected mode, set up a flat 32-bit environment, run rep movsd
-; and then exit.  IMPORTANT: This code assumes cs == ss == 0.
-;
-; This code is probably excessively anal-retentive in its handling of
-; segments, but this stuff is painful enough as it is without having to rely
-; on everything happening "as it ought to."
-;
-               align 4
-bcopy_gdt:     dw bcopy_gdt_size-1     ; Null descriptor - contains GDT
-               dd bcopy_gdt            ; pointer for LGDT instruction
-               dw 0
-               dd 0000ffffh            ; Code segment, use16, readable,
-               dd 00009b00h            ; present, dpl 0, cover 64K
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 008f9300h            ; present, dpl 0, cover all 4G
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 00009300h            ; present, dpl 0, cover 64K
-bcopy_gdt_size:        equ $-bcopy_gdt
-
-bcopy:         push eax
-               pushf                   ; Saves, among others, the IF flag
-               push gs
-               push fs
-               push ds
-               push es
-
-               cli
-               call enable_a20
-
-               o32 lgdt [cs:bcopy_gdt]
-               mov eax,cr0
-               or al,1
-               mov cr0,eax             ; Enter protected mode
-               jmp 08h:.in_pm
-
-.in_pm:                mov ax,10h              ; Data segment selector
-               mov es,ax
-               mov ds,ax
-
-               mov al,18h              ; "Real-mode-like" data segment
-               mov ss,ax
-               mov fs,ax
-               mov gs,ax       
-       
-               a32 rep movsd           ; Do our business
-               
-               mov es,ax               ; Set to "real-mode-like"
-               mov ds,ax
-       
-               mov eax,cr0
-               and al,~1
-               mov cr0,eax             ; Disable protected mode
-               jmp 0:.in_rm
-
-.in_rm:                xor ax,ax               ; Back in real mode
-               mov ss,ax
-               pop es
-               pop ds
-               pop fs
-               pop gs
-               call disable_a20
-
-               popf                    ; Re-enables interrupts
-               pop eax
-               ret
-
-;
-; Routines to enable and disable (yuck) A20.  These routines are gathered
-; from tips from a couple of sources, including the Linux kernel and
-; http://www.x86.org/.  The need for the delay to be as large as given here
-; is indicated by Donnie Barnes of RedHat, the problematic system being an
-; IBM ThinkPad 760EL.
-;
-; We typically toggle A20 twice for every 64K transferred.
-; 
-%define        io_delay        call _io_delay
-%define IO_DELAY_PORT  80h             ; Invalid port (we hope!)
-%define disable_wait   32              ; How long to wait for a disable
-
-%define A20_DUNNO      0               ; A20 type unknown
-%define A20_NONE       1               ; A20 always on?
-%define A20_BIOS       2               ; A20 BIOS enable
-%define A20_KBC                3               ; A20 through KBC
-%define A20_FAST       4               ; A20 through port 92h
-
-slow_out:      out dx, al              ; Fall through
-
-_io_delay:     out IO_DELAY_PORT,al
-               out IO_DELAY_PORT,al
-               ret
-
-enable_a20:
-               pushad
-               mov byte [cs:A20Tries],255 ; Times to try to make this work
-
-try_enable_a20:
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-;
-; If the A20 type is known, jump straight to type
-;
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20List]
-
-;
-; First, see if we are on a system with no A20 gate
-;
-a20_dunno:
-a20_none:
-               mov byte [cs:A20Type], A20_NONE
-               call a20_test
-               jnz a20_done
-
-;
-; Next, try the BIOS (INT 15h AX=2401h)
-;
-a20_bios:
-               mov byte [cs:A20Type], A20_BIOS
-               mov ax,2401h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-
-               call a20_test
-               jnz a20_done
-
-;
-; Enable the keyboard controller A20 gate
-;
-a20_kbc:
-               mov dl, 1                       ; Allow early exit
-               call empty_8042
-               jnz a20_done                    ; A20 live, no need to use KBC
-
-               mov byte [cs:A20Type], A20_KBC  ; Starting KBC command sequence
-
-               mov al,0D1h                     ; Command write
-               out 064h, al
-               call empty_8042_uncond
-
-               mov al,0DFh                     ; A20 on
-               out 060h, al
-               call empty_8042_uncond
-
-               ; Verify that A20 actually is enabled.  Do that by
-               ; observing a word in low memory and the same word in
-               ; the HMA until they are no longer coherent.  Note that
-               ; we don't do the same check in the disable case, because
-               ; we don't want to *require* A20 masking (SYSLINUX should
-               ; work fine without it, if the BIOS does.)
-.kbc_wait:     push cx
-               xor cx,cx
-.kbc_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .kbc_wait_loop
-
-               pop cx
-;
-; Running out of options here.  Final attempt: enable the "fast A20 gate"
-;
-a20_fast:
-               mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
-               in al, 092h
-               or al,02h
-               and al,~01h                     ; Don't accidentally reset the machine!
-               out 092h, al
-
-.fast_wait:    push cx
-               xor cx,cx
-.fast_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .fast_wait_loop
-
-               pop cx
-
-;
-; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
-; and report failure to the user.
-;
-
-
-               dec byte [cs:A20Tries]
-               jnz try_enable_a20
-
-               mov si, err_a20
-               jmp abort_load
-;
-; A20 unmasked, proceed...
-;
-a20_done_pop:  pop cx
-a20_done:      popad
-               ret
-
-;
-; This routine tests if A20 is enabled (ZF = 0).  This routine
-; must not destroy any register contents.
-;
-a20_test:
-               push es
-               push cx
-               push ax
-               mov cx,0FFFFh           ; HMA = segment 0FFFFh
-               mov es,cx
-               mov cx,32               ; Loop count
-               mov ax,[cs:A20Test]
-.a20_wait:     inc ax
-               mov [cs:A20Test],ax
-               io_delay                ; Serialize, and fix delay
-               cmp ax,[es:A20Test+10h]
-               loopz .a20_wait
-.a20_done:     pop ax
-               pop cx
-               pop es
-               ret
-
-disable_a20:
-               pushad
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20DList]
-
-a20d_bios:
-               mov ax,2400h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-               jmp short a20d_snooze
-
-;
-; Disable the "fast A20 gate"
-;
-a20d_fast:
-               in al, 092h
-               and al,~03h
-               out 092h, al
-               jmp short a20d_snooze
-
-;
-; Disable the keyboard controller A20 gate
-;
-a20d_kbc:
-               call empty_8042_uncond
-               mov al,0D1h
-               out 064h, al            ; Command write
-               call empty_8042_uncond
-               mov al,0DDh             ; A20 off
-               out 060h, al
-               call empty_8042_uncond
-               ; Wait a bit for it to take effect
-a20d_snooze:
-               push cx
-               mov cx, disable_wait
-.delayloop:    call a20_test
-               jz .disabled
-               loop .delayloop
-.disabled:     pop cx
-a20d_dunno:
-a20d_none:
-               popad
-               ret
-
-;
-; Routine to empty the 8042 KBC controller.  If dl != 0
-; then we will test A20 in the loop and exit if A20 is
-; suddenly enabled.
-;
-empty_8042_uncond:
-               xor dl,dl
-empty_8042:
-               call a20_test
-               jz .a20_on
-               and dl,dl
-               jnz .done
-.a20_on:       io_delay
-               in al, 064h             ; Status port
-               test al,1
-               jz .no_output
-               io_delay
-               in al, 060h             ; Read input
-               jmp short empty_8042
-.no_output:
-               test al,2
-               jnz empty_8042
-               io_delay
-.done:         ret     
-
-;
-; WBINVD instruction; gets auto-eliminated on 386 CPUs
-;
-try_wbinvd:
-               wbinvd
-               ret
-
-;
-; Load RAM disk into high memory
-;
-; Need to be set:
-;      su_ramdiskat    - Where in memory to load
-;      su_ramdisklen   - Size of file
-;      SI              - initrd filehandle/cluster pointer
-;
-loadinitrd:
-                push es                         ; Save ES on entry
-               mov ax,real_mode_seg
-                mov es,ax
-                mov edi,[es:su_ramdiskat]      ; initrd load address
-               push si
-               mov si,crlfloading_msg          ; Write "Loading "
-               call cwritestr
-                mov si,InitRDCName             ; Write ramdisk name
-                call cwritestr
-                mov si,dotdot_msg              ; Write dots
-                call cwritestr
-               pop si
-
-               mov eax,[es:su_ramdisklen]
-               call load_high                  ; Load the file
-
-               call crlf
-                pop es                          ; Restore original ES
-                ret
-
-;
-; load_high:   loads (the remainder of) a file into high memory.
-;              This routine prints dots for each 64K transferred, and
-;              calls abort_check periodically.
-; 
-;              The xfer_buf_seg is used as a bounce buffer.
-;
-;              The input address (EDI) should be dword aligned, and the final
-;              dword written is padded with zeroes if necessary.
-;
-; Inputs:      SI  = file handle/cluster pointer
-;              EDI = target address in high memory
-;              EAX = size of remaining file in bytes
-;
-; Outputs:     SI  = file handle/cluster pointer
-;              EDI = first untouched address (not including padding)
-;
-load_high:
-               push es
-
-               mov bx,xfer_buf_seg
-               mov es,bx
-
-.read_loop:
-               and si,si                       ; If SI == 0 then we have end of file
-               jz .eof
-               push si
-               mov si,dot_msg
-               call cwritestr
-               pop si
-               call abort_check
-
-               push eax                        ; <A> Total bytes to transfer
-               cmp eax,(1 << 16)               ; Max 64K in one transfer
-               jna .size_ok
-               mov eax,(1 << 16)
-.size_ok:
-               xor edx,edx
-               push eax                        ; <B> Bytes transferred this chunk
-               movzx ecx,word [ClustSize]
-               div ecx                         ; Convert to clusters
-               ; Round up...
-               add edx,byte -1                 ; Sets CF if EDX >= 1
-               adc eax,byte 0                  ; Add 1 to EAX if CF set
-
-               ; Now (e)ax contains the number of clusters to get
-               push edi                        ; <C> Target buffer
-               mov cx,ax
-               xor bx,bx                       ; ES:0
-               call getfssec                   ; Load the data into xfer_buf_seg
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               push ecx                        ; <B> Byte count this round 
-               push edi                        ; <C> Target buffer
-.fix_slop:
-               test cl,3
-               jz .noslop
-               ; The last dword fractional - pad with zeroes
-               ; Zero-padding is critical for multi-file initramfs.
-               mov byte [es:ecx],0
-               inc ecx
-               jmp short .fix_slop
-.noslop:
-               shr ecx,2                       ; Convert to dwords
-               push esi                        ; <D> File handle/cluster pointer
-               mov esi,(xfer_buf_seg << 4)     ; Source address
-               call bcopy                      ; Copy to high memory
-               pop esi                         ; <D> File handle/cluster pointer
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               pop eax                         ; <A> Total bytes to transfer
-               add edi,ecx
-               sub eax,ecx
-               jnz .read_loop                  ; More to read...
-               
-.eof:
-               pop es
-               ret
-
-;
 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
 ;
 abort_check:
@@ -2783,6 +1719,8 @@ lc_ret:         ret
 %include "writestr.inc"                ; String output
 %include "parseconfig.inc"     ; High-level config file handling
 %include "parsecmd.inc"                ; Low-level config file handling
+%include "bcopy32.inc"         ; 32-bit bcopy
+%include "loadhigh.inc"                ; Load a file into high memory
 %include "font.inc"            ; VGA font stuff
 %include "graphics.inc"                ; VGA graphics
 
@@ -2919,9 +1857,6 @@ VKernelCtr        dw 0                    ; Number of registered vkernels
 ForcePrompt    dw 0                    ; Force prompt
 AllowImplicit   dw 1                    ; Allow implicit kernels
 SerialPort     dw 0                    ; Serial port base (or 0 for no serial port)
-A20List                dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
-A20DList       dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
-A20Type                dw A20_DUNNO            ; A20 type unknown
 VGAFontSize    dw 16                   ; Defaults to 16 byte font
 UserFont       db 0                    ; Using a user-specified font
 ScrollAttribute        db 07h                  ; White on black (for text mode)
diff --git a/loadhigh.inc b/loadhigh.inc
new file mode 100644 (file)
index 0000000..c952ae8
--- /dev/null
@@ -0,0 +1,98 @@
+;; $Id$
+;; -----------------------------------------------------------------------
+;;   
+;;   Copyright 1994-2002 H. Peter Anvin - All Rights Reserved
+;;
+;;   This program is free software; you can redistribute it and/or modify
+;;   it under the terms of the GNU General Public License as published by
+;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;;   Bostom MA 02111-1307, USA; either version 2 of the License, or
+;;   (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; loadhigh.inc
+;; 
+;; Load a file into high memory
+;;
+
+;
+; load_high:   loads (the remainder of) a file into high memory.
+;              This routine prints dots for each 64K transferred, and
+;              calls abort_check periodically.
+; 
+;              The xfer_buf_seg is used as a bounce buffer.
+;
+;              The input address (EDI) should be dword aligned, and the final
+;              dword written is padded with zeroes if necessary.
+;
+; Inputs:      SI  = file handle/cluster pointer
+;              EDI = target address in high memory
+;              EAX = size of remaining file in bytes
+;
+; Outputs:     SI  = file handle/cluster pointer
+;              EDI = first untouched address (not including padding)
+;
+load_high:
+               push es
+
+               mov bx,xfer_buf_seg
+               mov es,bx
+
+.read_loop:
+               and si,si                       ; If SI == 0 then we have end of file
+               jz .eof
+               push si
+               mov si,dot_msg
+               call cwritestr
+               pop si
+               call abort_check
+
+               push eax                        ; <A> Total bytes to transfer
+               cmp eax,(1 << 16)               ; Max 64K in one transfer
+               jna .size_ok
+               mov eax,(1 << 16)
+.size_ok:
+               xor edx,edx
+               push eax                        ; <B> Bytes transferred this chunk
+               movzx ecx,word [ClustSize]
+               div ecx                         ; Convert to clusters
+               ; Round up...
+               add edx,byte -1                 ; Sets CF if EDX >= 1
+               adc eax,byte 0                  ; Add 1 to EAX if CF set
+
+               ; Now (e)ax contains the number of clusters to get
+               push edi                        ; <C> Target buffer
+               mov cx,ax
+               xor bx,bx                       ; ES:0
+               call getfssec                   ; Load the data into xfer_buf_seg
+               pop edi                         ; <C> Target buffer
+               pop ecx                         ; <B> Byte count this round
+               push ecx                        ; <B> Byte count this round 
+               push edi                        ; <C> Target buffer
+.fix_slop:
+               test cl,3
+               jz .noslop
+               ; The last dword fractional - pad with zeroes
+               ; Zero-padding is critical for multi-file initramfs.
+               mov byte [es:ecx],0
+               inc ecx
+               jmp short .fix_slop
+.noslop:
+               shr ecx,2                       ; Convert to dwords
+               push esi                        ; <D> File handle/cluster pointer
+               mov esi,(xfer_buf_seg << 4)     ; Source address
+               call bcopy                      ; Copy to high memory
+               pop esi                         ; <D> File handle/cluster pointer
+               pop edi                         ; <C> Target buffer
+               pop ecx                         ; <B> Byte count this round
+               pop eax                         ; <A> Total bytes to transfer
+               add edi,ecx
+               sub eax,ecx
+               jnz .read_loop                  ; More to read...
+               
+.eof:
+               pop es
+               ret
+
index 3627865..8c83fac 100644 (file)
@@ -33,9 +33,10 @@ my_id                equ pxelinux_id
 max_cmd_len    equ 255                 ; Must be odd; 255 is the kernel limit
 FILENAME_MAX_LG2 equ 6                 ; log2(Max filename size Including final null)
 FILENAME_MAX   equ (1 << FILENAME_MAX_LG2)
+NULLFILE       equ 0                   ; Zero byte == null file name
 REBOOT_TIME    equ 5*60                ; If failure, time until full reset
 HIGHMEM_MAX    equ 037FFFFFFh          ; DEFAULT highest address for an initrd
-HIGHMEM_SLOP   equ 128*1024            ; Avoid this much memory near the top
+%assign HIGHMEM_SLOP 128*1024          ; Avoid this much memory near the top
 DEFAULT_BAUD   equ 9600                ; Default baud rate for serial port
 BAUD_DIVISOR   equ 115200              ; Serial port parameter
 MAX_SOCKETS_LG2        equ 6                   ; log2(Max number of open sockets)
@@ -195,9 +196,9 @@ HiLoadAddr      resd 1                      ; Address pointer for high load loop
 HighMemSize    resd 1                  ; End of memory pointer (bytes)
 RamdiskMax     resd 1                  ; Highest address for a ramdisk
 KernelSize     resd 1                  ; Size of kernel (bytes)
+SavedSSSP      resd 1                  ; Our SS:SP while running a COMBOOT image
 Stack          resd 1                  ; Pointer to reset stack
 PXEEntry       resd 1                  ; !PXE API entry point
-SavedSSSP      resd 1                  ; Our SS:SP while running a COMBOOT image
 RebootTime     resd 1                  ; Reboot timeout, if set by option
 KernelClust    resd 1                  ; Kernel size in clusters
 FBytes         equ $                   ; Used by open/getc
@@ -1150,165 +1151,13 @@ kernel_good:
                je near is_bootsector
                ; Otherwise Linux kernel
 ;
-; A Linux kernel consists of three parts: boot sector, setup code, and
-; kernel code. The boot sector is never executed when using an external
-; booting utility, but it contains some status bytes that are necessary.
-;
-; First check that our kernel is at least 1K and less than 8M (if it is
-; more than 8M, we need to change the logic for loading it anyway...)
-;
-; We used to require the kernel to be 64K or larger, but it has gotten
-; popular to use the Linux kernel format for other things, which may
-; not be so large.
-;
-is_linux_kernel:
-                cmp dx,80h                     ; 8 megs
-               ja kernel_corrupt
-               and dx,dx
-               jnz kernel_sane
-               cmp ax,1024                     ; Bootsect + 1 setup sect
-               jb kernel_corrupt
-kernel_sane:   push ax
-               push dx
-               push si
-               mov si,loading_msg
-                call cwritestr
-;
-; Now start transferring the kernel
-;
-               push word real_mode_seg
-               pop es
-
-               movzx eax,ax                    ; Fix this by using a 32-bit
-               shl edx,16                      ; register for the kernel size
-               or eax,edx
-               mov [KernelSize],eax
-               xor edx,edx
-               div dword [ClustSize]           ; # of clusters total
-               ; Round up...
-               add edx,byte -1                 ; Sets CF if EDX >= 1
-               adc eax,byte 0                  ; Add 1 to EAX if CF set
-                mov [KernelClust],eax
-
-;
-; Now, if we transfer these straight, we'll hit 64K boundaries.         Hence we
-; have to see if we're loading more than 64K, and if so, load it step by
-; step.
+; Linux kernel loading code is common.  However, we need to define
+; a couple of helper macros...
 ;
 
-;
-; Start by loading the bootsector/setup code, to see if we need to
-; do something funky.  It should fit in the first 32K (loading 64K won't
-; work since we might have funny stuff up near the end of memory).
-; If we have larger than 32K clusters, yes, we're hosed.
-;
-               call abort_check                ; Check for abort key
-               mov ecx,[ClustPerMoby]
-               shr ecx,1                       ; Half a moby
-               cmp ecx,[KernelClust]
-               jna .normalkernel
-               mov ecx,[KernelClust]
-.normalkernel:
-               sub [KernelClust],ecx
-               xor bx,bx
-                pop si                          ; Cluster pointer on stack
-               call getfssec
-                cmp word [es:bs_bootsign],0AA55h
-               jne near kernel_corrupt         ; Boot sec signature missing
-;
-; Get the BIOS' idea of what the size of high memory is.
-;
-               push si                         ; Save our cluster pointer!
-;
-; First, try INT 15:E820 (get BIOS memory map)
-;
-get_e820:
-               push es
-               xor ebx,ebx                     ; Start with first record
-               mov es,bx                       ; Need ES = DS = 0 for now
-               jmp short .do_e820              ; Skip "at end" check first time!
-.int_loop:     and ebx,ebx                     ; If we're back at beginning...
-               jz no_e820                      ; ... bail; nothing found
-.do_e820:      mov eax,0000E820h
-               mov edx,534D4150h               ; "SMAP" backwards
-               mov ecx,20
-               mov di,E820Buf
-               int 15h
-               jc no_e820
-               cmp eax,534D4150h
-               jne no_e820
-;
-; Look for a memory block starting at <= 1 MB and continuing upward
-;
-               cmp dword [E820Buf+4], byte 0
-               ja .int_loop                    ; Start >= 4 GB?
-               mov edx, (1 << 20)
-               sub edx, [E820Buf]
-               jb .int_loop                    ; Start >= 1 MB?
-               mov eax, 0FFFFFFFFh
-               cmp dword [E820Buf+12], byte 0
-               ja .huge                        ; Size >= 4 GB
-               mov eax, [E820Buf+8]
-.huge:         sub eax, edx                    ; Adjust size to start at 1 MB
-               jbe .int_loop                   ; Completely below 1 MB?
-
-               ; Now EAX contains the size of memory 1 MB...up
-               cmp dword [E820Buf+16], byte 1
-               jne near err_nohighmem          ; High memory isn't usable memory!!!!
-
-               ; We're good!
-               pop es
-               jmp short got_highmem_add1mb    ; Still need to add low 1 MB
-
-;
-; INT 15:E820 failed.  Try INT 15:E801.
-;
-no_e820:       pop es
-
-               mov ax,0e801h                   ; Query high memory (semi-recent)
-               int 15h
-               jc no_e801
-               cmp ax,3c00h
-               ja no_e801                      ; > 3C00h something's wrong with this call
-               jb e801_hole                    ; If memory hole we can only use low part
-
-               mov ax,bx
-               shl eax,16                      ; 64K chunks
-               add eax,(16 << 20)              ; Add first 16M
-               jmp short got_highmem                           
-
-;
-; INT 15:E801 failed.  Try INT 15:88.
-;
-no_e801:
-               mov ah,88h                      ; Query high memory (oldest)
-               int 15h
-               cmp ax,14*1024                  ; Don't trust memory >15M
-               jna e801_hole
-               mov ax,14*1024
-e801_hole:
-               and eax,0ffffh
-               shl eax,10                      ; Convert from kilobytes
-got_highmem_add1mb:
-               add eax,(1 << 20)               ; First megabyte
-got_highmem:
-               sub eax,HIGHMEM_SLOP
-               mov [HighMemSize],eax
-
-;
-; Construct the command line (append options have already been copied)
-;
-construct_cmdline:
-               mov di,[CmdLinePtr]
-                mov si,boot_image              ; BOOT_IMAGE=
-                mov cx,boot_image_len
-                rep movsb
-                mov si,KernelCName             ; Unmangled kernel name
-                mov cx,[KernelCNameLen]
-                rep movsb
-                mov al,' '                      ; Space
-                stosb
-
+; Handle "ipappend" option
+%define HAVE_SPECIAL_APPEND
+%macro SPECIAL_APPEND 0
                mov al,[IPAppend]               ; ip=
                and al,al
                jz .noipappend
@@ -1318,497 +1167,24 @@ construct_cmdline:
                mov al,' '
                stosb
 .noipappend:
-                mov si,[CmdOptPtr]              ; Options from user input
-               mov cx,(kern_cmd_len+3) >> 2
-               rep movsd
-;
-; Scan through the command line for anything that looks like we might be
-; interested in.  The original version of this code automatically assumed
-; the first option was BOOT_IMAGE=, but that is no longer certain.
-;
-               mov si,cmd_line_here
-                mov byte [initrd_flag],0
-                push es                                ; Set DS <- real_mode_seg
-                pop ds
-get_next_opt:   lodsb
-               and al,al
-               jz near cmdline_end
-               cmp al,' '
-               jbe get_next_opt
-               dec si
-                mov eax,[si]
-                cmp eax,'vga='
-               je is_vga_cmd
-                cmp eax,'mem='
-               je is_mem_cmd
-                push es                         ; Save ES -> real_mode_seg
-                push cs
-                pop es                          ; Set ES <- normal DS
-                mov di,initrd_cmd
-               mov cx,initrd_cmd_len
-               repe cmpsb
-                jne not_initrd
-               mov di,InitRD
-                push si                         ; mangle_dir mangles si
-                call mangle_name                ; Mangle ramdisk name
-                pop si
-               cmp byte [es:InitRD],0          ; Null filename?
-                seta byte [es:initrd_flag]     ; Set flag if not
-not_initrd:    pop es                          ; Restore ES -> real_mode_seg
-skip_this_opt:  lodsb                           ; Load from command line
-                cmp al,' '
-                ja skip_this_opt
-                dec si
-                jmp short get_next_opt
-is_vga_cmd:
-                add si,byte 4
-                mov eax,[si]
-                mov bx,-1
-                cmp eax, 'norm'                 ; vga=normal
-                je vc0
-                and eax,0ffffffh               ; 3 bytes
-                mov bx,-2
-                cmp eax, 'ext'                  ; vga=ext
-                je vc0
-                mov bx,-3
-                cmp eax, 'ask'                  ; vga=ask
-                je vc0
-                call parseint                   ; vga=<number>
-               jc skip_this_opt                ; Not an integer
-vc0:           mov [bs_vidmode],bx             ; Set video mode
-               jmp short skip_this_opt
-is_mem_cmd:
-                add si,byte 4
-                call parseint
-               jc skip_this_opt                ; Not an integer
-               sub ebx,HIGHMEM_SLOP
-               mov [cs:HighMemSize],ebx
-               jmp short skip_this_opt
-cmdline_end:
-                push cs                         ; Restore standard DS
-                pop ds
-               sub si,cmd_line_here
-               mov [CmdLineLen],si             ; Length including final null
-;
-; Now check if we have a large kernel, which needs to be loaded high
-;
-               mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
-               cmp dword [es:su_header],HEADER_ID      ; New setup code ID
-               jne near old_kernel             ; Old kernel, load low
-               cmp word [es:su_version],0200h  ; Setup code version 2.0
-               jb near old_kernel              ; Old kernel, load low
-                cmp word [es:su_version],0201h ; Version 2.01+?
-                jb new_kernel                   ; If 2.00, skip this step
-                mov word [es:su_heapend],linux_stack   ; Set up the heap
-                or byte [es:su_loadflags],80h  ; Let the kernel know we care
-               cmp word [es:su_version],0203h  ; Version 2.03+?
-               jb new_kernel                   ; Not 2.03+
-               mov eax,[es:su_ramdisk_max]
-               mov [RamdiskMax],eax            ; Set the ramdisk limit
-
-;
-; We definitely have a new-style kernel.  Let the kernel know who we are,
-; and that we are clueful
-;
-new_kernel:
-               mov byte [es:su_loader],my_id   ; Show some ID
-               movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
-               mov [SetupSecs],ax
-;
-; About to load the kernel.  This is a modern kernel, so use the boot flags
-; we were provided.
-;
-                mov al,[es:su_loadflags]
-               mov [LoadFlags],al
-;
-; Load the kernel.  We always load it at 100000h even if we're supposed to
-; load it "low"; for a "low" load we copy it down to low memory right before
-; jumping to it.
-;
-read_kernel:
-                mov si,KernelCName             ; Print kernel name part of
-                call cwritestr                  ; "Loading" message
-                mov si,dotdot_msg              ; Print dots
-                call cwritestr
-
-                mov eax,[HighMemSize]
-               sub eax,100000h                 ; Load address
-               cmp eax,[KernelSize]
-               jb near no_high_mem             ; Not enough high memory
-;
-; Move the stuff beyond the setup code to high memory at 100000h
-;
-               movzx esi,word [SetupSecs]      ; Setup sectors
-               inc esi                         ; plus 1 boot sector
-                shl esi,9                      ; Convert to bytes
-                mov ecx,8000h                  ; 32K
-               sub ecx,esi                     ; Number of bytes to copy
-               push ecx
-               shr ecx,2                       ; Convert to dwords
-               add esi,(real_mode_seg << 4)    ; Pointer to source
-                mov edi,100000h                 ; Copy to address 100000h
-                call bcopy                     ; Transfer to high memory
-
-               ; On exit EDI -> where to load the rest
-
-                mov si,dot_msg                 ; Progress report
-                call cwritestr
-                call abort_check
-
-               pop ecx                         ; Number of bytes in the initial portion
-               pop si                          ; Restore file handle/cluster pointer
-               mov eax,[KernelSize]
-               sub eax,ecx                     ; Amount of kernel left over
-               jbe high_load_done              ; Zero left (tiny kernel)
-
-               call load_high                  ; Copy the file
-
-high_load_done:
-                mov ax,real_mode_seg           ; Set to real mode seg
-                mov es,ax
-
-                mov si,dot_msg
-                call cwritestr
-
-;
-; Now see if we have an initial RAMdisk; if so, do requisite computation
-; We know we have a new kernel; the old_kernel code already will have objected
-; if we tried to load initrd using an old kernel
-;
-load_initrd:
-                test byte [initrd_flag],1
-                jz near nk_noinitrd
-                push es                         ; ES->real_mode_seg
-                push ds
-                pop es                          ; We need ES==DS
-                mov si,InitRD
-                mov di,InitRDCName
-                call unmangle_name              ; Create human-readable name
-                sub di,InitRDCName
-                mov [InitRDCNameLen],di
-                mov di,InitRD
-                call searchdir                  ; Look for it in directory
-                pop es
-               jz initrd_notthere
-               mov [es:su_ramdisklen1],ax      ; Ram disk length
-               mov [es:su_ramdisklen2],dx
-               mov edx,[HighMemSize]           ; End of memory
-               dec edx
-               mov eax,[RamdiskMax]            ; Highest address allowed by kernel
-               cmp edx,eax
-               jna memsize_ok
-               mov edx,eax                     ; Adjust to fit inside limit
-memsize_ok:
-               inc edx
-                xor dx,dx                      ; Round down to 64K boundary
-               sub edx,[es:su_ramdisklen]      ; Subtract size of ramdisk
-                xor dx,dx                      ; Round down to 64K boundary
-                mov [es:su_ramdiskat],edx      ; Load address
-               call loadinitrd                 ; Load initial ramdisk
-               jmp short initrd_end
-
-initrd_notthere:
-                mov si,err_noinitrd
-                call cwritestr
-                mov si,InitRDCName
-                call cwritestr
-                mov si,crlf_msg
-                jmp abort_load
-
-no_high_mem:    mov si,err_nohighmem           ; Error routine
-                jmp abort_load
-
-initrd_end:
-nk_noinitrd:
-;
-; Abandon hope, ye that enter here!  We do no longer permit aborts.
-;
-                call abort_check               ; Last chance!!
-
-               mov si,ready_msg
-               call cwritestr
+%endmacro
 
-               call vgaclearmode               ; We can't trust ourselves after this
-;
 ; Unload PXE stack
-;
+%define HAVE_UNLOAD_PREP
+%macro UNLOAD_PREP 0
                call unload_pxe
                cli
                xor ax,ax
                mov ss,ax
-               mov sp,7C00h                    ; Set up a more normal stack
-               
-;
-; Now, if we were supposed to load "low", copy the kernel down to 10000h
-; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
-; capable of starting their setup from a different address.
-;
-               mov ax,real_mode_seg
-               mov fs,ax
+               mov sp,7C00h                    ; Set up a conventional stack
+%endmacro
 
-;
-; Copy command line.  Unfortunately, the kernel boot protocol requires
-; the command line to exist in the 9xxxxh range even if the rest of the
-; setup doesn't.
-;
-               cli                             ; In case of hooked interrupts
-               test byte [LoadFlags],LOAD_HIGH
-               jz need_high_cmdline
-               cmp word [fs:su_version],0202h  ; Support new cmdline protocol?
-               jb need_high_cmdline
-               ; New cmdline protocol
-               ; Store 32-bit (flat) pointer to command line
-               mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
-               jmp short in_proper_place
+%include "runkernel.inc"
 
-need_high_cmdline:
 ;
-; Copy command line up to 90000h
+; COMBOOT-loading code
 ;
-               mov ax,9000h
-               mov es,ax
-               mov si,cmd_line_here
-               mov di,si
-               mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
-               mov [fs:kern_cmd_offset],di     ; Store pointer
-
-               mov cx,[CmdLineLen]
-               add cx,byte 3
-               shr cx,2                        ; Convert to dwords
-               fs rep movsd
-
-               push fs
-               pop es
-
-               test byte [LoadFlags],LOAD_HIGH
-               jnz in_proper_place             ; If high load, we're done
-
-;
-; Loading low; we can't assume it's safe to run in place.
-;
-; Copy real_mode stuff up to 90000h
-;
-               mov ax,9000h
-               mov es,ax
-               mov cx,[SetupSecs]
-               inc cx                          ; Setup + boot sector
-               shl cx,7                        ; Sectors -> dwords
-               xor si,si
-               xor di,di
-               fs rep movsd                    ; Copy setup + boot sector
-;
-; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
-; setup sectors, but the boot protocol had not yet been defined.  They
-; rely on a signature to figure out if they need to copy stuff from
-; the "protected mode" kernel area.  Unfortunately, we used that area
-; as a transfer buffer, so it's going to find the signature there.
-; Hence, zero the low 32K beyond the setup area.
-;
-               mov di,[SetupSecs]
-               inc di                          ; Setup + boot sector
-               mov cx,32768/512                ; Sectors/32K
-               sub cx,di                       ; Remaining sectors
-               shl di,9                        ; Sectors -> bytes
-               shl cx,7                        ; Sectors -> dwords
-               xor eax,eax
-               rep stosd                       ; Clear region
-;
-; Copy the kernel down to the "low" location
-;
-               mov ecx,[KernelSize]
-               add ecx,3                       ; Round upwards
-               shr ecx,2                       ; Bytes -> dwords
-               mov esi,100000h
-               mov edi,10000h
-               call bcopy
-
-;
-; Now everything is where it needs to be...
-;
-; When we get here, es points to the final segment, either
-; 9000h or real_mode_seg
-;
-in_proper_place:
-
-;
-; If the default root device is set to FLOPPY (0000h), change to
-; /dev/fd0 (0200h)
-;
-               cmp word [es:bs_rootdev],byte 0
-               jne root_not_floppy
-               mov word [es:bs_rootdev],0200h
-root_not_floppy:
-;
-; Copy the disk table to high memory, then re-initialize the floppy
-; controller
-;
-; This needs to be moved before the copy
-;
-%if 0
-               push ds
-               push bx
-               lds si,[fdctab]
-               mov di,linux_fdctab
-               mov cx,3                        ; 12 bytes
-               push di
-               rep movsd
-               pop di
-               mov [fdctab1],di                ; Save new floppy tab pos
-               mov [fdctab2],es
-               xor ax,ax
-               xor dx,dx
-               int 13h
-               pop bx
-               pop ds
-%endif
-;
-; Linux wants the floppy motor shut off before starting the kernel,
-; at least bootsect.S seems to imply so
-;
-kill_motor:
-               mov dx,03F2h
-               xor al,al
-               call slow_out
-;
-; If we're debugging, wait for a keypress so we can read any debug messages
-;
-%ifdef debug
-                xor ax,ax
-                int 16h
-%endif
-;
-; Set up segment registers and the Linux real-mode stack
-; Note: es == the real mode segment
-;
-               cli
-               mov bx,es
-               mov ds,bx
-               mov fs,bx
-               mov gs,bx
-               mov ss,bx
-               mov sp,linux_stack
-;
-; We're done... now RUN THAT KERNEL!!!!
-; Setup segment == real mode segment + 020h; we need to jump to offset
-; zero in the real mode segment.
-;
-               add bx,020h
-               push bx
-               push word 0h
-               retf
-
-;
-; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
-; initrd, and are always loaded low.
-;
-old_kernel:
-                test byte [initrd_flag],1      ; Old kernel can't have initrd
-                jz load_old_kernel
-                mov si,err_oldkernel
-                jmp abort_load
-load_old_kernel:
-               mov word [SetupSecs],4          ; Always 4 setup sectors
-               mov byte [LoadFlags],0          ; Always low
-               jmp read_kernel
-
-;
-; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
-; except that it may, of course, not contain any DOS system calls.  We
-; do, however, allow the execution of INT 20h to return to SYSLINUX.
-;
-is_comboot_image:
-               and dx,dx
-               jnz near comboot_too_large
-               cmp ax,0ff00h           ; Max size in bytes
-               jae comboot_too_large
-
-               ;
-               ; Set up the DOS vectors in the IVT (INT 20h-3fh)
-               ;
-               mov dword [4*0x20],comboot_return       ; INT 20h vector
-               mov eax,comboot_bogus
-               mov di,4*0x21
-               mov cx,31               ; All remaining DOS vectors
-               rep stosd
-       
-               mov cx,comboot_seg
-               mov es,cx
-
-               mov bx,100h             ; Load at <seg>:0100h
-
-               mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
-               call getfssec
-
-               xor di,di
-               mov cx,64               ; 256 bytes (size of PSP)
-               xor eax,eax             ; Clear PSP
-               rep stosd
-
-               mov word [es:0], 020CDh ; INT 20h instruction
-               ; First non-free paragraph
-               mov word [es:02h], comboot_seg+1000h
-
-               ; Copy the command line from high memory
-               mov cx,125              ; Max cmdline len (minus space and CR)
-               mov si,[CmdOptPtr]
-               mov di,081h             ; Offset in PSP for command line
-               mov al,' '              ; DOS command lines begin with a space
-               stosb
-
-comboot_cmd_cp:        lodsb
-               and al,al
-               jz comboot_end_cmd
-               stosb
-               loop comboot_cmd_cp
-comboot_end_cmd: mov al,0Dh            ; CR after last character
-               stosb
-               mov al,126              ; Include space but not CR
-               sub al,cl
-               mov [es:80h], al        ; Store command line length
-
-               mov [SavedSSSP],sp
-               mov ax,ss               ; Save away SS:SP
-               mov [SavedSSSP+2],ax
-
-               call vgaclearmode       ; Reset video
-
-               mov ax,es
-               mov ds,ax
-               mov ss,ax
-               xor sp,sp
-               push word 0             ; Return to address 0 -> exit
-
-               jmp comboot_seg:100h    ; Run it
-
-; Looks like a COMBOOT image but too large
-comboot_too_large:
-               mov si,err_comlarge
-               call cwritestr
-cb_enter:      jmp enter_command
-
-; Proper return vector
-comboot_return:        cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ds,ax
-               mov es,ax
-               lss sp,[SavedSSSP]
-               sti
-               cld
-               jmp short cb_enter
-
-; Attempted to execute DOS system call
-comboot_bogus: cli                     ; Don't trust anyone
-               xor ax,ax
-               mov ds,ax
-               mov es,ax
-               lss sp,[SavedSSSP]
-               sti
-               cld
-               mov si,KernelCName
-               call cwritestr
-               mov si,err_notdos
-               call cwritestr
-               jmp short cb_enter
+%include "comboot.inc"
 
 ;
 ; Load a boot sector
@@ -1840,424 +1216,6 @@ local_boot:
                retf                            ; Return to PXE
 
 ;
-; 32-bit bcopy routine for real mode
-;
-; We enter protected mode, set up a flat 32-bit environment, run rep movsd
-; and then exit.  IMPORTANT: This code assumes cs == 0.
-;
-; This code is probably excessively anal-retentive in its handling of
-; segments, but this stuff is painful enough as it is without having to rely
-; on everything happening "as it ought to."
-;
-               align 4
-bcopy_gdt:     dw bcopy_gdt_size-1     ; Null descriptor - contains GDT
-               dd bcopy_gdt            ; pointer for LGDT instruction
-               dw 0
-               dd 0000ffffh            ; Code segment, use16, readable,
-               dd 00009b00h            ; present, dpl 0, cover 64K
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 008f9300h            ; present, dpl 0, cover all 4G
-               dd 0000ffffh            ; Data segment, use16, read/write,
-               dd 00009300h            ; present, dpl 0, cover 64K
-bcopy_gdt_size:        equ $-bcopy_gdt
-
-bcopy:         push eax
-               pushf                   ; Saves, among others, the IF flag
-               push gs
-               push fs
-               push ds
-               push es
-               mov [cs:SavedSSSP],sp
-               mov ax,ss
-               mov [cs:SavedSSSP+2],ax
-
-               cli
-               call enable_a20
-
-               o32 lgdt [cs:bcopy_gdt]
-               mov eax,cr0
-               or al,1
-               mov cr0,eax             ; Enter protected mode
-               jmp 08h:.in_pm
-
-.in_pm:                mov ax,10h              ; Data segment selector
-               mov es,ax
-               mov ds,ax
-
-               mov al,18h              ; "Real-mode-like" data segment
-               mov ss,ax
-               mov fs,ax
-               mov gs,ax       
-       
-               a32 rep movsd           ; Do our business
-               
-               mov es,ax               ; Set to "real-mode-like"
-               mov ds,ax
-       
-               mov eax,cr0
-               and al,~1
-               mov cr0,eax             ; Disable protected mode
-               jmp 0:.in_rm
-
-.in_rm:                ; Back in real mode
-               lss sp,[cs:SavedSSSP]
-               pop es
-               pop ds
-               pop fs
-               pop gs
-               call disable_a20
-
-               popf                    ; Re-enables interrupts
-               pop eax
-               ret
-
-;
-; Routines to enable and disable (yuck) A20.  These routines are gathered
-; from tips from a couple of sources, including the Linux kernel and
-; http://www.x86.org/.  The need for the delay to be as large as given here
-; is indicated by Donnie Barnes of RedHat, the problematic system being an
-; IBM ThinkPad 760EL.
-;
-; We typically toggle A20 twice for every 64K transferred.
-; 
-%define        io_delay        call _io_delay
-%define IO_DELAY_PORT  80h             ; Invalid port (we hope!)
-%define disable_wait   32              ; How long to wait for a disable
-
-%define A20_DUNNO      0               ; A20 type unknown
-%define A20_NONE       1               ; A20 always on?
-%define A20_BIOS       2               ; A20 BIOS enable
-%define A20_KBC                3               ; A20 through KBC
-%define A20_FAST       4               ; A20 through port 92h
-
-slow_out:      out dx, al              ; Fall through
-
-_io_delay:     out IO_DELAY_PORT,al
-               out IO_DELAY_PORT,al
-               ret
-
-enable_a20:
-               pushad
-               mov byte [cs:A20Tries],255 ; Times to try to make this work
-
-try_enable_a20:
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-;
-; If the A20 type is known, jump straight to type
-;
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20List]
-
-;
-; First, see if we are on a system with no A20 gate
-;
-a20_dunno:
-a20_none:
-               mov byte [cs:A20Type], A20_NONE
-               call a20_test
-               jnz a20_done
-
-;
-; Next, try the BIOS (INT 15h AX=2401h)
-;
-a20_bios:
-               mov byte [cs:A20Type], A20_BIOS
-               mov ax,2401h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-
-               call a20_test
-               jnz a20_done
-
-;
-; Enable the keyboard controller A20 gate
-;
-a20_kbc:
-               mov dl, 1                       ; Allow early exit
-               call empty_8042
-               jnz a20_done                    ; A20 live, no need to use KBC
-
-               mov byte [cs:A20Type], A20_KBC  ; Starting KBC command sequence
-
-               mov al,0D1h                     ; Command write
-               out 064h, al
-               call empty_8042_uncond
-
-               mov al,0DFh                     ; A20 on
-               out 060h, al
-               call empty_8042_uncond
-
-               ; Verify that A20 actually is enabled.  Do that by
-               ; observing a word in low memory and the same word in
-               ; the HMA until they are no longer coherent.  Note that
-               ; we don't do the same check in the disable case, because
-               ; we don't want to *require* A20 masking (SYSLINUX should
-               ; work fine without it, if the BIOS does.)
-.kbc_wait:     push cx
-               xor cx,cx
-.kbc_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .kbc_wait_loop
-
-               pop cx
-;
-; Running out of options here.  Final attempt: enable the "fast A20 gate"
-;
-a20_fast:
-               mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
-               in al, 092h
-               or al,02h
-               and al,~01h                     ; Don't accidentally reset the machine!
-               out 092h, al
-
-.fast_wait:    push cx
-               xor cx,cx
-.fast_wait_loop:
-               call a20_test
-               jnz a20_done_pop
-               loop .fast_wait_loop
-
-               pop cx
-
-;
-; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
-; and report failure to the user.
-;
-
-
-               dec byte [cs:A20Tries]
-               jnz try_enable_a20
-
-               mov si, err_a20
-               jmp abort_load
-;
-; A20 unmasked, proceed...
-;
-a20_done_pop:  pop cx
-a20_done:      popad
-               ret
-
-;
-; This routine tests if A20 is enabled (ZF = 0).  This routine
-; must not destroy any register contents.
-;
-a20_test:
-               push es
-               push cx
-               push ax
-               mov cx,0FFFFh           ; HMA = segment 0FFFFh
-               mov es,cx
-               mov cx,32               ; Loop count
-               mov ax,[cs:A20Test]
-.a20_wait:     inc ax
-               mov [cs:A20Test],ax
-               io_delay                ; Serialize, and fix delay
-               cmp ax,[es:A20Test+10h]
-               loopz .a20_wait
-.a20_done:     pop ax
-               pop cx
-               pop es
-               ret
-
-disable_a20:
-               pushad
-;
-; Flush the caches
-;
-;              call try_wbinvd
-
-               mov bp,[cs:A20Type]
-               add bp,bp                       ; Convert to word offset
-               jmp word [cs:bp+A20DList]
-
-a20d_bios:
-               mov ax,2400h
-               pushf                           ; Some BIOSes muck with IF
-               int 15h
-               popf
-               jmp short a20d_snooze
-
-;
-; Disable the "fast A20 gate"
-;
-a20d_fast:
-               in al, 092h
-               and al,~03h
-               out 092h, al
-               jmp short a20d_snooze
-
-;
-; Disable the keyboard controller A20 gate
-;
-a20d_kbc:
-               call empty_8042_uncond
-               mov al,0D1h
-               out 064h, al            ; Command write
-               call empty_8042_uncond
-               mov al,0DDh             ; A20 off
-               out 060h, al
-               call empty_8042_uncond
-               ; Wait a bit for it to take effect
-a20d_snooze:
-               push cx
-               mov cx, disable_wait
-.delayloop:    call a20_test
-               jz .disabled
-               loop .delayloop
-.disabled:     pop cx
-a20d_dunno:
-a20d_none:
-               popad
-               ret
-
-;
-; Routine to empty the 8042 KBC controller.  If dl != 0
-; then we will test A20 in the loop and exit if A20 is
-; suddenly enabled.
-;
-empty_8042_uncond:
-               xor dl,dl
-empty_8042:
-               call a20_test
-               jz .a20_on
-               and dl,dl
-               jnz .done
-.a20_on:       io_delay
-               in al, 064h             ; Status port
-               test al,1
-               jz .no_output
-               io_delay
-               in al, 060h             ; Read input
-               jmp short empty_8042
-.no_output:
-               test al,2
-               jnz empty_8042
-               io_delay
-.done:         ret     
-
-;
-; WBINVD instruction; gets auto-eliminated on 386 CPUs
-;
-try_wbinvd:
-               wbinvd
-               ret
-
-;
-; Load RAM disk into high memory
-;
-; Need to be set:
-;      su_ramdiskat    - Where in memory to load
-;      su_ramdisklen   - Size of file
-;      SI              - initrd filehandle/cluster pointer
-;
-loadinitrd:
-                push es                         ; Save ES on entry
-               mov ax,real_mode_seg
-                mov es,ax
-                mov edi,[es:su_ramdiskat]      ; initrd load address
-               push si
-               mov si,crlfloading_msg          ; Write "Loading "
-               call cwritestr
-                mov si,InitRDCName             ; Write ramdisk name
-                call cwritestr
-                mov si,dotdot_msg              ; Write dots
-                call cwritestr
-               pop si
-
-               mov eax,[es:su_ramdisklen]
-               call load_high                  ; Load the file
-
-               call crlf
-                pop es                          ; Restore original ES
-                ret
-
-;
-; load_high:   loads (the remainder of) a file into high memory.
-;              This routine prints dots for each 64K transferred, and
-;              calls abort_check periodically.
-; 
-;              The xfer_buf_seg is used as a bounce buffer.
-;
-;              The input address (EDI) should be dword aligned, and the final
-;              dword written is padded with zeroes if necessary.
-;
-; Inputs:      SI  = file handle/cluster pointer
-;              EDI = target address in high memory
-;              EAX = size of remaining file in bytes
-;
-; Outputs:     SI  = file handle/cluster pointer
-;              EDI = first untouched address (not including padding)
-;
-load_high:
-               push es
-
-               mov bx,xfer_buf_seg
-               mov es,bx
-
-.read_loop:
-               and si,si                       ; If SI == 0 then we have end of file
-               jz .eof
-               push si
-               mov si,dot_msg
-               call cwritestr
-               pop si
-               call abort_check
-
-               push eax                        ; <A> Total bytes to transfer
-               cmp eax,(1 << 16)               ; Max 64K in one transfer
-               jna .size_ok
-               mov eax,(1 << 16)
-.size_ok:
-               xor edx,edx
-               push eax                        ; <B> Bytes transferred this chunk
-               movzx ecx,word [ClustSize]
-               div ecx                         ; Convert to clusters
-               ; Round up...
-               add edx,byte -1                 ; Sets CF if EDX >= 1
-               adc eax,byte 0                  ; Add 1 to EAX if CF set
-
-               ; Now (e)ax contains the number of clusters to get
-               push edi                        ; <C> Target buffer
-               mov cx,ax
-               xor bx,bx                       ; ES:0
-               call getfssec                   ; Load the data into xfer_buf_seg
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               push ecx                        ; <B> Byte count this round 
-               push edi                        ; <C> Target buffer
-.fix_slop:
-               test cl,3
-               jz .noslop
-               ; The last dword fractional - pad with zeroes
-               ; Zero-padding is critical for multi-file initramfs.
-               mov byte [es:ecx],0
-               inc ecx
-               jmp short .fix_slop
-.noslop:
-               shr ecx,2                       ; Convert to dwords
-               push esi                        ; <D> File handle/cluster pointer
-               mov esi,(xfer_buf_seg << 4)     ; Source address
-               call bcopy                      ; Copy to high memory
-               pop esi                         ; <D> File handle/cluster pointer
-               pop edi                         ; <C> Target buffer
-               pop ecx                         ; <B> Byte count this round
-               pop eax                         ; <A> Total bytes to transfer
-               add edi,ecx
-               sub eax,ecx
-               jnz .read_loop                  ; More to read...
-               
-.eof:
-               pop es
-               ret
-
-;
 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
 ;
 abort_check:
@@ -3324,6 +2282,8 @@ writestr  equ cwritestr
 %include "writehex.inc"                ; Hexadecimal output
 %include "parseconfig.inc"     ; High-level config file handling
 %include "parsecmd.inc"                ; Low-level config file handling
+%include "bcopy32.inc"         ; 32-bit bcopy
+%include "loadhigh.inc"                ; Load a file into high memory
 %include "font.inc"            ; VGA font stuff
 %include "graphics.inc"                ; VGA graphics
 
@@ -3538,9 +2498,6 @@ ForcePrompt       dw 0                    ; Force prompt
 AllowImplicit   dw 1                    ; Allow implicit kernels
 SerialPort     dw 0                    ; Serial port base (or 0 for no serial port)
 NextSocket     dw 49152                ; Counter for allocating socket numbers
-A20List                dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
-A20DList       dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
-A20Type                dw A20_DUNNO            ; A20 type unknown
 VGAFontSize    dw 16                   ; Defaults to 16 byte font
 UserFont       db 0                    ; Using a user-specified font
 ScrollAttribute        db 07h                  ; White on black (for text mode)
diff --git a/runkernel.inc b/runkernel.inc
new file mode 100644 (file)
index 0000000..cc1dfb1
--- /dev/null
@@ -0,0 +1,614 @@
+;; $Id$
+;; -----------------------------------------------------------------------
+;;   
+;;   Copyright 1994-2002 H. Peter Anvin - All Rights Reserved
+;;
+;;   This program is free software; you can redistribute it and/or modify
+;;   it under the terms of the GNU General Public License as published by
+;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;;   Bostom MA 02111-1307, USA; either version 2 of the License, or
+;;   (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; runkernel.inc
+;; 
+;; Common code for running a Linux kernel
+;;
+
+;
+; Hook macros, that may or may not be defined
+;
+%ifndef HAVE_SPECIAL_APPEND
+%macro SPECIAL_APPEND 0
+%endmacro
+%endif
+
+%ifndef HAVE_UNLOAD_PREP
+%macro UNLOAD_PREP 0
+%endmacro
+%endif
+
+;
+; A Linux kernel consists of three parts: boot sector, setup code, and
+; kernel code. The boot sector is never executed when using an external
+; booting utility, but it contains some status bytes that are necessary.
+;
+; First check that our kernel is at least 1K and less than 8M (if it is
+; more than 8M, we need to change the logic for loading it anyway...)
+;
+; We used to require the kernel to be 64K or larger, but it has gotten
+; popular to use the Linux kernel format for other things, which may
+; not be so large.
+;
+is_linux_kernel:
+                cmp dx,80h                     ; 8 megs
+               ja kernel_corrupt
+               and dx,dx
+               jnz kernel_sane
+               cmp ax,1024                     ; Bootsect + 1 setup sect
+               jb near kernel_corrupt
+kernel_sane:   push ax
+               push dx
+               push si
+               mov si,loading_msg
+                call cwritestr
+;
+; Now start transferring the kernel
+;
+               push word real_mode_seg
+               pop es
+
+               movzx eax,ax                    ; Fix this by using a 32-bit
+               shl edx,16                      ; register for the kernel size
+               or eax,edx
+               mov [KernelSize],eax
+               xor edx,edx
+               div dword [ClustSize]           ; # of clusters total
+               ; Round up...
+               add edx,byte -1                 ; Sets CF if EDX >= 1
+               adc eax,byte 0                  ; Add 1 to EAX if CF set
+                mov [KernelClust],eax
+
+;
+; Now, if we transfer these straight, we'll hit 64K boundaries.         Hence we
+; have to see if we're loading more than 64K, and if so, load it step by
+; step.
+;
+
+;
+; Start by loading the bootsector/setup code, to see if we need to
+; do something funky.  It should fit in the first 32K (loading 64K won't
+; work since we might have funny stuff up near the end of memory).
+; If we have larger than 32K clusters, yes, we're hosed.
+;
+               call abort_check                ; Check for abort key
+               mov ecx,[ClustPerMoby]
+               shr ecx,1                       ; Half a moby
+               cmp ecx,[KernelClust]
+               jna .normalkernel
+               mov ecx,[KernelClust]
+.normalkernel:
+               sub [KernelClust],ecx
+               xor bx,bx
+                pop si                          ; Cluster pointer on stack
+               call getfssec
+                cmp word [es:bs_bootsign],0AA55h
+               jne near kernel_corrupt         ; Boot sec signature missing
+;
+; Get the BIOS' idea of what the size of high memory is.
+;
+               push si                         ; Save our cluster pointer!
+;
+; First, try INT 15:E820 (get BIOS memory map)
+;
+get_e820:
+               push es
+               xor ebx,ebx                     ; Start with first record
+               mov es,bx                       ; Need ES = DS = 0 for now
+               jmp short .do_e820              ; Skip "at end" check first time!
+.int_loop:     and ebx,ebx                     ; If we're back at beginning...
+               jz no_e820                      ; ... bail; nothing found
+.do_e820:      mov eax,0000E820h
+               mov edx,534D4150h               ; "SMAP" backwards
+               mov ecx,20
+               mov di,E820Buf
+               int 15h
+               jc no_e820
+               cmp eax,534D4150h
+               jne no_e820
+;
+; Look for a memory block starting at <= 1 MB and continuing upward
+;
+               cmp dword [E820Buf+4], byte 0
+               ja .int_loop                    ; Start >= 4 GB?
+               mov edx, (1 << 20)
+               sub edx, [E820Buf]
+               jb .int_loop                    ; Start >= 1 MB?
+               mov eax, 0FFFFFFFFh
+               cmp dword [E820Buf+12], byte 0
+               ja .huge                        ; Size >= 4 GB
+               mov eax, [E820Buf+8]
+.huge:         sub eax, edx                    ; Adjust size to start at 1 MB
+               jbe .int_loop                   ; Completely below 1 MB?
+
+               ; Now EAX contains the size of memory 1 MB...up
+               cmp dword [E820Buf+16], byte 1
+               jne near err_nohighmem          ; High memory isn't usable memory!!!!
+
+               ; We're good!
+               pop es
+               jmp short got_highmem_add1mb    ; Still need to add low 1 MB
+
+;
+; INT 15:E820 failed.  Try INT 15:E801.
+;
+no_e820:       pop es
+
+               mov ax,0e801h                   ; Query high memory (semi-recent)
+               int 15h
+               jc no_e801
+               cmp ax,3c00h
+               ja no_e801                      ; > 3C00h something's wrong with this call
+               jb e801_hole                    ; If memory hole we can only use low part
+
+               mov ax,bx
+               shl eax,16                      ; 64K chunks
+               add eax,(16 << 20)              ; Add first 16M
+               jmp short got_highmem                           
+
+;
+; INT 15:E801 failed.  Try INT 15:88.
+;
+no_e801:
+               mov ah,88h                      ; Query high memory (oldest)
+               int 15h
+               cmp ax,14*1024                  ; Don't trust memory >15M
+               jna e801_hole
+               mov ax,14*1024
+e801_hole:
+               and eax,0ffffh
+               shl eax,10                      ; Convert from kilobytes
+got_highmem_add1mb:
+               add eax,(1 << 20)               ; First megabyte
+got_highmem:
+%if HIGHMEM_SLOP != 0
+               sub eax,HIGHMEM_SLOP
+%endif
+               mov [HighMemSize],eax
+
+;
+; Construct the command line (append options have already been copied)
+;
+construct_cmdline:
+               mov di,[CmdLinePtr]
+                mov si,boot_image              ; BOOT_IMAGE=
+                mov cx,boot_image_len
+                rep movsb
+                mov si,KernelCName             ; Unmangled kernel name
+                mov cx,[KernelCNameLen]
+                rep movsb
+                mov al,' '                      ; Space
+                stosb
+
+               SPECIAL_APPEND                  ; Module-specific hook
+
+                mov si,[CmdOptPtr]              ; Options from user input
+               mov cx,(kern_cmd_len+3) >> 2
+               rep movsd
+
+;
+; Scan through the command line for anything that looks like we might be
+; interested in.  The original version of this code automatically assumed
+; the first option was BOOT_IMAGE=, but that is no longer certain.
+;
+               mov si,cmd_line_here
+                mov byte [initrd_flag],0
+                push es                                ; Set DS <- real_mode_seg
+                pop ds
+get_next_opt:   lodsb
+               and al,al
+               jz near cmdline_end
+               cmp al,' '
+               jbe get_next_opt
+               dec si
+                mov eax,[si]
+                cmp eax,'vga='
+               je is_vga_cmd
+                cmp eax,'mem='
+               je is_mem_cmd
+                push es                         ; Save ES -> real_mode_seg
+                push cs
+                pop es                          ; Set ES <- normal DS
+                mov di,initrd_cmd
+               mov cx,initrd_cmd_len
+               repe cmpsb
+                jne not_initrd
+               mov di,InitRD
+                push si                         ; mangle_dir mangles si
+                call mangle_name                ; Mangle ramdisk name
+                pop si
+               cmp byte [es:InitRD],NULLFILE   ; Null filename?
+                seta byte [es:initrd_flag]     ; Set flag if not
+not_initrd:    pop es                          ; Restore ES -> real_mode_seg
+skip_this_opt:  lodsb                           ; Load from command line
+                cmp al,' '
+                ja skip_this_opt
+                dec si
+                jmp short get_next_opt
+is_vga_cmd:
+                add si,byte 4
+                mov eax,[si]
+                mov bx,-1
+                cmp eax, 'norm'                 ; vga=normal
+                je vc0
+                and eax,0ffffffh               ; 3 bytes
+                mov bx,-2
+                cmp eax, 'ext'                  ; vga=ext
+                je vc0
+                mov bx,-3
+                cmp eax, 'ask'                  ; vga=ask
+                je vc0
+                call parseint                   ; vga=<number>
+               jc skip_this_opt                ; Not an integer
+vc0:           mov [bs_vidmode],bx             ; Set video mode
+               jmp short skip_this_opt
+is_mem_cmd:
+                add si,byte 4
+                call parseint
+               jc skip_this_opt                ; Not an integer
+%if HIGHMEM_SLOP != 0
+               sub ebx,HIGHMEM_SLOP
+%endif
+               mov [cs:HighMemSize],ebx
+               jmp short skip_this_opt
+cmdline_end:
+                push cs                         ; Restore standard DS
+                pop ds
+               sub si,cmd_line_here
+               mov [CmdLineLen],si             ; Length including final null
+;
+; Now check if we have a large kernel, which needs to be loaded high
+;
+               mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
+               cmp dword [es:su_header],HEADER_ID      ; New setup code ID
+               jne near old_kernel             ; Old kernel, load low
+               cmp word [es:su_version],0200h  ; Setup code version 2.0
+               jb near old_kernel              ; Old kernel, load low
+                cmp word [es:su_version],0201h ; Version 2.01+?
+                jb new_kernel                   ; If 2.00, skip this step
+                mov word [es:su_heapend],linux_stack   ; Set up the heap
+                or byte [es:su_loadflags],80h  ; Let the kernel know we care
+               cmp word [es:su_version],0203h  ; Version 2.03+?
+               jb new_kernel                   ; Not 2.03+
+               mov eax,[es:su_ramdisk_max]
+               mov [RamdiskMax],eax            ; Set the ramdisk limit
+
+;
+; We definitely have a new-style kernel.  Let the kernel know who we are,
+; and that we are clueful
+;
+new_kernel:
+               mov byte [es:su_loader],my_id   ; Show some ID
+               movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
+               mov [SetupSecs],ax
+;
+; About to load the kernel.  This is a modern kernel, so use the boot flags
+; we were provided.
+;
+                mov al,[es:su_loadflags]
+               mov [LoadFlags],al
+;
+; Load the kernel.  We always load it at 100000h even if we're supposed to
+; load it "low"; for a "low" load we copy it down to low memory right before
+; jumping to it.
+;
+read_kernel:
+                mov si,KernelCName             ; Print kernel name part of
+                call cwritestr                  ; "Loading" message
+                mov si,dotdot_msg              ; Print dots
+                call cwritestr
+
+                mov eax,[HighMemSize]
+               sub eax,100000h                 ; Load address
+               cmp eax,[KernelSize]
+               jb near no_high_mem             ; Not enough high memory
+;
+; Move the stuff beyond the setup code to high memory at 100000h
+;
+               movzx esi,word [SetupSecs]      ; Setup sectors
+               inc esi                         ; plus 1 boot sector
+                shl esi,9                      ; Convert to bytes
+                mov ecx,8000h                  ; 32K
+               sub ecx,esi                     ; Number of bytes to copy
+               push ecx
+               shr ecx,2                       ; Convert to dwords
+               add esi,(real_mode_seg << 4)    ; Pointer to source
+                mov edi,100000h                 ; Copy to address 100000h
+                call bcopy                     ; Transfer to high memory
+
+               ; On exit EDI -> where to load the rest
+
+                mov si,dot_msg                 ; Progress report
+                call cwritestr
+                call abort_check
+
+               pop ecx                         ; Number of bytes in the initial portion
+               pop si                          ; Restore file handle/cluster pointer
+               mov eax,[KernelSize]
+               sub eax,ecx                     ; Amount of kernel left over
+               jbe high_load_done              ; Zero left (tiny kernel)
+
+               call load_high                  ; Copy the file
+
+high_load_done:
+                mov ax,real_mode_seg           ; Set to real mode seg
+                mov es,ax
+
+                mov si,dot_msg
+                call cwritestr
+
+;
+; Now see if we have an initial RAMdisk; if so, do requisite computation
+; We know we have a new kernel; the old_kernel code already will have objected
+; if we tried to load initrd using an old kernel
+;
+load_initrd:
+                test byte [initrd_flag],1
+                jz near nk_noinitrd
+                push es                         ; ES->real_mode_seg
+                push ds
+                pop es                          ; We need ES==DS
+                mov si,InitRD
+                mov di,InitRDCName
+                call unmangle_name              ; Create human-readable name
+                sub di,InitRDCName
+                mov [InitRDCNameLen],di
+                mov di,InitRD
+                call searchdir                  ; Look for it in directory
+                pop es
+               jz initrd_notthere
+               mov [es:su_ramdisklen1],ax      ; Ram disk length
+               mov [es:su_ramdisklen2],dx
+               mov edx,[HighMemSize]           ; End of memory
+               dec edx
+               mov eax,[RamdiskMax]            ; Highest address allowed by kernel
+               cmp edx,eax
+               jna memsize_ok
+               mov edx,eax                     ; Adjust to fit inside limit
+memsize_ok:
+               inc edx
+                xor dx,dx                      ; Round down to 64K boundary
+               sub edx,[es:su_ramdisklen]      ; Subtract size of ramdisk
+                xor dx,dx                      ; Round down to 64K boundary
+                mov [es:su_ramdiskat],edx      ; Load address
+               call loadinitrd                 ; Load initial ramdisk
+               jmp short initrd_end
+
+initrd_notthere:
+                mov si,err_noinitrd
+                call cwritestr
+                mov si,InitRDCName
+                call cwritestr
+                mov si,crlf_msg
+                jmp abort_load
+
+no_high_mem:    mov si,err_nohighmem           ; Error routine
+                jmp abort_load
+
+initrd_end:
+nk_noinitrd:
+;
+; Abandon hope, ye that enter here!  We do no longer permit aborts.
+;
+                call abort_check               ; Last chance!!
+
+               mov si,ready_msg
+               call cwritestr
+
+               call vgaclearmode               ; We can't trust ourselves after this
+
+               UNLOAD_PREP                     ; Module-specific hook
+
+;
+; Now, if we were supposed to load "low", copy the kernel down to 10000h
+; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
+; capable of starting their setup from a different address.
+;
+               mov ax,real_mode_seg
+               mov fs,ax
+
+;
+; Copy command line.  Unfortunately, the kernel boot protocol requires
+; the command line to exist in the 9xxxxh range even if the rest of the
+; setup doesn't.
+;
+               cli                             ; In case of hooked interrupts
+               test byte [LoadFlags],LOAD_HIGH
+               jz need_high_cmdline
+               cmp word [fs:su_version],0202h  ; Support new cmdline protocol?
+               jb need_high_cmdline
+               ; New cmdline protocol
+               ; Store 32-bit (flat) pointer to command line
+               mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
+               jmp short in_proper_place
+
+need_high_cmdline:
+;
+; Copy command line up to 90000h
+;
+               mov ax,9000h
+               mov es,ax
+               mov si,cmd_line_here
+               mov di,si
+               mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
+               mov [fs:kern_cmd_offset],di     ; Store pointer
+
+               mov cx,[CmdLineLen]
+               add cx,byte 3
+               shr cx,2                        ; Convert to dwords
+               fs rep movsd
+
+               push fs
+               pop es
+
+               test byte [LoadFlags],LOAD_HIGH
+               jnz in_proper_place             ; If high load, we're done
+
+;
+; Loading low; we can't assume it's safe to run in place.
+;
+; Copy real_mode stuff up to 90000h
+;
+               mov ax,9000h
+               mov es,ax
+               mov cx,[SetupSecs]
+               inc cx                          ; Setup + boot sector
+               shl cx,7                        ; Sectors -> dwords
+               xor si,si
+               xor di,di
+               fs rep movsd                    ; Copy setup + boot sector
+;
+; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
+; setup sectors, but the boot protocol had not yet been defined.  They
+; rely on a signature to figure out if they need to copy stuff from
+; the "protected mode" kernel area.  Unfortunately, we used that area
+; as a transfer buffer, so it's going to find the signature there.
+; Hence, zero the low 32K beyond the setup area.
+;
+               mov di,[SetupSecs]
+               inc di                          ; Setup + boot sector
+               mov cx,32768/512                ; Sectors/32K
+               sub cx,di                       ; Remaining sectors
+               shl di,9                        ; Sectors -> bytes
+               shl cx,7                        ; Sectors -> dwords
+               xor eax,eax
+               rep stosd                       ; Clear region
+;
+; Copy the kernel down to the "low" location
+;
+               mov ecx,[KernelSize]
+               add ecx,3                       ; Round upwards
+               shr ecx,2                       ; Bytes -> dwords
+               mov esi,100000h
+               mov edi,10000h
+               call bcopy
+
+;
+; Now everything is where it needs to be...
+;
+; When we get here, es points to the final segment, either
+; 9000h or real_mode_seg
+;
+in_proper_place:
+
+;
+; If the default root device is set to FLOPPY (0000h), change to
+; /dev/fd0 (0200h)
+;
+               cmp word [es:bs_rootdev],byte 0
+               jne root_not_floppy
+               mov word [es:bs_rootdev],0200h
+root_not_floppy:
+;
+; Copy the disk table to high memory, then re-initialize the floppy
+; controller
+;
+; This needs to be moved before the copy
+;
+%if 0
+               push ds
+               push bx
+               lds si,[fdctab]
+               mov di,linux_fdctab
+               mov cx,3                        ; 12 bytes
+               push di
+               rep movsd
+               pop di
+               mov [fdctab1],di                ; Save new floppy tab pos
+               mov [fdctab2],es
+               xor ax,ax
+               xor dx,dx
+               int 13h
+               pop bx
+               pop ds
+%endif
+;
+; Linux wants the floppy motor shut off before starting the kernel,
+; at least bootsect.S seems to imply so
+;
+kill_motor:
+               mov dx,03F2h
+               xor al,al
+               call slow_out
+;
+; If we're debugging, wait for a keypress so we can read any debug messages
+;
+%ifdef debug
+                xor ax,ax
+                int 16h
+%endif
+;
+; Set up segment registers and the Linux real-mode stack
+; Note: es == the real mode segment
+;
+               cli
+               mov bx,es
+               mov ds,bx
+               mov fs,bx
+               mov gs,bx
+               mov ss,bx
+               mov sp,linux_stack
+;
+; We're done... now RUN THAT KERNEL!!!!
+; Setup segment == real mode segment + 020h; we need to jump to offset
+; zero in the real mode segment.
+;
+               add bx,020h
+               push bx
+               push word 0h
+               retf
+
+;
+; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
+; initrd, and are always loaded low.
+;
+old_kernel:
+                test byte [initrd_flag],1      ; Old kernel can't have initrd
+                jz load_old_kernel
+                mov si,err_oldkernel
+                jmp abort_load
+load_old_kernel:
+               mov word [SetupSecs],4          ; Always 4 setup sectors
+               mov byte [LoadFlags],0          ; Always low
+               jmp read_kernel
+
+;
+; Load RAM disk into high memory
+;
+; Need to be set:
+;      su_ramdiskat    - Where in memory to load
+;      su_ramdisklen   - Size of file
+;      SI              - initrd filehandle/cluster pointer
+;
+loadinitrd:
+                push es                         ; Save ES on entry
+               mov ax,real_mode_seg
+                mov es,ax
+                mov edi,[es:su_ramdiskat]      ; initrd load address
+               push si
+               mov si,crlfloading_msg          ; Write "Loading "
+               call cwritestr
+                mov si,InitRDCName             ; Write ramdisk name
+                call cwritestr
+                mov si,dotdot_msg              ; Write dots
+                call cwritestr
+               pop si
+
+               mov eax,[es:su_ramdisklen]
+               call load_high                  ; Load the file
+
+               call crlf
+                pop es                          ; Restore original ES
+                ret