;; ----------------------------------------------------------------------- ;; ;; 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 ; 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 ; == 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 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 ; je set_func_flag %if IS_PXELINUX cmp al,'N' & 1Fh ; je show_network_info %endif cmp al,'U' & 1Fh ; je kill_command ; Kill input line cmp al,'V' & 1Fh ; je print_version cmp al,'X' & 1Fh ; 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 ; 0 = F10 or al,20h ; Lower case cmp al,'9' jna .digit cmp al,'a' ; F10-F12 = 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 searchdir jz short fk_nofile ; File not found push si call crlf pop si 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: xor si,si ; Beginning of vk_seg .scan: cmp si,[VKernelBytes] jae .not_vk push ds push word vk_seg pop ds mov di,VKernelBuf call rllunpack pop ds ; SI 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 ; push di ; push cx ; push cx ; push di ; repe cmpsb pop di ; di == command_line pop bx ; 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 ; cx == [OnerrorLen] pop di ; di == command_line pop si ; 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 .bss 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"