summaryrefslogtreecommitdiff
path: root/runkernel.inc
diff options
context:
space:
mode:
authorhpa <hpa>2002-04-27 23:41:49 +0000
committerhpa <hpa>2002-04-27 23:41:49 +0000
commit7fb748081728cf625444cd7f37c96ae4435e06a6 (patch)
treee6a4fa02937168ed23b31f517743ee05611565ac /runkernel.inc
parentac0f6669a22e4805d4e2ecb4a9df5ca43c109af3 (diff)
downloadsyslinux-7fb748081728cf625444cd7f37c96ae4435e06a6.tar.gz
More factoring of common code
Diffstat (limited to 'runkernel.inc')
-rw-r--r--runkernel.inc614
1 files changed, 614 insertions, 0 deletions
diff --git a/runkernel.inc b/runkernel.inc
new file mode 100644
index 00000000..cc1dfb1e
--- /dev/null
+++ b/runkernel.inc
@@ -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