aboutsummaryrefslogtreecommitdiffstats
path: root/core/runkernel.inc
diff options
context:
space:
mode:
Diffstat (limited to 'core/runkernel.inc')
-rw-r--r--core/runkernel.inc634
1 files changed, 634 insertions, 0 deletions
diff --git a/core/runkernel.inc b/core/runkernel.inc
new file mode 100644
index 00000000..abd23782
--- /dev/null
+++ b/core/runkernel.inc
@@ -0,0 +1,634 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 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,
+;; Boston 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, or else it isn't long
+; enough to have the appropriate headers.
+;
+; 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.
+;
+; Additionally, we used to have a test for 8 MB or smaller. Equally
+; obsolete.
+;
+is_linux_kernel:
+ push si ; <A> file pointer
+ mov si,loading_msg
+ call cwritestr
+ mov si,KernelCName ; Print kernel name part of
+ call cwritestr ; "Loading" message
+
+
+;
+; Now start transferring the kernel
+;
+ push word real_mode_seg
+ pop es
+
+;
+; 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).
+;
+ call dot_pause ; Check for abort key
+ mov cx,8000h >> SECTOR_SHIFT ; Half a moby (32K)
+ xor bx,bx
+ pop si ; <A> file pointer
+ call getfssec
+ cmp cx,1024
+ jb kernel_corrupt
+ cmp word [es:bs_bootsign],0AA55h
+ jne kernel_corrupt ; Boot sec signature missing
+
+;
+; Save the file pointer for later...
+;
+ push si ; <A> file pointer
+
+;
+; 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
+ call strcpy
+
+;
+; 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
+ xor ax,ax
+ mov [InitRDPtr],ax ; No initrd= option (yet)
+ push es ; Set DS <- real_mode_seg
+ pop ds
+get_next_opt: lodsb
+ and al,al
+ jz 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
+%if IS_PXELINUX
+ cmp eax,'keep' ; Is it "keeppxe"?
+ jne .notkeep
+ cmp dword [si+3],'ppxe'
+ jne .notkeep
+ cmp byte [si+7],' ' ; Must be whitespace or EOS
+ ja .notkeep
+ or byte [cs:KeepPXE],1
+.notkeep:
+%endif
+ push es ; <B> 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
+
+ cmp al,' '
+ jbe .noramdisk
+ mov [cs:InitRDPtr],si
+ jmp .not_initrd
+.noramdisk:
+ xor ax,ax
+ mov [cs:InitRDPtr],ax
+.not_initrd: pop es ; <B> 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,4
+ mov eax,[si-1]
+ mov bx,-1
+ cmp eax,'=nor' ; vga=normal
+ je vc0
+ dec bx ; bx <- -2
+ cmp eax,'=ext' ; vga=ext
+ je vc0
+ dec bx ; 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,4
+ call parseint
+ jc skip_this_opt ; Not an integer
+%if HIGHMEM_SLOP != 0
+ sub ebx,HIGHMEM_SLOP
+%endif
+ mov [cs:MyHighMemSize],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
+;
+prepare_header:
+ mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
+ cmp dword [es:su_header],HEADER_ID ; New setup code ID
+ jne old_kernel ; Old kernel, load low
+ mov ax,[es:su_version]
+ mov [KernelVersion],ax
+ cmp ax,0200h ; Setup code version 2.0
+ jb old_kernel ; Old kernel, load low
+ cmp ax,0201h ; Version 2.01+?
+ jb new_kernel ; If 2.00, skip this step
+ ; Set up the heap (assuming loading high for now)
+ mov word [es:su_heapend],linux_stack-512
+ or byte [es:su_loadflags],80h ; Let the kernel know we care
+ cmp ax,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
+ xor eax,eax
+ mov [es:su_ramdisklen],eax ; No initrd loaded yet
+
+;
+; 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
+
+ ; Cap the ramdisk memory range if appropriate
+ mov eax,[RamdiskMax]
+ cmp eax,[MyHighMemSize]
+ ja .ok
+ mov [MyHighMemSize],eax
+.ok:
+
+any_kernel:
+
+;
+; 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:
+ movzx ax,byte [es:bs_setupsecs] ; Setup sectors
+ and ax,ax
+ jnz .sects_ok
+ mov al,4 ; 0 = 4 setup sectors
+.sects_ok:
+ inc ax ; Including the boot sector
+ mov [SetupSecs],ax
+
+ call dot_pause
+
+;
+; Move the stuff beyond the setup code to high memory at 100000h
+;
+ movzx esi,word [SetupSecs] ; Setup sectors
+ shl si,9 ; Convert to bytes
+ mov ecx,8000h ; 32K
+ sub ecx,esi ; Number of bytes to copy
+ add esi,(real_mode_seg << 4) ; Pointer to source
+ mov edi,100000h ; Copy to address 100000h
+
+ call bcopy ; Transfer to high memory
+
+ pop si ; <A> File pointer
+ and si,si ; EOF already?
+ jz high_load_done
+
+ ; On exit EDI -> where to load the rest
+
+ mov bx,dot_pause
+ or eax,-1 ; Load the whole file
+ mov dx,3 ; Pad to dword
+ call load_high
+
+high_load_done:
+ mov [KernelEnd],edi
+ mov ax,real_mode_seg ; Set to real mode seg
+ mov es,ax
+
+ mov si,dot_msg
+ call cwritestr
+;
+; Some older kernels (1.2 era) would have more than 4 setup sectors, but
+; would not rely on the boot protocol to manage that. These kernels fail
+; if they see protected-mode kernel data after the setup sectors, so
+; clear that memory.
+;
+ mov di,[SetupSecs]
+ shl di,9
+ xor eax,eax
+ mov cx,cmd_line_here
+ sub cx,di
+ shr cx,2
+ rep stosd
+
+;
+; 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:
+ xor eax,eax
+ cmp [InitRDPtr],ax
+ jz .noinitrd
+ call parse_load_initrd
+.noinitrd:
+
+;
+; Abandon hope, ye that enter here! We do no longer permit aborts.
+;
+ call abort_check ; Last chance!!
+
+ mov si,ready_msg
+ call cwritestr
+
+ 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 es,ax
+ mov fs,ax
+
+;
+; 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 command line. Unfortunately, the old kernel boot protocol requires
+; the command line to exist in the 9xxxxh range even if the rest of the
+; setup doesn't.
+;
+setup_command_line:
+ mov dx,[KernelVersion]
+ test byte [LoadFlags],LOAD_HIGH
+ jz .need_high_cmdline
+ cmp dx,0202h ; Support new cmdline protocol?
+ jb .need_high_cmdline
+ ; New cmdline protocol
+ ; Store 32-bit (flat) pointer to command line
+ ; This is the "high" location, since we have bzImage
+ mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here
+ mov word [HeapEnd],linux_stack
+ mov word [fs:su_heapend],linux_stack-512
+ jmp .setup_done
+
+.need_high_cmdline:
+;
+; Copy command line down to fit in high conventional memory
+; -- this happens if we have a zImage kernel or the protocol
+; is less than 2.02.
+;
+ mov si,cmd_line_here
+ mov di,old_cmd_line_here
+ mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
+ mov [fs:kern_cmd_offset],di ; Store pointer
+ mov word [HeapEnd],old_linux_stack
+ mov ax,255 ; Max cmdline limit
+ cmp dx,0201h
+ jb .adjusted
+ ; Protocol 2.01+
+ mov word [fs:su_heapend],old_linux_stack-512
+ jbe .adjusted
+ ; Protocol 2.02+
+ ; Note that the only reason we would end up here is
+ ; because we have a zImage, so we anticipate the move
+ ; to 90000h already...
+ mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
+ mov ax,old_max_cmd_len ; 2.02+ allow a higher limit
+.adjusted:
+
+ mov cx,[CmdLineLen]
+ cmp cx,ax
+ jna .len_ok
+ mov cx,ax ; Truncate the command line
+.len_ok:
+ fs rep movsb
+ stosb ; Final null, note AL=0 already
+ mov [CmdLineEnd],di
+ cmp dx,0200h
+ jb .nomovesize
+ mov [es:su_movesize],di ; Tell the kernel what to move
+.nomovesize:
+.setup_done:
+
+;
+; Time to start setting up move descriptors
+;
+setup_move:
+ mov di,trackbuf
+ xor cx,cx ; Number of descriptors
+
+ mov bx,es ; real_mode_seg
+ mov fs,bx
+ push ds ; We need DS == ES == CS here
+ pop es
+
+ test byte [LoadFlags],LOAD_HIGH
+ jnz .loading_high
+
+; Loading low: move real_mode stuff to 90000h, then move the kernel down
+ mov eax,90000h
+ stosd
+ mov eax,real_mode_seg << 4
+ stosd
+ movzx eax,word [CmdLineEnd]
+ stosd
+ inc cx
+
+ mov eax,10000h ; Target address of low kernel
+ stosd
+ mov eax,100000h ; Where currently loaded
+ stosd
+ neg eax
+ add eax,[KernelEnd]
+ stosd
+ inc cx
+
+ mov bx,9000h ; Revised real mode segment
+
+.loading_high:
+
+ cmp word [InitRDPtr],0 ; Did we have an initrd?
+ je .no_initrd
+
+ mov eax,[fs:su_ramdiskat]
+ stosd
+ mov eax,[InitRDStart]
+ stosd
+ mov eax,[fs:su_ramdisklen]
+ stosd
+ inc cx
+
+.no_initrd:
+ push cx ; Length of descriptor list
+ push word trackbuf
+
+ mov dword [EntryPoint],run_linux_kernel
+ ; BX points to the final real mode segment, and will be loaded
+ ; into DS.
+ jmp replace_bootstrap
+
+
+run_linux_kernel:
+;
+; Set up segment registers and the Linux real-mode stack
+; Note: ds == the real mode segment
+;
+ cli
+ mov ax,ds
+ mov ss,ax
+ mov sp,strict word linux_stack
+ ; Point HeapEnd to the immediate of the instruction above
+HeapEnd equ $-2 ; Self-modifying code! Fun!
+ mov es,ax
+ mov fs,ax
+ mov gs,ax
+
+;
+; 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 ax,020h
+ push ax
+ 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:
+ xor ax,ax
+ cmp word [InitRDPtr],ax ; Old kernel can't have initrd
+ je .load
+ mov si,err_oldkernel
+ jmp abort_load
+.load:
+ mov byte [LoadFlags],al ; Always low
+ mov word [KernelVersion],ax ; Version 0.00
+ jmp any_kernel
+
+;
+; parse_load_initrd
+;
+; Parse an initrd= option and load the initrds. This sets
+; InitRDStart and InitRDEnd with dword padding between; we then
+; do a global memory shuffle to move it to the end of memory.
+;
+; On entry, EDI points to where to start loading.
+;
+parse_load_initrd:
+ push es
+ push ds
+ mov ax,real_mode_seg
+ mov ds,ax
+ push cs
+ pop es ; DS == real_mode_seg, ES == CS
+
+ mov [cs:InitRDStart],edi
+ mov [cs:InitRDEnd],edi
+
+ mov si,[cs:InitRDPtr]
+
+.get_chunk:
+ ; DS:SI points to the start of a name
+
+ mov bx,si
+.find_end:
+ lodsb
+ cmp al,','
+ je .got_end
+ cmp al,' '
+ jbe .got_end
+ jmp .find_end
+
+.got_end:
+ push ax ; Terminating character
+ push si ; Next filename (if any)
+ mov byte [si-1],0 ; Zero-terminate
+ mov si,bx ; Current filename
+
+ push di
+ mov di,InitRD ; Target buffer for mangled name
+ call mangle_name
+ pop di
+ call loadinitrd
+
+ pop si
+ pop ax
+ mov [si-1],al ; Restore ending byte
+
+ cmp al,','
+ je .get_chunk
+
+ ; Compute the initrd target location
+ mov edx,[cs:InitRDEnd]
+ sub edx,[cs:InitRDStart]
+ mov [su_ramdisklen],edx
+ mov eax,[cs:MyHighMemSize]
+ sub eax,edx
+ and ax,0F000h ; Round to a page boundary
+ mov [su_ramdiskat],eax
+
+ pop ds
+ pop es
+ ret
+
+;
+; Load RAM disk into high memory
+;
+; Input: InitRD - set to the mangled name of the initrd
+; EDI - location to load
+; Output: EDI - location for next initrd
+; InitRDEnd - updated
+;
+loadinitrd:
+ push ds
+ push es
+ mov ax,cs ; CS == DS == ES
+ mov ds,ax
+ mov es,ax
+ push edi
+ 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 edi
+ jz .notthere
+
+ 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 dx,3
+ mov bx,dot_pause
+ call load_high
+ mov [InitRDEnd],ebx
+
+ pop es
+ pop ds
+ ret
+
+.notthere:
+ mov si,err_noinitrd
+ call cwritestr
+ mov si,InitRDCName
+ call cwritestr
+ mov si,crlf_msg
+ jmp abort_load
+
+no_high_mem: ; Error routine
+ mov si,err_nohighmem
+ jmp abort_load
+
+ ret
+
+ section .data
+crlfloading_msg db CR, LF
+loading_msg db 'Loading ', 0
+dotdot_msg db '.'
+dot_msg db '.', 0
+ready_msg db 'ready.', CR, LF, 0
+err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
+ db CR, LF, 0
+err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
+
+boot_image db 'BOOT_IMAGE='
+boot_image_len equ $-boot_image
+
+ section .bss
+ alignb 4
+MyHighMemSize resd 1 ; Possibly adjusted highmem size
+RamdiskMax resd 1 ; Highest address for ramdisk
+KernelSize resd 1 ; Size of kernel in bytes
+KernelSects resd 1 ; Size of kernel in sectors
+KernelEnd resd 1 ; Ending address of the kernel image
+InitRDStart resd 1 ; Start of initrd (pre-relocation)
+InitRDEnd resd 1 ; End of initrd (pre-relocation)
+CmdLineLen resw 1 ; Length of command line including null
+CmdLineEnd resw 1 ; End of the command line in real_mode_seg
+SetupSecs resw 1 ; Number of setup sectors (+bootsect)
+InitRDPtr resw 1 ; Pointer to initrd= option in command line
+KernelVersion resw 1 ; Kernel protocol version
+LoadFlags resb 1 ; Loadflags from kernel