Use the ADV to support boot-once
[profile/ivi/syslinux.git] / ui.inc
1 ;; -----------------------------------------------------------------------
2 ;;
3 ;;   Copyright 1994-2007 H. Peter Anvin - All Rights Reserved
4 ;;
5 ;;   This program is free software; you can redistribute it and/or modify
6 ;;   it under the terms of the GNU General Public License as published by
7 ;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 ;;   Boston MA 02111-1307, USA; either version 2 of the License, or
9 ;;   (at your option) any later version; incorporated herein by reference.
10 ;;
11 ;; -----------------------------------------------------------------------
12
13 ;
14 ; This file should be entered with the config file open (for getc)
15 ;
16 load_config_file:
17                 call parse_config               ; Parse configuration file
18 no_config_file:
19
20                 call adv_init
21 ;
22 ; Check for an ADV boot-once entry
23 ;
24                 mov dl,ADV_BOOTONCE
25                 call adv_get
26                 jcxz .no_bootonce
27
28 .have_bootone:
29                 ; We apparently have a boot-once set; clear it and
30                 ; then execute the boot-once...
31
32                 ; Save the boot-once data; SI = data, CX = length
33                 mov di,command_line
34                 rep movsb
35                 xor ax,ax
36                 stosb
37
38                 ; Clear the boot-once data from the ADV
39                 xor cx,cx                       ; Set to zero = delete
40                 call adv_set
41                 jc .err
42                 call adv_write
43 .err:           jmp load_kernel
44
45 .no_bootonce:
46
47 ;
48 ; Check whether or not we are supposed to display the boot prompt.
49 ;
50 check_for_key:
51                 cmp word [ForcePrompt],0        ; Force prompt?
52                 jnz enter_command
53                 test byte [KbdFlags],5Bh        ; Shift Alt Caps Scroll
54                 jz auto_boot                    ; If neither, default boot
55
56 enter_command:
57                 cmp word [NoEscape],0           ; If NOESCAPE, no prompt,
58                 jne auto_boot                   ; always run default cmd
59
60                 mov si,boot_prompt
61                 call cwritestr
62
63                 mov byte [FuncFlag],0           ; <Ctrl-F> not pressed
64                 mov di,command_line
65
66 ;
67 ; get the very first character -- we can either time
68 ; out, or receive a character press at this time.  Some dorky BIOSes stuff
69 ; a return in the buffer on bootup, so wipe the keyboard buffer first.
70 ;
71 clear_buffer:   mov ah,11h                      ; Check for pending char
72                 int 16h
73                 jz get_char_time
74                 mov ah,10h                      ; Get char
75                 int 16h
76                 jmp short clear_buffer
77
78                 ; For the first character, both KbdTimeout and
79                 ; TotalTimeout apply; after that, only TotalTimeout.
80
81 get_char_time:
82                 mov eax,[TotalTimeout]
83                 mov [ThisTotalTo],eax
84                 mov eax,[KbdTimeout]
85                 mov [ThisKbdTo],eax
86
87 get_char:
88                 call getchar_timeout
89                 and dword [ThisKbdTo],0         ; For the next time...
90
91                 and al,al
92                 jz func_key
93
94 got_ascii:      cmp al,7Fh                      ; <DEL> == <BS>
95                 je backspace
96                 cmp al,' '                      ; ASCII?
97                 jb not_ascii
98                 ja enter_char
99                 cmp di,command_line             ; Space must not be first
100                 je short get_char
101 enter_char:     test byte [FuncFlag],1
102                 jnz ctrl_f                      ; Keystroke after <Ctrl-F>
103                 cmp di,max_cmd_len+command_line ; Check there's space
104                 jnb short get_char
105                 stosb                           ; Save it
106                 call writechr                   ; Echo to screen
107                 jmp short get_char
108 not_ascii:
109                 cmp al,0Dh                      ; Enter
110                 je command_done
111                 cmp al,'F' & 1Fh                ; <Ctrl-F>
112                 je set_func_flag
113 %if IS_PXELINUX
114                 cmp al,'N' & 1Fh                ; <Ctrl-N>
115                 je show_network_info
116 %endif
117                 cmp al,'U' & 1Fh                ; <Ctrl-U>
118                 je kill_command                 ; Kill input line
119                 cmp al,'V' & 1Fh                ; <Ctrl-V>
120                 je print_version
121                 cmp al,'X' & 1Fh                ; <Ctrl-X>
122                 je force_text_mode
123                 cmp al,08h                      ; Backspace
124                 jne get_char
125 backspace:      cmp di,command_line             ; Make sure there is anything
126                 je get_char                     ; to erase
127                 dec di                          ; Unstore one character
128                 mov si,wipe_char                ; and erase it from the screen
129                 call cwritestr
130 get_char_2:
131                 jmp short get_char
132
133 kill_command:
134                 call crlf
135                 jmp enter_command
136
137 force_text_mode:
138                 call vgaclearmode
139                 jmp enter_command
140
141 set_func_flag:
142                 mov byte [FuncFlag],1
143                 jmp short get_char_2
144
145 ctrl_f:
146                 xor ah,ah
147                 mov [FuncFlag],ah
148                 cmp al,'0'
149                 jb get_char_2
150                 je .zero                        ; <Ctrl-F>0 = F10
151                 or al,20h                       ; Lower case
152                 cmp al,'9'
153                 jna .digit
154                 cmp al,'a'                      ; F10-F12 = <Ctrl-F>A, B, C
155                 jb get_char_2
156                 cmp al,'c'
157                 ja get_char_2
158                 sub al,'a'-10
159                 jmp show_help
160 .zero:
161                 mov al,10
162                 jmp show_help
163 .digit:
164                 sub al,'1'
165                 jmp show_help
166
167 func_key:
168                 ; AL = 0 if we get here
169                 xchg al,ah
170                 cmp al,44h                      ; F10
171                 ja .f11_f12
172                 sub al,3Bh                      ; F1
173                 jb get_char_2
174                 jmp show_help
175 .f11_f12:
176                 cmp al,85h                      ; F11
177                 jb get_char_2
178                 cmp al,86h                      ; F12
179                 ja get_char_2
180                 sub al,85h-10
181
182 show_help:      ; AX = func key # (0 = F1, 9 = F10, 11 = F12)
183                 push di                         ; Save end-of-cmdline pointer
184                 shl ax,FILENAME_MAX_LG2         ; Convert to pointer
185                 add ax,FKeyName
186                 xchg di,ax
187                 cmp byte [di+NULLOFFSET],NULLFILE
188                 je short fk_nofile              ; Undefined F-key
189                 call searchdir
190                 jz short fk_nofile              ; File not found
191                 push si
192                 call crlf
193                 pop si
194                 call get_msg_file
195                 jmp short fk_wrcmd
196
197 print_version:
198                 push di                         ; Command line write pointer
199                 mov si,syslinux_banner
200                 call cwritestr
201 %ifdef HAVE_BIOSNAME
202                 mov si,[BIOSName]
203                 call cwritestr
204 %endif
205                 mov si,copyright_str
206                 call cwritestr
207
208                 ; ... fall through ...
209
210                 ; Write the boot prompt and command line again and
211                 ; wait for input.  Note that this expects the cursor
212                 ; to already have been CRLF'd, and that the old value
213                 ; of DI (the command line write pointer) is on the stack.
214 fk_wrcmd:
215                 mov si,boot_prompt
216                 call cwritestr
217                 pop di                          ; Command line write pointer
218                 push di
219                 mov byte [di],0                 ; Null-terminate command line
220                 mov si,command_line
221                 call cwritestr                  ; Write command line so far
222 fk_nofile:      pop di
223                 jmp get_char
224
225 ;
226 ; Show network info (in the form of the ipappend strings)
227 ;
228 %if IS_PXELINUX
229 show_network_info:
230                 push di                         ; Command line write pointer
231                 call crlf
232                 mov si,IPAppends                ; See comboot.doc
233                 mov cx,numIPAppends
234 .loop:
235                 lodsw
236                 push si
237                 mov si,ax
238                 call cwritestr
239                 call crlf
240                 pop si
241                 loop .loop
242                 jmp fk_wrcmd
243 %endif
244
245 ;
246 ; Jump here to run the default command line
247 ;
248 auto_boot:
249                 mov si,default_cmd
250                 mov di,command_line
251                 mov cx,(max_cmd_len+4) >> 2
252                 rep movsd
253                 jmp short load_kernel
254
255 ;
256 ; Jump here when the command line is completed
257 ;
258 command_done:
259                 call crlf
260                 cmp di,command_line             ; Did we just hit return?
261                 je auto_boot
262                 xor al,al                       ; Store a final null
263                 stosb
264
265 load_kernel:                                    ; Load the kernel now
266 ;
267 ; First we need to mangle the kernel name the way DOS would...
268 ;
269                 mov si,command_line
270                 mov di,KernelName
271                 push si
272                 call mangle_name
273                 pop si
274 ;
275 ; Fast-forward to first option (we start over from the beginning, since
276 ; mangle_name doesn't necessarily return a consistent ending state.)
277 ;
278 clin_non_wsp:   lodsb
279                 cmp al,' '
280                 ja clin_non_wsp
281 clin_is_wsp:    and al,al
282                 jz clin_opt_ptr
283                 lodsb
284                 cmp al,' '
285                 jbe clin_is_wsp
286 clin_opt_ptr:   dec si                          ; Point to first nonblank
287                 mov [CmdOptPtr],si              ; Save ptr to first option
288 ;
289 ; If "allowoptions 0", put a null character here in order to ignore any
290 ; user-specified options.
291 ;
292                 mov ax,[AllowOptions]
293                 and ax,ax
294                 jnz clin_opt_ok
295                 mov [si],al
296 clin_opt_ok:
297
298 ;
299 ; Now check if it is a "virtual kernel"
300 ;
301 vk_check:
302                 xor si,si                       ; Beginning of vk_seg
303 .scan:
304                 cmp si,[VKernelBytes]
305                 jae .not_vk
306
307                 push ds
308                 push word vk_seg
309                 pop ds
310
311                 mov di,VKernelBuf
312                 call rllunpack
313                 pop ds
314                 ; SI updated on return
315
316                 sub di,cx                       ; Return to beginning of buf
317                 push si
318                 mov si,KernelName
319                 mov cx,FILENAME_MAX
320                 es repe cmpsb
321                 pop si
322                 je .found
323                 jmp .scan
324
325 ;
326 ; We *are* using a "virtual kernel"
327 ;
328 .found:
329                 push es
330                 push word real_mode_seg
331                 pop es
332                 mov di,cmd_line_here
333                 mov si,VKernelBuf+vk_append
334                 mov cx,[VKernelBuf+vk_appendlen]
335                 rep movsb
336                 mov [CmdLinePtr],di             ; Where to add rest of cmd
337                 pop es
338                 mov di,KernelName
339                 push di
340                 mov si,VKernelBuf+vk_rname
341                 mov cx,FILENAME_MAX             ; We need ECX == CX later
342                 rep movsb
343                 pop di
344 %if IS_PXELINUX
345                 mov al,[VKernelBuf+vk_ipappend]
346                 mov [IPAppend],al
347 %endif
348                 xor bx,bx                       ; Try only one version
349
350                 mov al, [VKernelBuf+vk_type]
351                 mov [KernelType], al
352
353 %if IS_PXELINUX || IS_ISOLINUX
354                 ; Is this a "localboot" pseudo-kernel?
355 %if IS_PXELINUX
356                 cmp byte [VKernelBuf+vk_rname+4], 0
357 %else
358                 cmp byte [VKernelBuf+vk_rname], 0
359 %endif
360                 jne get_kernel          ; No, it's real, go get it
361
362                 mov ax, [VKernelBuf+vk_rname+1]
363                 jmp local_boot
364 %else
365                 jmp get_kernel
366 %endif
367
368 .not_vk:
369
370 ;
371 ; Not a "virtual kernel" - check that's OK and construct the command line
372 ;
373                 cmp word [AllowImplicit],byte 0
374                 je bad_implicit
375                 push es
376                 push si
377                 push di
378                 mov di,real_mode_seg
379                 mov es,di
380                 mov si,AppendBuf
381                 mov di,cmd_line_here
382                 mov cx,[AppendLen]
383                 rep movsb
384                 mov [CmdLinePtr],di
385                 pop di
386                 pop si
387                 pop es
388
389                 mov [KernelType], cl            ; CL == 0 here
390
391 ;
392 ; Find the kernel on disk
393 ;
394 get_kernel:     mov byte [KernelName+FILENAME_MAX],0    ; Zero-terminate filename/extension
395                 mov di,KernelName+4*IS_PXELINUX
396                 xor al,al
397                 mov cx,FILENAME_MAX-5           ; Need 4 chars + null
398                 repne scasb                     ; Scan for final null
399                 jne .no_skip
400                 dec di                          ; Point to final null
401 .no_skip:       mov [KernelExtPtr],di
402                 mov bx,exten_table
403 .search_loop:   push bx
404                 mov di,KernelName               ; Search on disk
405                 call searchdir
406                 pop bx
407                 jnz kernel_good
408                 mov eax,[bx]                    ; Try a different extension
409                 mov si,[KernelExtPtr]
410                 mov [si],eax
411                 mov byte [si+4],0
412                 add bx,byte 4
413                 cmp bx,exten_table_end
414                 jna .search_loop                ; allow == case (final case)
415                 ; Fall into bad_kernel
416 ;
417 ; bad_kernel: Kernel image not found
418 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
419 ;
420 bad_implicit:
421 bad_kernel:
422                 mov cx,[OnerrorLen]
423                 and cx,cx
424                 jnz on_error
425 .really:
426                 mov si,KernelName
427                 mov di,KernelCName
428                 push di
429                 call unmangle_name              ; Get human form
430                 mov si,err_notfound             ; Complain about missing kernel
431                 call cwritestr
432                 pop si                          ; KernelCName
433                 call cwritestr
434                 mov si,crlf_msg
435                 jmp abort_load                  ; Ask user for clue
436
437 ;
438 ; on_error: bad kernel, but we have onerror set; CX = OnerrorLen
439 ;
440 on_error:
441                 mov si,Onerror
442                 mov di,command_line
443                 push si                         ; <A>
444                 push di                         ; <B>
445                 push cx                         ; <C>
446                 push cx                         ; <D>
447                 push di                         ; <E>
448                 repe cmpsb
449                 pop di                          ; <E> di == command_line
450                 pop bx                          ; <D> bx == [OnerrorLen]
451                 je bad_kernel.really            ; Onerror matches command_line already
452                 neg bx                          ; bx == -[OnerrorLen]
453                 lea cx,[max_cmd_len+bx]
454                 ; CX == max_cmd_len-[OnerrorLen]
455                 mov di,command_line+max_cmd_len-1
456                 mov byte [di+1],0               ; Enforce null-termination
457                 lea si,[di+bx]
458                 std
459                 rep movsb                       ; Make space in command_line
460                 cld
461                 pop cx                          ; <C> cx == [OnerrorLen]
462                 pop di                          ; <B> di == command_line
463                 pop si                          ; <A> si  == Onerror
464                 rep movsb
465                 jmp load_kernel
466
467 ;
468 ; kernel_corrupt: Called if the kernel file does not seem healthy
469 ;
470 kernel_corrupt: mov si,err_notkernel
471                 jmp abort_load
472
473 ;
474 ; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts
475 ; which can be adjusted by the caller based on the corresponding
476 ; master variables; on return they're updated.
477 ;
478 ; This cheats.  If we say "no timeout" we actually get a timeout of
479 ; 7.5 years.
480 ;
481 getchar_timeout:
482                 call vgashowcursor
483                 RESET_IDLE
484
485 .loop:
486                 push word [BIOS_timer]
487                 call pollchar
488                 jnz .got_char
489                 pop ax
490                 cmp ax,[BIOS_timer]             ; Has the timer advanced?
491                 je .loop
492                 DO_IDLE
493
494                 dec dword [ThisKbdTo]
495                 jz .timeout
496                 dec dword [ThisTotalTo]
497                 jnz .loop
498
499 .timeout:
500                 ; Timeout!!!!
501                 pop cx                          ; Discard return address
502                 call vgahidecursor
503                 mov si,Ontimeout                ; Copy ontimeout command
504                 mov di,command_line
505                 mov cx,[OntimeoutLen]           ; if we have one...
506                 rep movsb
507                 jmp command_done
508
509 .got_char:
510                 pop cx                          ; Discard
511                 call getchar
512                 call vgahidecursor
513                 ret
514
515 ;
516 ; This is it!  We have a name (and location on the disk)... let's load
517 ; that sucker!!  First we have to decide what kind of file this is; base
518 ; that decision on the file extension.  The following extensions are
519 ; recognized; case insensitive:
520 ;
521 ; .com  - COMBOOT image
522 ; .cbt  - COMBOOT image
523 ; .c32  - COM32 image
524 ; .bs   - Boot sector
525 ; .0    - PXE bootstrap program (PXELINUX only)
526 ; .bin  - Boot sector
527 ; .bss  - Boot sector, but transfer over DOS superblock (SYSLINUX only)
528 ; .img  - Floppy image (ISOLINUX only)
529 ;
530 ; Anything else is assumed to be a Linux kernel.
531 ;
532                 section .bss
533                 alignb 4
534 Kernel_EAX      resd 1
535 Kernel_SI       resw 1
536
537                 section .text
538 kernel_good_saved:
539                 ; Alternate entry point for which the return from
540                 ; searchdir is stored in memory.  This is used for
541                 ; COMBOOT function INT 22h, AX=0016h.
542                 mov si,[Kernel_SI]
543                 mov eax,[Kernel_EAX]
544                 mov dx,[Kernel_EAX+2]
545
546 kernel_good:
547                 pusha
548                 mov si,KernelName
549                 mov di,KernelCName
550                 call unmangle_name
551                 sub di,KernelCName
552                 mov [KernelCNameLen],di
553                 popa
554
555                 push di
556                 push ax
557                 mov di,KernelName+4*IS_PXELINUX
558                 xor al,al
559                 mov cx,FILENAME_MAX
560                 repne scasb
561                 jne .one_step
562                 dec di
563 .one_step:      mov ecx,[di-4]                  ; 4 bytes before end
564                 pop ax
565                 pop di
566
567 ;
568 ; At this point, DX:AX contains the size of the kernel, SI contains
569 ; the file handle/cluster pointer, and ECX contains the extension (if any.)
570 ;
571                 movzx di,byte [KernelType]
572                 add di,di
573                 jmp [kerneltype_table+di]
574
575 is_unknown_filetype:
576                 or ecx,20202000h                ; Force lower case (except dot)
577
578                 cmp ecx,'.com'
579                 je is_comboot_image
580                 cmp ecx,'.cbt'
581                 je is_comboot_image
582                 cmp ecx,'.c32'
583                 je is_com32_image
584 %if IS_ISOLINUX
585                 cmp ecx,'.img'
586                 je is_disk_image
587 %endif
588                 cmp ecx,'.bss'
589                 je is_bss_sector
590                 cmp ecx,'.bin'
591                 je is_bootsector
592                 shr ecx,8
593                 cmp ecx,'.bs'
594                 je is_bootsector
595                 shr ecx,8
596                 cmp cx,'.0'
597                 je is_bootsector
598
599                 ; Otherwise Linux kernel
600                 jmp is_linux_kernel
601
602 is_config_file:
603                 pusha
604                 mov si,KernelCName              ; Save the config file name, for posterity
605                 mov di,ConfigName
606                 call strcpy
607                 popa
608                 call openfd
609                 call reset_config
610                 jmp load_config_file
611
612 ; This is an image type we can't deal with
613 is_bad_image:
614                 mov si,err_badimage
615                 call cwritestr
616                 jmp enter_command
617
618 %if IS_SYSLINUX || IS_MDSLINUX
619                 ; ok
620 %else
621 is_bss_sector   equ is_bad_image
622 %endif
623 %if IS_ISOLINUX
624                 ; ok
625 %else
626 is_disk_image   equ is_bad_image
627 %endif
628
629                 section .data
630 boot_prompt     db 'boot: ', 0
631 wipe_char       db BS, ' ', BS, 0
632 err_badimage    db 'Invalid image type for this media type!', CR, LF, 0
633 err_notfound    db 'Could not find kernel image: ',0
634 err_notkernel   db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
635
636
637                 align 2, db 0
638 kerneltype_table:
639                 dw is_unknown_filetype  ; VK_KERNEL
640                 dw is_linux_kernel      ; VK_LINUX
641                 dw is_bootsector        ; VK_BOOT
642                 dw is_bss_sector        ; VK_BSS
643                 dw is_bootsector        ; VK_PXE
644                 dw is_disk_image        ; VK_FDIMAGE
645                 dw is_comboot_image     ; VK_COMBOOT
646                 dw is_com32_image       ; VK_COM32
647                 dw is_config_file       ; VK_CONFIG
648
649                 section .bss
650                 alignb 4
651 ThisKbdTo       resd 1                  ; Temporary holder for KbdTimeout
652 ThisTotalTo     resd 1                  ; Temporary holder for TotalTimeout
653 KernelExtPtr    resw 1                  ; During search, final null pointer
654 CmdOptPtr       resw 1                  ; Pointer to first option on cmd line
655 KbdFlags        resb 1                  ; Check for keyboard escapes
656 FuncFlag        resb 1                  ; Escape sequences received from keyboard
657 KernelType      resb 1                  ; Kernel type, from vkernel, if known
658
659                 section .text
660 ;
661 ; Linux kernel loading code is common.
662 ;
663 %include "runkernel.inc"
664
665 ;
666 ; COMBOOT-loading code
667 ;
668 %include "comboot.inc"
669 %include "com32.inc"
670 %include "cmdline.inc"
671
672 ;
673 ; Boot sector loading code
674 ;
675 %include "bootsect.inc"
676
677 ;
678 ; Abort loading code
679 ;
680 %include "abort.inc"
681
682 ;
683 ; Hardware cleanup common code
684 ;
685 %include "cleanup.inc"