blob: 4753bfcd972fa54b0b169cb82e7258cfa91d35ad [file] [log] [blame]
;; -----------------------------------------------------------------------
;;
;; 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.
;;
;; -----------------------------------------------------------------------
;
; This file should be entered with the config file open (for getc)
;
load_config_file:
call parse_config ; Parse configuration file
no_config_file:
call adv_init
;
; Check for an ADV boot-once entry
;
mov dl,ADV_BOOTONCE
call adv_get
jcxz .no_bootonce
.have_bootone:
; We apparently have a boot-once set; clear it and
; then execute the boot-once...
; Save the boot-once data; SI = data, CX = length
mov di,command_line
rep movsb
xor ax,ax
stosb
; Clear the boot-once data from the ADV
xor cx,cx ; Set to zero = delete
call adv_set
jc .err
call adv_write
.err: jmp load_kernel
.no_bootonce:
;
; Check whether or not we are supposed to display the boot prompt.
;
check_for_key:
cmp word [ForcePrompt],0 ; Force prompt?
jnz enter_command
test byte [KbdFlags],5Bh ; Shift Alt Caps Scroll
jz auto_boot ; If neither, default boot
enter_command:
cmp word [NoEscape],0 ; If NOESCAPE, no prompt,
jne auto_boot ; always run default cmd
mov si,boot_prompt
call cwritestr
mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
mov di,command_line
;
; get the very first character -- we can either time
; out, or receive a character press at this time. Some dorky BIOSes stuff
; a return in the buffer on bootup, so wipe the keyboard buffer first.
;
clear_buffer: mov ah,11h ; Check for pending char
int 16h
jz get_char_time
mov ah,10h ; Get char
int 16h
jmp short clear_buffer
; For the first character, both KbdTimeout and
; TotalTimeout apply; after that, only TotalTimeout.
get_char_time:
mov eax,[TotalTimeout]
mov [ThisTotalTo],eax
mov eax,[KbdTimeout]
mov [ThisKbdTo],eax
get_char:
call getchar_timeout
and dword [ThisKbdTo],0 ; For the next time...
and al,al
jz func_key
got_ascii: cmp al,7Fh ; <DEL> == <BS>
je backspace
cmp al,' ' ; ASCII?
jb not_ascii
ja enter_char
cmp di,command_line ; Space must not be first
je short get_char
enter_char: test byte [FuncFlag],1
jnz ctrl_f ; Keystroke after <Ctrl-F>
cmp di,max_cmd_len+command_line ; Check there's space
jnb short get_char
stosb ; Save it
call writechr ; Echo to screen
jmp short get_char
not_ascii:
cmp al,0Dh ; Enter
je command_done
cmp al,'F' & 1Fh ; <Ctrl-F>
je set_func_flag
%if IS_PXELINUX
cmp al,'N' & 1Fh ; <Ctrl-N>
je show_network_info
%endif
cmp al,'U' & 1Fh ; <Ctrl-U>
je kill_command ; Kill input line
cmp al,'V' & 1Fh ; <Ctrl-V>
je print_version
cmp al,'X' & 1Fh ; <Ctrl-X>
je force_text_mode
cmp al,08h ; Backspace
jne get_char
backspace: cmp di,command_line ; Make sure there is anything
je get_char ; to erase
dec di ; Unstore one character
mov si,wipe_char ; and erase it from the screen
call cwritestr
get_char_2:
jmp short get_char
kill_command:
call crlf
jmp enter_command
force_text_mode:
call vgaclearmode
jmp enter_command
set_func_flag:
mov byte [FuncFlag],1
jmp short get_char_2
ctrl_f:
xor ah,ah
mov [FuncFlag],ah
cmp al,'0'
jb get_char_2
je .zero ; <Ctrl-F>0 = F10
or al,20h ; Lower case
cmp al,'9'
jna .digit
cmp al,'a' ; F10-F12 = <Ctrl-F>A, B, C
jb get_char_2
cmp al,'c'
ja get_char_2
sub al,'a'-10
jmp show_help
.zero:
mov al,10
jmp show_help
.digit:
sub al,'1'
jmp show_help
func_key:
; AL = 0 if we get here
xchg al,ah
cmp al,44h ; F10
ja .f11_f12
sub al,3Bh ; F1
jb get_char_2
jmp show_help
.f11_f12:
cmp al,85h ; F11
jb get_char_2
cmp al,86h ; F12
ja get_char_2
sub al,85h-10
show_help: ; AX = func key # (0 = F1, 9 = F10, 11 = F12)
push di ; Save end-of-cmdline pointer
shl ax,FILENAME_MAX_LG2 ; Convert to pointer
add ax,FKeyName
xchg di,ax
cmp byte [di+NULLOFFSET],NULLFILE
je short fk_nofile ; Undefined F-key
call open
jz short fk_nofile ; File not found
call crlf
call get_msg_file
jmp short fk_wrcmd
print_version:
push di ; Command line write pointer
mov si,syslinux_banner
call cwritestr
%ifdef HAVE_BIOSNAME
mov si,[BIOSName]
call cwritestr
%endif
mov si,copyright_str
call cwritestr
; ... fall through ...
; Write the boot prompt and command line again and
; wait for input. Note that this expects the cursor
; to already have been CRLF'd, and that the old value
; of DI (the command line write pointer) is on the stack.
fk_wrcmd:
mov si,boot_prompt
call cwritestr
pop di ; Command line write pointer
push di
mov byte [di],0 ; Null-terminate command line
mov si,command_line
call cwritestr ; Write command line so far
fk_nofile: pop di
jmp get_char
;
; Show network info (in the form of the ipappend strings)
;
%if IS_PXELINUX
show_network_info:
push di ; Command line write pointer
call crlf
mov si,IPAppends ; See comboot.doc
mov cx,numIPAppends
.loop:
lodsw
push si
mov si,ax
call cwritestr
call crlf
pop si
loop .loop
jmp fk_wrcmd
%endif
;
; Jump here to run the default command line
;
auto_boot:
mov si,default_cmd
mov di,command_line
mov cx,(max_cmd_len+4) >> 2
rep movsd
jmp short load_kernel
;
; Jump here when the command line is completed
;
command_done:
call crlf
cmp di,command_line ; Did we just hit return?
je auto_boot
xor al,al ; Store a final null
stosb
load_kernel: ; Load the kernel now
;
; First we need to mangle the kernel name the way DOS would...
;
mov si,command_line
mov di,KernelName
push si
call mangle_name
pop si
;
; Fast-forward to first option (we start over from the beginning, since
; mangle_name doesn't necessarily return a consistent ending state.)
;
clin_non_wsp: lodsb
cmp al,' '
ja clin_non_wsp
clin_is_wsp: and al,al
jz clin_opt_ptr
lodsb
cmp al,' '
jbe clin_is_wsp
clin_opt_ptr: dec si ; Point to first nonblank
mov [CmdOptPtr],si ; Save ptr to first option
;
; If "allowoptions 0", put a null character here in order to ignore any
; user-specified options.
;
mov ax,[AllowOptions]
and ax,ax
jnz clin_opt_ok
mov [si],al
clin_opt_ok:
;
; Now check if it is a "virtual kernel"
;
vk_check:
mov esi,[HighMemSize] ; Start from top of memory
.scan:
cmp esi,[VKernelEnd]
jbe .not_vk
mov di,VKernelBuf
call rllunpack
; ESI updated on return
sub di,cx ; Return to beginning of buf
push si
mov si,KernelName
mov cx,FILENAME_MAX
es repe cmpsb
pop si
je .found
jmp .scan
;
; We *are* using a "virtual kernel"
;
.found:
push es
push word real_mode_seg
pop es
mov di,cmd_line_here
mov si,VKernelBuf+vk_append
mov cx,[VKernelBuf+vk_appendlen]
rep movsb
mov [CmdLinePtr],di ; Where to add rest of cmd
pop es
mov di,KernelName
push di
mov si,VKernelBuf+vk_rname
mov cx,FILENAME_MAX ; We need ECX == CX later
rep movsb
pop di
%if IS_PXELINUX
mov al,[VKernelBuf+vk_ipappend]
mov [IPAppend],al
%endif
xor bx,bx ; Try only one version
mov al, [VKernelBuf+vk_type]
mov [KernelType], al
%if IS_PXELINUX || IS_ISOLINUX
; Is this a "localboot" pseudo-kernel?
%if IS_PXELINUX
cmp byte [VKernelBuf+vk_rname+4], 0
%else
cmp byte [VKernelBuf+vk_rname], 0
%endif
jne get_kernel ; No, it's real, go get it
mov ax, [VKernelBuf+vk_rname+1]
jmp local_boot
%else
jmp get_kernel
%endif
.not_vk:
;
; Not a "virtual kernel" - check that's OK and construct the command line
;
cmp word [AllowImplicit],byte 0
je bad_implicit
push es
push si
push di
mov di,real_mode_seg
mov es,di
mov si,AppendBuf
mov di,cmd_line_here
mov cx,[AppendLen]
rep movsb
mov [CmdLinePtr],di
pop di
pop si
pop es
mov [KernelType], cl ; CL == 0 here
;
; Find the kernel on disk
;
get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension
mov di,KernelName+4*IS_PXELINUX
xor al,al
mov cx,FILENAME_MAX-5 ; Need 4 chars + null
repne scasb ; Scan for final null
jne .no_skip
dec di ; Point to final null
.no_skip: mov [KernelExtPtr],di
mov bx,exten_table
.search_loop: push bx
mov di,KernelName ; Search on disk
call searchdir
pop bx
jnz kernel_good
mov eax,[bx] ; Try a different extension
mov si,[KernelExtPtr]
mov [si],eax
mov byte [si+4],0
add bx,byte 4
cmp bx,exten_table_end
jna .search_loop ; allow == case (final case)
; Fall into bad_kernel
;
; bad_kernel: Kernel image not found
; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
;
bad_implicit:
bad_kernel:
mov cx,[OnerrorLen]
and cx,cx
jnz on_error
.really:
mov si,KernelName
mov di,KernelCName
push di
call unmangle_name ; Get human form
mov si,err_notfound ; Complain about missing kernel
call cwritestr
pop si ; KernelCName
call cwritestr
mov si,crlf_msg
jmp abort_load ; Ask user for clue
;
; on_error: bad kernel, but we have onerror set; CX = OnerrorLen
;
on_error:
mov si,Onerror
mov di,command_line
push si ; <A>
push di ; <B>
push cx ; <C>
push cx ; <D>
push di ; <E>
repe cmpsb
pop di ; <E> di == command_line
pop bx ; <D> bx == [OnerrorLen]
je bad_kernel.really ; Onerror matches command_line already
neg bx ; bx == -[OnerrorLen]
lea cx,[max_cmd_len+bx]
; CX == max_cmd_len-[OnerrorLen]
mov di,command_line+max_cmd_len-1
mov byte [di+1],0 ; Enforce null-termination
lea si,[di+bx]
std
rep movsb ; Make space in command_line
cld
pop cx ; <C> cx == [OnerrorLen]
pop di ; <B> di == command_line
pop si ; <A> si == Onerror
rep movsb
jmp load_kernel
;
; kernel_corrupt: Called if the kernel file does not seem healthy
;
kernel_corrupt: mov si,err_notkernel
jmp abort_load
;
; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts
; which can be adjusted by the caller based on the corresponding
; master variables; on return they're updated.
;
; This cheats. If we say "no timeout" we actually get a timeout of
; 7.5 years.
;
getchar_timeout:
call vgashowcursor
RESET_IDLE
.loop:
push word [BIOS_timer]
call pollchar
jnz .got_char
pop ax
cmp ax,[BIOS_timer] ; Has the timer advanced?
je .loop
DO_IDLE
dec dword [ThisKbdTo]
jz .timeout
dec dword [ThisTotalTo]
jnz .loop
.timeout:
; Timeout!!!!
pop cx ; Discard return address
call vgahidecursor
mov si,Ontimeout ; Copy ontimeout command
mov di,command_line
mov cx,[OntimeoutLen] ; if we have one...
rep movsb
jmp command_done
.got_char:
pop cx ; Discard
call getchar
call vgahidecursor
ret
;
; This is it! We have a name (and location on the disk)... let's load
; that sucker!! First we have to decide what kind of file this is; base
; that decision on the file extension. The following extensions are
; recognized; case insensitive:
;
; .com - COMBOOT image
; .cbt - COMBOOT image
; .c32 - COM32 image
; .bs - Boot sector
; .0 - PXE bootstrap program (PXELINUX only)
; .bin - Boot sector
; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only)
; .img - Floppy image (ISOLINUX only)
;
; Anything else is assumed to be a Linux kernel.
;
section .bss
alignb 4
Kernel_EAX resd 1
Kernel_SI resw 1
section .text
kernel_good_saved:
; Alternate entry point for which the return from
; searchdir is stored in memory. This is used for
; COMBOOT function INT 22h, AX=0016h.
mov si,[Kernel_SI]
mov eax,[Kernel_EAX]
mov dx,[Kernel_EAX+2]
kernel_good:
pusha
mov si,KernelName
mov di,KernelCName
call unmangle_name
sub di,KernelCName
mov [KernelCNameLen],di
popa
push di
push ax
mov di,KernelName+4*IS_PXELINUX
xor al,al
mov cx,FILENAME_MAX
repne scasb
jne .one_step
dec di
.one_step: mov ecx,[di-4] ; 4 bytes before end
pop ax
pop di
;
; At this point, DX:AX contains the size of the kernel, SI contains
; the file handle/cluster pointer, and ECX contains the extension (if any.)
;
movzx di,byte [KernelType]
add di,di
jmp [kerneltype_table+di]
is_unknown_filetype:
or ecx,20202000h ; Force lower case (except dot)
cmp ecx,'.com'
je is_comboot_image
cmp ecx,'.cbt'
je is_comboot_image
cmp ecx,'.c32'
je is_com32_image
%if IS_ISOLINUX
cmp ecx,'.img'
je is_disk_image
%endif
cmp ecx,'.bss'
je is_bss_sector
cmp ecx,'.bin'
je is_bootsector
shr ecx,8
cmp ecx,'.bs'
je is_bootsector
shr ecx,8
cmp cx,'.0'
je is_bootsector
; Otherwise Linux kernel
jmp is_linux_kernel
is_config_file:
pusha
mov si,KernelCName ; Save the config file name, for posterity
mov di,ConfigName
call strcpy
popa
call openfd
call reset_config
jmp load_config_file
; This is an image type we can't deal with
is_bad_image:
mov si,err_badimage
call cwritestr
jmp enter_command
%if IS_SYSLINUX || IS_MDSLINUX
; ok
%else
is_bss_sector equ is_bad_image
%endif
%if IS_ISOLINUX
; ok
%else
is_disk_image equ is_bad_image
%endif
section .data
boot_prompt db 'boot: ', 0
wipe_char db BS, ' ', BS, 0
err_badimage db 'Invalid image type for this media type!', CR, LF, 0
err_notfound db 'Could not find kernel image: ',0
err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
align 2, db 0
kerneltype_table:
dw is_unknown_filetype ; VK_KERNEL
dw is_linux_kernel ; VK_LINUX
dw is_bootsector ; VK_BOOT
dw is_bss_sector ; VK_BSS
dw is_bootsector ; VK_PXE
dw is_disk_image ; VK_FDIMAGE
dw is_comboot_image ; VK_COMBOOT
dw is_com32_image ; VK_COM32
dw is_config_file ; VK_CONFIG
section .bss1
alignb 4
ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
KernelExtPtr resw 1 ; During search, final null pointer
CmdOptPtr resw 1 ; Pointer to first option on cmd line
KbdFlags resb 1 ; Check for keyboard escapes
FuncFlag resb 1 ; Escape sequences received from keyboard
KernelType resb 1 ; Kernel type, from vkernel, if known
section .text
;
; Linux kernel loading code is common.
;
%include "runkernel.inc"
;
; COMBOOT-loading code
;
%include "comboot.inc"
%include "com32.inc"
%include "cmdline.inc"
;
; Boot sector loading code
;
%include "bootsect.inc"
;
; Abort loading code
;
%include "abort.inc"
;
; Hardware cleanup common code
;
%include "cleanup.inc"