More fixes to the extlinux installer; change back to writable types
[profile/ivi/syslinux.git] / isolinux.asm
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id$
3 ; ****************************************************************************
4 ;
5 ;  isolinux.asm
6 ;
7 ;  A program to boot Linux kernels off a CD-ROM using the El Torito
8 ;  boot standard in "no emulation" mode, making the entire filesystem
9 ;  available.  It is based on the SYSLINUX boot loader for MS-DOS
10 ;  floppies.
11 ;
12 ;   Copyright (C) 1994-2004  H. Peter Anvin
13 ;
14 ;  This program is free software; you can redistribute it and/or modify
15 ;  it under the terms of the GNU General Public License as published by
16 ;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
17 ;  Boston MA 02111-1307, USA; either version 2 of the License, or
18 ;  (at your option) any later version; incorporated herein by reference.
19 ;
20 ; ****************************************************************************
21
22 %define IS_ISOLINUX 1
23 %include "macros.inc"
24 %include "config.inc"
25 %include "kernel.inc"
26 %include "bios.inc"
27 %include "tracers.inc"
28
29 ;
30 ; Some semi-configurable constants... change on your own risk.
31 ;
32 my_id           equ isolinux_id
33 FILENAME_MAX_LG2 equ 8                  ; log2(Max filename size Including final null)
34 FILENAME_MAX    equ (1 << FILENAME_MAX_LG2)
35 NULLFILE        equ 0                   ; Zero byte == null file name
36 retry_count     equ 6                   ; How patient are we with the BIOS?
37 %assign HIGHMEM_SLOP 128*1024           ; Avoid this much memory near the top
38 MAX_OPEN_LG2    equ 6                   ; log2(Max number of open files)
39 MAX_OPEN        equ (1 << MAX_OPEN_LG2)
40 SECTOR_SHIFT    equ 11                  ; 2048 bytes/sector (El Torito requirement)
41 SECTOR_SIZE     equ (1 << SECTOR_SHIFT)
42
43 ;
44 ; This is what we need to do when idle
45 ;
46 %macro  RESET_IDLE 0
47         ; Nothing
48 %endmacro
49 %macro  DO_IDLE 0
50         ; Nothing
51 %endmacro
52
53 ;
54 ; The following structure is used for "virtual kernels"; i.e. LILO-style
55 ; option labels.  The options we permit here are `kernel' and `append
56 ; Since there is no room in the bottom 64K for all of these, we
57 ; stick them at vk_seg:0000 and copy them down before we need them.
58 ;
59                 struc vkernel
60 vk_vname:       resb FILENAME_MAX       ; Virtual name **MUST BE FIRST!**
61 vk_rname:       resb FILENAME_MAX       ; Real name
62 vk_appendlen:   resw 1
63                 alignb 4
64 vk_append:      resb max_cmd_len+1      ; Command line
65                 alignb 4
66 vk_end:         equ $                   ; Should be <= vk_size
67                 endstruc
68
69 ;
70 ; Segment assignments in the bottom 640K
71 ; 0000h - main code/data segment (and BIOS segment)
72 ;
73 real_mode_seg   equ 3000h
74 vk_seg          equ 2000h               ; Virtual kernels
75 xfer_buf_seg    equ 1000h               ; Bounce buffer for I/O to high mem
76 comboot_seg     equ real_mode_seg       ; COMBOOT image loading zone
77
78 ;
79 ; File structure.  This holds the information for each currently open file.
80 ;
81                 struc open_file_t
82 file_sector     resd 1                  ; Sector pointer (0 = structure free)
83 file_left       resd 1                  ; Number of sectors left
84                 endstruc
85
86 %ifndef DEPEND
87 %if (open_file_t_size & (open_file_t_size-1))
88 %error "open_file_t is not a power of 2"
89 %endif
90 %endif
91
92                 struc dir_t
93 dir_lba         resd 1                  ; Directory start (LBA)
94 dir_len         resd 1                  ; Length in bytes
95 dir_clust       resd 1                  ; Length in clusters
96                 endstruc
97
98 ; ---------------------------------------------------------------------------
99 ;   BEGIN CODE
100 ; ---------------------------------------------------------------------------
101
102 ;
103 ; Memory below this point is reserved for the BIOS and the MBR
104 ;
105 BSS_START       equ 1000h
106                 section .bss start=BSS_START
107 trackbufsize    equ 8192
108 trackbuf        resb trackbufsize       ; Track buffer goes here
109 getcbuf         resb trackbufsize
110 ;               ends at 5000h
111
112                 alignb 4
113 ISOFileName     resb 64                 ; ISO filename canonicalization buffer
114 ISOFileNameEnd  equ $
115 CurDir          resb dir_t_size         ; Current directory
116 RootDir         resb dir_t_size         ; Root directory
117 FirstSecSum     resd 1                  ; Checksum of bytes 64-2048
118 ImageDwords     resd 1                  ; isolinux.bin size, dwords
119 InitStack       resd 1                  ; Initial stack pointer (SS:SP)
120 DiskSys         resw 1                  ; Last INT 13h call
121 ImageSectors    resw 1                  ; isolinux.bin size, sectors
122 DiskError       resb 1                  ; Error code for disk I/O
123 DriveNo         resb 1                  ; CD-ROM BIOS drive number
124 ISOFlags        resb 1                  ; Flags for ISO directory search
125 RetryCount      resb 1                  ; Used for disk access retries
126
127                 alignb open_file_t_size
128 Files           resb MAX_OPEN*open_file_t_size
129
130 ;
131 ; Constants for the xfer_buf_seg
132 ;
133 ; The xfer_buf_seg is also used to store message file buffers.  We
134 ; need two trackbuffers (text and graphics), plus a work buffer
135 ; for the graphics decompressor.
136 ;
137 xbs_textbuf     equ 0                   ; Also hard-coded, do not change
138 xbs_vgabuf      equ trackbufsize
139 xbs_vgatmpbuf   equ 2*trackbufsize
140
141                 section .text
142                 org 7C00h
143 ;;
144 ;; Primary entry point.  Because BIOSes are buggy, we only load the first
145 ;; CD-ROM sector (2K) of the file, so the number one priority is actually
146 ;; loading the rest.
147 ;;
148 bootsec         equ $
149
150 StackBuf        equ $-44
151
152 _start:         ; Far jump makes sure we canonicalize the address
153                 cli
154                 jmp 0:_start1
155                 times 8-($-$$) nop              ; Pad to file offset 8
156
157                 ; This table hopefully gets filled in by mkisofs using the
158                 ; -boot-info-table option.  If not, the values in this
159                 ; table are default values that we can use to get us what
160                 ; we need, at least under a certain set of assumptions.
161 bi_pvd:         dd 16                           ; LBA of primary volume descriptor
162 bi_file:        dd 0                            ; LBA of boot file
163 bi_length:      dd 0xdeadbeef                   ; Length of boot file
164 bi_csum:        dd 0xdeadbeef                   ; Checksum of boot file
165 bi_reserved:    times 10 dd 0xdeadbeef          ; Reserved
166
167 _start1:        mov [cs:InitStack],sp           ; Save initial stack pointer
168                 mov [cs:InitStack+2],ss
169                 xor ax,ax
170                 mov ss,ax
171                 mov sp,StackBuf                 ; Set up stack
172                 mov ds,ax
173                 mov es,ax
174                 mov fs,ax
175                 mov gs,ax
176                 sti
177
178                 cld
179                 ; Show signs of life
180                 mov si,syslinux_banner
181                 call writestr
182 %ifdef DEBUG_MESSAGES
183                 mov si,copyright_str
184                 call writestr
185 %endif
186
187                 ;
188                 ; Before modifying any memory, get the checksum of bytes
189                 ; 64-2048
190                 ;
191 initial_csum:   xor edi,edi
192                 mov si,_start1
193                 mov cx,(SECTOR_SIZE-64) >> 2
194 .loop:          lodsd
195                 add edi,eax
196                 loop .loop
197                 mov [FirstSecSum],edi
198
199                 mov [DriveNo],dl
200 %ifdef DEBUG_MESSAGES
201                 mov si,startup_msg
202                 call writemsg
203                 mov al,dl
204                 call writehex2
205                 call crlf
206 %endif
207
208                 ; Now figure out what we're actually doing
209                 ; Note: use passed-in DL value rather than 7Fh because
210                 ; at least some BIOSes will get the wrong value otherwise
211                 mov ax,4B01h                    ; Get disk emulation status
212                 mov dl,[DriveNo]
213                 mov si,spec_packet
214                 int 13h
215                 jc award_hack                   ; changed for BrokenAwardHack
216                 mov dl,[DriveNo]
217                 cmp [sp_drive],dl               ; Should contain the drive number
218                 jne spec_query_failed
219
220 %ifdef DEBUG_MESSAGES
221                 mov si,spec_ok_msg
222                 call writemsg
223                 mov al,byte [sp_drive]
224                 call writehex2
225                 call crlf
226 %endif
227
228 found_drive:
229                 ; Alright, we have found the drive.  Now, try to find the
230                 ; boot file itself.  If we have a boot info table, life is
231                 ; good; if not, we have to make some assumptions, and try
232                 ; to figure things out ourselves.  In particular, the
233                 ; assumptions we have to make are:
234                 ; - single session only
235                 ; - only one boot entry (no menu or other alternatives)
236
237                 cmp dword [bi_file],0           ; Address of code to load
238                 jne found_file                  ; Boot info table present :)
239
240 %ifdef DEBUG_MESSAGES
241                 mov si,noinfotable_msg
242                 call writemsg
243 %endif
244                 
245                 ; No such luck.  See if the the spec packet contained one.
246                 mov eax,[sp_lba]
247                 and eax,eax
248                 jz set_file                     ; Good enough
249
250 %ifdef DEBUG_MESSAGES
251                 mov si,noinfoinspec_msg
252                 call writemsg
253 %endif
254                 
255                 ; No such luck.  Get the Boot Record Volume, assuming single
256                 ; session disk, and that we're the first entry in the chain
257                 mov eax,17                      ; Assumed address of BRV
258                 mov bx,trackbuf
259                 call getonesec
260
261                 mov eax,[trackbuf+47h]          ; Get boot catalog address
262                 mov bx,trackbuf
263                 call getonesec                  ; Get boot catalog
264
265                 mov eax,[trackbuf+28h]          ; First boot entry
266                 ; And hope and pray this is us...
267
268                 ; Some BIOSes apparently have limitations on the size 
269                 ; that may be loaded (despite the El Torito spec being very
270                 ; clear on the fact that it must all be loaded.)  Therefore,
271                 ; we load it ourselves, and *bleep* the BIOS.
272
273 set_file:
274                 mov [bi_file],eax
275
276 found_file:
277                 ; Set up boot file sizes
278                 mov eax,[bi_length]
279                 sub eax,SECTOR_SIZE-3
280                 shr eax,2                       ; bytes->dwords
281                 mov [ImageDwords],eax           ; boot file dwords
282                 add eax,(2047 >> 2)
283                 shr eax,9                       ; dwords->sectors
284                 mov [ImageSectors],ax           ; boot file sectors
285
286                 mov eax,[bi_file]               ; Address of code to load
287                 inc eax                         ; Don't reload bootstrap code
288 %ifdef DEBUG_MESSAGES
289                 mov si,offset_msg
290                 call writemsg
291                 call writehex8
292                 call crlf
293 %endif
294
295                 ; Just in case some BIOSes have problems with
296                 ; segment wraparound, use the normalized address
297                 mov bx,((7C00h+2048) >> 4)
298                 mov es,bx
299                 xor bx,bx
300                 mov bp,[ImageSectors]
301 %ifdef DEBUG_MESSAGES
302                 push ax
303                 mov si,size_msg
304                 call writemsg
305                 mov ax,bp
306                 call writehex4
307                 call crlf
308                 pop ax
309 %endif
310                 call getlinsec
311
312                 push ds
313                 pop es
314
315 %ifdef DEBUG_MESSAGES
316                 mov si,loaded_msg
317                 call writemsg
318 %endif
319
320                 ; Verify the checksum on the loaded image.
321 verify_image:
322                 mov si,7C00h+2048
323                 mov bx,es
324                 mov ecx,[ImageDwords]
325                 mov edi,[FirstSecSum]           ; First sector checksum
326 .loop           es lodsd
327                 add edi,eax
328                 dec ecx
329                 jz .done
330                 and si,si
331                 jnz .loop
332                 ; SI wrapped around, advance ES
333                 add bx,1000h
334                 mov es,bx
335                 jmp short .loop
336 .done:          mov ax,ds
337                 mov es,ax
338                 cmp [bi_csum],edi
339                 je integrity_ok
340
341                 mov si,checkerr_msg
342                 call writemsg
343                 jmp kaboom
344
345 integrity_ok:
346 %ifdef DEBUG_MESSAGES
347                 mov si,allread_msg
348                 call writemsg
349 %endif
350                 jmp all_read                    ; Jump to main code
351
352 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
353 ;; Start of BrokenAwardHack --- 10-nov-2002           Knut_Petersen@t-online.de
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
355 ;;
356 ;; There is a problem with certain versions of the AWARD BIOS ... 
357 ;; the boot sector will be loaded and executed correctly, but, because the
358 ;; int 13 vector points to the wrong code in the BIOS, every attempt to
359 ;; load the spec packet will fail. We scan for the equivalent of
360 ;;
361 ;;      mov     ax,0201h
362 ;;      mov     bx,7c00h
363 ;;      mov     cx,0006h
364 ;;      mov     dx,0180h
365 ;;      pushf
366 ;;      call    <direct far>
367 ;;
368 ;; and use <direct far> as the new vector for int 13. The code above is
369 ;; used to load the boot code into ram, and there should be no reason
370 ;; for anybody to change it now or in the future. There are no opcodes
371 ;; that use encodings relativ to IP, so scanning is easy. If we find the
372 ;; code above in the BIOS code we can be pretty sure to run on a machine
373 ;; with an broken AWARD BIOS ... 
374 ;;
375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
376                                                                              ;;
377 %ifdef DEBUG_MESSAGES                                                        ;;
378                                                                              ;;
379 award_notice    db      "Trying BrokenAwardHack first ...",CR,LF,0           ;;
380 award_not_orig  db      "BAH: Original Int 13 vector   : ",0                 ;;
381 award_not_new   db      "BAH: Int 13 vector changed to : ",0                 ;;
382 award_not_succ  db      "BAH: SUCCESS",CR,LF,0                               ;;
383 award_not_fail  db      "BAH: FAILURE"                                       ;;
384 award_not_crlf  db      CR,LF,0                                              ;;
385                                                                              ;;
386 %endif                                                                       ;;
387                                                                              ;;
388 award_oldint13  dd      0                                                    ;;
389 award_string    db      0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah    ;;
390                                                                              ;;
391                                                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
392 award_hack:     mov     si,spec_err_msg         ; Moved to this place from
393                 call    writemsg                ; spec_query_faild
394                                                 ;
395 %ifdef DEBUG_MESSAGES                           ;
396                                                 ;
397                 mov     si,award_notice         ; display our plan
398                 call    writemsg                ;
399                 mov     si,award_not_orig       ; display original int 13
400                 call    writemsg                ; vector
401 %endif                                          ;
402                 mov     eax,[13h*4]             ;
403                 mov     [award_oldint13],eax    ;
404                                                 ;
405 %ifdef DEBUG_MESSAGES                           ;
406                                                 ;
407                 call    writehex8               ;
408                 mov     si,award_not_crlf       ; 
409                 call    writestr                ;
410 %endif                                          ;
411                 push    es                      ; save ES
412                 mov     ax,0f000h               ; ES = BIOS Seg
413                 mov     es,ax                   ;
414                 cld                             ;
415                 xor     di,di                   ; start at ES:DI = f000:0
416 award_loop:     push    di                      ; save DI
417                 mov     si,award_string         ; scan for award_string
418                 mov     cx,7                    ; length of award_string = 7dw
419                 repz    cmpsw                   ; compare
420                 pop     di                      ; restore DI
421                 jcxz    award_found             ; jmp if found
422                 inc     di                      ; not found, inc di
423                 jno     award_loop              ; 
424                                                 ;
425 award_failed:   pop     es                      ; No, not this way :-((
426 award_fail2:                                    ;
427                                                 ;
428 %ifdef DEBUG_MESSAGES                           ;
429                                                 ;
430                 mov     si,award_not_fail       ; display failure ...
431                 call    writemsg                ;
432 %endif                                          ;
433                 mov     eax,[award_oldint13]    ; restore the original int
434                 or      eax,eax                 ; 13 vector if there is one
435                 jz      spec_query_failed       ; and try other workarounds
436                 mov     [13h*4],eax             ;
437                 jmp     spec_query_failed       ;
438                                                 ;
439 award_found:    mov     eax,[es:di+0eh]         ; load possible int 13 addr
440                 pop     es                      ; restore ES
441                                                 ;
442                 cmp     eax,[award_oldint13]    ; give up if this is the
443                 jz      award_failed            ; active int 13 vector,
444                 mov     [13h*4],eax             ; otherwise change 0:13h*4
445                                                 ;
446                                                 ;
447 %ifdef DEBUG_MESSAGES                           ;
448                                                 ;
449                 push    eax                     ; display message and 
450                 mov     si,award_not_new        ; new vector address
451                 call    writemsg                ;
452                 pop     eax                     ;
453                 call    writehex8               ;
454                 mov     si,award_not_crlf       ;
455                 call    writestr                ;
456 %endif                                          ;
457                 mov     ax,4B01h                ; try to read the spec packet
458                 mov     dl,[DriveNo]            ; now ... it should not fail
459                 mov     si,spec_packet          ; any longer
460                 int     13h                     ; 
461                 jc      award_fail2             ;
462                                                 ;
463 %ifdef DEBUG_MESSAGES                           ;
464                                                 ;
465                 mov     si,award_not_succ       ; display our SUCCESS
466                 call    writemsg                ;
467 %endif                                          ;
468                 jmp     found_drive             ; and leave error recovery code
469                                                 ;
470 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
471 ;; End of BrokenAwardHack ----            10-nov-2002 Knut_Petersen@t-online.de
472 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
473
474
475                 ; INT 13h, AX=4B01h, DL=<passed in value> failed.
476                 ; Try to scan the entire 80h-FFh from the end.
477
478 spec_query_failed:
479
480                 ; some code moved to BrokenAwardHack
481
482                 mov dl,0FFh
483 .test_loop:     pusha
484                 mov ax,4B01h
485                 mov si,spec_packet
486                 mov byte [si],13                ; Size of buffer
487                 int 13h
488                 popa
489                 jc .still_broken
490
491                 mov si,maybe_msg
492                 call writemsg
493                 mov al,dl
494                 call writehex2
495                 call crlf
496
497                 cmp byte [sp_drive],dl
498                 jne .maybe_broken
499
500                 ; Okay, good enough...
501                 mov si,alright_msg
502                 call writemsg
503                 mov [DriveNo],dl
504 .found_drive:   jmp found_drive
505
506                 ; Award BIOS 4.51 apparently passes garbage in sp_drive,
507                 ; but if this was the drive number originally passed in
508                 ; DL then consider it "good enough"
509 .maybe_broken:
510                 cmp byte [DriveNo],dl
511                 je .found_drive
512
513 .still_broken:  dec dx
514                 cmp dl, 80h
515                 jnb .test_loop
516
517                 ; No spec packet anywhere.  Some particularly pathetic
518                 ; BIOSes apparently don't even implement function
519                 ; 4B01h, so we can't query a spec packet no matter
520                 ; what.  If we got a drive number in DL, then try to
521                 ; use it, and if it works, then well...
522                 mov dl,[DriveNo]
523                 cmp dl,81h                      ; Should be 81-FF at least
524                 jb fatal_error                  ; If not, it's hopeless
525
526                 ; Write a warning to indicate we're on *very* thin ice now
527                 mov si,nospec_msg
528                 call writemsg
529                 mov al,dl
530                 call writehex2
531                 call crlf
532                 jmp .found_drive                ; Pray that this works...
533
534 fatal_error:
535                 mov si,nothing_msg
536                 call writemsg
537
538 .norge:         jmp short .norge
539
540                 ; Information message (DS:SI) output
541                 ; Prefix with "isolinux: "
542                 ;
543 writemsg:       push ax
544                 push si
545                 mov si,isolinux_str
546                 call writestr
547                 pop si
548                 call writestr
549                 pop ax                          
550                 ret
551
552 ;
553 ; Write a character to the screen.  There is a more "sophisticated"
554 ; version of this in the subsequent code, so we patch the pointer
555 ; when appropriate.
556 ;
557
558 writechr:
559                 jmp near writechr_simple        ; 3-byte jump
560
561 writechr_simple:
562                 pushfd
563                 pushad
564                 mov ah,0Eh
565                 xor bx,bx
566                 int 10h
567                 popad
568                 popfd
569                 ret
570
571 ;
572 ; Get one sector.  Convenience entry point.
573 ;
574 getonesec:
575                 mov bp,1
576                 ; Fall through to getlinsec
577
578 ;
579 ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
580 ;
581 ; Note that we can't always do this as a single request, because at least
582 ; Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick
583 ; to 32 sectors (64K) per request.
584 ;
585 ; Input:
586 ;       EAX     - Linear sector number
587 ;       ES:BX   - Target buffer
588 ;       BP      - Sector count
589 ;
590 getlinsec:
591                 mov si,dapa                     ; Load up the DAPA
592                 mov [si+4],bx
593                 mov bx,es
594                 mov [si+6],bx
595                 mov [si+8],eax
596 .loop:
597                 push bp                         ; Sectors left
598                 cmp bp,[MaxTransfer]
599                 jbe .bp_ok
600                 mov bp,[MaxTransfer]
601 .bp_ok:
602                 mov [si+2],bp
603                 push si
604                 mov dl,[DriveNo]
605                 mov ah,42h                      ; Extended Read
606                 call xint13
607                 pop si
608                 pop bp
609                 movzx eax,word [si+2]           ; Sectors we read
610                 add [si+8],eax                  ; Advance sector pointer
611                 sub bp,ax                       ; Sectors left
612                 shl ax,SECTOR_SHIFT-4           ; 2048-byte sectors -> segment
613                 add [si+6],ax                   ; Advance buffer pointer
614                 and bp,bp
615                 jnz .loop
616                 mov eax,[si+8]                  ; Next sector
617                 ret
618
619                 ; INT 13h with retry
620 xint13:         mov byte [RetryCount],retry_count
621 .try:           pushad
622                 int 13h
623                 jc .error
624                 add sp,byte 8*4                 ; Clean up stack
625                 ret
626 .error:
627                 mov [DiskError],ah              ; Save error code
628                 popad
629                 mov [DiskSys],ax                ; Save system call number
630                 dec byte [RetryCount]
631                 jz .real_error
632                 push ax
633                 mov al,[RetryCount]
634                 mov ah,[dapa+2]                 ; Sector transfer count
635                 cmp al,2                        ; Only 2 attempts left
636                 ja .nodanger
637                 mov ah,1                        ; Drop transfer size to 1
638                 jmp short .setsize
639 .nodanger:
640                 cmp al,retry_count-2
641                 ja .again                       ; First time, just try again
642                 shr ah,1                        ; Otherwise, try to reduce
643                 adc ah,0                        ; the max transfer size, but not to 0
644 .setsize:
645                 mov [MaxTransfer],ah
646                 mov [dapa+2],ah
647 .again:
648                 pop ax
649                 jmp .try
650
651 .real_error:    mov si,diskerr_msg
652                 call writemsg
653                 mov al,[DiskError]
654                 call writehex2
655                 mov si,oncall_str
656                 call writestr
657                 mov ax,[DiskSys]
658                 call writehex4
659                 mov si,ondrive_str
660                 call writestr
661                 mov al,dl
662                 call writehex2
663                 call crlf
664                 ; Fall through to kaboom
665
666 ;
667 ; kaboom: write a message and bail out.  Wait for a user keypress,
668 ;         then do a hard reboot.
669 ;
670 kaboom:
671                 lss sp,[cs:Stack]
672                 mov ax,cs
673                 mov ds,ax
674                 mov es,ax
675                 mov fs,ax
676                 mov gs,ax
677                 sti
678                 mov si,err_bootfailed
679                 call cwritestr
680                 call getchar
681                 cli
682                 mov word [BIOS_magic],0 ; Cold reboot
683                 jmp 0F000h:0FFF0h       ; Reset vector address
684
685 ; -----------------------------------------------------------------------------
686 ;  Common modules needed in the first sector
687 ; -----------------------------------------------------------------------------
688
689 %include "writestr.inc"         ; String output
690 writestr        equ cwritestr
691 %include "writehex.inc"         ; Hexadecimal output
692
693 ; -----------------------------------------------------------------------------
694 ; Data that needs to be in the first sector
695 ; -----------------------------------------------------------------------------
696
697 syslinux_banner db CR, LF, 'ISOLINUX ', version_str, ' ', date, ' ', 0
698 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
699                 db CR, LF, 0
700 isolinux_str    db 'isolinux: ', 0
701 %ifdef DEBUG_MESSAGES
702 startup_msg:    db 'Starting up, DL = ', 0
703 spec_ok_msg:    db 'Loaded spec packet OK, drive = ', 0
704 secsize_msg:    db 'Sector size appears to be ', 0
705 offset_msg:     db 'Loading main image from LBA = ', 0
706 size_msg:       db 'Sectors to load = ', 0
707 loaded_msg:     db 'Loaded boot image, verifying...', CR, LF, 0
708 verify_msg:     db 'Image checksum verified.', CR, LF, 0
709 allread_msg     db 'Main image read, jumping to main code...', CR, LF, 0
710 %endif
711 noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0
712 noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0
713 spec_err_msg:   db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
714 maybe_msg:      db 'Found something at drive = ', 0
715 alright_msg:    db 'Looks like it might be right, continuing...', CR, LF, 0
716 nospec_msg      db 'Extremely broken BIOS detected, last ditch attempt with drive = ', 0
717 nosecsize_msg:  db 'Failed to get sector size, assuming 0800', CR, LF, 0
718 diskerr_msg:    db 'Disk error ', 0
719 oncall_str:     db ', AX = ',0
720 ondrive_str:    db ', drive ', 0
721 nothing_msg:    db 'Failed to locate CD-ROM device; boot failed.', CR, LF, 0
722 checkerr_msg:   db 'Image checksum error, sorry...', CR, LF, 0
723
724 err_bootfailed  db CR, LF, 'Boot failed: press a key to retry...'
725 bailmsg         equ err_bootfailed
726 crlf_msg        db CR, LF
727 null_msg        db 0
728
729 ;
730 ; El Torito spec packet
731 ;
732                 align 8, db 0
733 spec_packet:    db 13h                          ; Size of packet
734 sp_media:       db 0                            ; Media type
735 sp_drive:       db 0                            ; Drive number
736 sp_controller:  db 0                            ; Controller index
737 sp_lba:         dd 0                            ; LBA for emulated disk image
738 sp_devspec:     dw 0                            ; IDE/SCSI information
739 sp_buffer:      dw 0                            ; User-provided buffer
740 sp_loadseg:     dw 0                            ; Load segment
741 sp_sectors:     dw 0                            ; Sector count
742 sp_chs:         db 0,0,0                        ; Simulated CHS geometry
743 sp_dummy:       db 0                            ; Scratch, safe to overwrite
744
745 ;
746 ; EBIOS drive parameter packet
747 ;
748                 align 8, db 0
749 drive_params:   dw 30                           ; Buffer size
750 dp_flags:       dw 0                            ; Information flags
751 dp_cyl:         dd 0                            ; Physical cylinders
752 dp_head:        dd 0                            ; Physical heads
753 dp_sec:         dd 0                            ; Physical sectors/track
754 dp_totalsec:    dd 0,0                          ; Total sectors
755 dp_secsize:     dw 0                            ; Bytes per sector
756 dp_dpte:        dd 0                            ; Device Parameter Table
757 dp_dpi_key:     dw 0                            ; 0BEDDh if rest valid
758 dp_dpi_len:     db 0                            ; DPI len
759                 db 0
760                 dw 0
761 dp_bus:         times 4 db 0                    ; Host bus type
762 dp_interface:   times 8 db 0                    ; Interface type
763 db_i_path:      dd 0,0                          ; Interface path
764 db_d_path:      dd 0,0                          ; Device path
765                 db 0
766 db_dpi_csum:    db 0                            ; Checksum for DPI info
767
768 ;
769 ; EBIOS disk address packet
770 ;
771                 align 8, db 0
772 dapa:           dw 16                           ; Packet size
773 .count:         dw 0                            ; Block count
774 .off:           dw 0                            ; Offset of buffer
775 .seg:           dw 0                            ; Segment of buffer
776 .lba:           dd 0                            ; LBA (LSW)
777                 dd 0                            ; LBA (MSW)
778
779                 alignb 4, db 0
780 Stack           dw _start, 0                    ; SS:SP for stack reset
781 MaxTransfer     dw 32                           ; Max sectors per transfer
782
783 rl_checkpt      equ $                           ; Must be <= 800h
784
785 rl_checkpt_off  equ ($-$$)
786 %ifndef DEPEND
787 %if rl_checkpt_off > 0x800
788 %error "Sector 0 overflow"
789 %endif
790 %endif
791
792 ; ----------------------------------------------------------------------------
793 ;  End of code and data that have to be in the first sector
794 ; ----------------------------------------------------------------------------
795
796 all_read:
797 ;
798 ; Initialize screen (if we're using one)
799 ;
800                 ; Now set up screen parameters
801                 call adjust_screen
802
803                 ; Wipe the F-key area
804                 mov al,NULLFILE
805                 mov di,FKeyName
806                 mov cx,10*(1 << FILENAME_MAX_LG2)
807                 rep stosb
808
809                 ; Patch the writechr routine to point to the full code
810                 mov word [writechr+1], writechr_full-(writechr+3)
811
812 ; Tell the user we got this far...
813 %ifndef DEBUG_MESSAGES                  ; Gets messy with debugging on
814                 mov si,copyright_str
815                 call writestr
816 %endif
817
818 ; Test tracers
819                 TRACER 'T'
820                 TRACER '>'
821
822 ;
823 ; Common initialization code
824 ;
825 %include "init.inc"
826 %include "cpuinit.inc"
827
828 ;
829 ; Now we're all set to start with our *real* business.  First load the
830 ; configuration file (if any) and parse it.
831 ;
832 ; In previous versions I avoided using 32-bit registers because of a
833 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
834 ; random.  I figure, though, that if there are any of those still left
835 ; they probably won't be trying to install Linux on them...
836 ;
837 ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
838 ; to take'm out.  In fact, we may want to put them back if we're going
839 ; to boot ELKS at some point.
840 ;
841
842 ;
843 ; Now, we need to sniff out the actual filesystem data structures.
844 ; mkisofs gave us a pointer to the primary volume descriptor
845 ; (which will be at 16 only for a single-session disk!); from the PVD
846 ; we should be able to find the rest of what we need to know.
847
848 get_fs_structures:
849                 mov eax,[bi_pvd]
850                 mov bx,trackbuf
851                 call getonesec
852
853                 mov eax,[trackbuf+156+2]
854                 mov [RootDir+dir_lba],eax
855                 mov [CurDir+dir_lba],eax
856 %ifdef DEBUG_MESSAGES
857                 mov si,dbg_rootdir_msg
858                 call writemsg
859                 call writehex8
860                 call crlf
861 %endif
862                 mov eax,[trackbuf+156+10]
863                 mov [RootDir+dir_len],eax               
864                 mov [CurDir+dir_len],eax
865                 add eax,SECTOR_SIZE-1
866                 shr eax,SECTOR_SHIFT
867                 mov [RootDir+dir_clust],eax
868                 mov [CurDir+dir_clust],eax
869
870                 ; Look for an isolinux directory, and if found,
871                 ; make it the current directory instead of the root
872                 ; directory.
873                 mov di,boot_dir                 ; Search for /boot/isolinux
874                 mov al,02h
875                 call searchdir_iso
876                 jnz .found_dir
877                 mov di,isolinux_dir
878                 mov al,02h                      ; Search for /isolinux
879                 call searchdir_iso
880                 jz .no_isolinux_dir
881 .found_dir:
882                 mov [CurDir+dir_len],eax
883                 mov eax,[si+file_left]
884                 mov [CurDir+dir_clust],eax
885                 xor eax,eax                     ; Free this file pointer entry
886                 xchg eax,[si+file_sector]
887                 mov [CurDir+dir_lba],eax
888 %ifdef DEBUG_MESSAGES
889                 push si
890                 mov si,dbg_isodir_msg
891                 call writemsg
892                 pop si
893                 call writehex8
894                 call crlf
895 %endif
896 .no_isolinux_dir:
897
898 ;
899 ; Locate the configuration file
900 ;
901 load_config:
902 %ifdef DEBUG_MESSAGES
903                 mov si,dbg_config_msg
904                 call writemsg
905 %endif
906
907                 mov di,isolinux_cfg
908                 call open
909                 jz no_config_file               ; Not found or empty
910
911 %ifdef DEBUG_MESSAGES
912                 mov si,dbg_configok_msg
913                 call writemsg
914 %endif
915
916 ;
917 ; Now we have the config file open.  Parse the config file and
918 ; run the user interface.
919 ;
920 %include "ui.inc"
921
922 ;
923 ; Linux kernel loading code is common.
924 ;
925 %include "runkernel.inc"
926
927 ;
928 ; COMBOOT-loading code
929 ;
930 %include "comboot.inc"
931 %include "com32.inc"
932 %include "cmdline.inc"
933
934 ;
935 ; Boot sector loading code
936 ;
937 %include "bootsect.inc"
938
939 ;
940 ; Enable disk emulation.  The kind of disk we emulate is dependent on the size of
941 ; the file: 1200K, 1440K or 2880K floppy, otherwise harddisk.
942 ;
943 is_disk_image:
944                 TRACER CR
945                 TRACER LF
946                 TRACER 'D'
947                 TRACER ':'
948
949                 shl edx,16
950                 mov dx,ax                       ; Set EDX <- file size
951                 mov di,img_table
952                 mov cx,img_table_count
953                 mov eax,[si+file_sector]        ; Starting LBA of file
954                 mov [dsp_lba],eax               ; Location of file
955                 mov byte [dsp_drive], 0         ; 00h floppy, 80h hard disk
956 .search_table:
957                 TRACER 't'
958                 mov eax,[di+4]
959                 cmp edx,[di]
960                 je .type_found
961                 add di,8
962                 loop .search_table
963
964                 ; Hard disk image.  Need to examine the partition table
965                 ; in order to deduce the C/H/S geometry.  Sigh.
966 .hard_disk_image:
967                 TRACER 'h'
968                 cmp edx,512
969                 jb .bad_image
970
971                 mov bx,trackbuf
972                 mov cx,1                        ; Load 1 sector
973                 call getfssec
974                 
975                 cmp word [trackbuf+510],0aa55h  ; Boot signature
976                 jne .bad_image          ; Image not bootable
977
978                 mov cx,4                        ; 4 partition entries
979                 mov di,trackbuf+446             ; Start of partition table
980
981                 xor ax,ax                       ; Highest sector(al) head(ah)
982
983 .part_scan:
984                 cmp byte [di+4], 0
985                 jz .part_loop
986                 lea si,[di+1]
987                 call .hs_check
988                 add si,byte 4
989                 call .hs_check
990 .part_loop:
991                 add di,byte 16
992                 loop .part_scan
993                 
994                 push eax                        ; H/S
995                 push edx                        ; File size
996                 mov bl,ah
997                 xor bh,bh
998                 inc bx                          ; # of heads in BX
999                 xor ah,ah                       ; # of sectors in AX
1000                 cwde                            ; EAX[31:16] <- 0
1001                 mul bx
1002                 shl eax,9                       ; Convert to bytes
1003                 ; Now eax contains the number of bytes per cylinder
1004                 pop ebx                         ; File size
1005                 xor edx,edx
1006                 div ebx
1007                 and edx,edx
1008                 jz .no_remainder
1009                 inc eax                         ; Fractional cylinder...
1010                 ; Now (e)ax contains the number of cylinders
1011 .no_remainder:  cmp eax,1024
1012                 jna .ok_cyl
1013                 mov ax,1024                     ; Max possible #
1014 .ok_cyl:        dec ax                          ; Convert to max cylinder no
1015                 pop ebx                         ; S(bl) H(bh)
1016                 shl ah,6
1017                 or bl,ah
1018                 xchg ax,bx
1019                 shl eax,16
1020                 mov ah,bl
1021                 mov al,4                        ; Hard disk boot
1022                 mov byte [dsp_drive], 80h       ; Drive 80h = hard disk
1023
1024 .type_found:
1025                 TRACER 'T'
1026                 mov bl,[sp_media]
1027                 and bl,0F0h                     ; Copy controller info bits
1028                 or al,bl
1029                 mov [dsp_media],al              ; Emulation type
1030                 shr eax,8
1031                 mov [dsp_chs],eax               ; C/H/S geometry
1032                 mov ax,[sp_devspec]             ; Copy device spec
1033                 mov [dsp_devspec],ax
1034                 mov al,[sp_controller]          ; Copy controller index
1035                 mov [dsp_controller],al
1036
1037                 TRACER 'V'
1038                 call vgaclearmode               ; Reset video
1039
1040                 mov ax,4C00h                    ; Enable emulation and boot
1041                 mov si,dspec_packet
1042                 mov dl,[DriveNo]
1043                 lss sp,[InitStack]
1044                 TRACER 'X'
1045
1046                 int 13h
1047
1048                 ; If this returns, we have problems
1049 .bad_image:
1050                 mov si,err_disk_image
1051                 call cwritestr
1052                 jmp enter_command
1053
1054 ;
1055 ; Look for the highest seen H/S geometry
1056 ; We compute cylinders separately
1057 ;
1058 .hs_check:
1059                 mov bl,[si]                     ; Head #
1060                 cmp bl,ah
1061                 jna .done_track
1062                 mov ah,bl                       ; New highest head #
1063 .done_track:    mov bl,[si+1]
1064                 and bl,3Fh                      ; Sector #
1065                 cmp bl,al
1066                 jna .done_sector
1067                 mov al,bl
1068 .done_sector:   ret
1069
1070 ;
1071 ; Boot a specified local disk.  AX specifies the BIOS disk number; or
1072 ; 0xFFFF in case we should execute INT 18h ("next device.")
1073 ;
1074 local_boot:
1075                 call vgaclearmode
1076                 lss sp,[cs:Stack]               ; Restore stack pointer
1077                 xor dx,dx
1078                 mov ds,dx
1079                 mov es,dx
1080                 mov fs,dx
1081                 mov gs,dx
1082                 mov si,localboot_msg
1083                 call writestr
1084                 cmp ax,-1
1085                 je .int18
1086                 
1087                 ; Load boot sector from the specified BIOS device and jump to it.
1088                 mov dl,al
1089                 xor dh,dh
1090                 push dx
1091                 xor ax,ax                       ; Reset drive
1092                 call xint13
1093                 mov ax,0201h                    ; Read one sector
1094                 mov cx,0001h                    ; C/H/S = 0/0/1 (first sector)
1095                 mov bx,trackbuf
1096                 call xint13
1097                 pop dx
1098                 cli                             ; Abandon hope, ye who enter here
1099                 mov si,trackbuf
1100                 mov di,07C00h
1101                 mov cx,512                      ; Probably overkill, but should be safe
1102                 rep movsd
1103                 lss sp,[cs:InitStack]
1104                 jmp 0:07C00h                    ; Jump to new boot sector
1105
1106 .int18:
1107                 int 18h                         ; Hope this does the right thing...
1108                 jmp kaboom                      ; If we returned, oh boy...
1109
1110 ;
1111 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1112 ;
1113 abort_check:
1114                 call pollchar
1115                 jz ac_ret1
1116                 pusha
1117                 call getchar
1118                 cmp al,27                       ; <ESC>
1119                 je ac_kill
1120                 cmp al,3                        ; <Ctrl-C>
1121                 jne ac_ret2
1122 ac_kill:        mov si,aborted_msg
1123
1124 ;
1125 ; abort_load: Called by various routines which wants to print a fatal
1126 ;             error message and return to the command prompt.  Since this
1127 ;             may happen at just about any stage of the boot process, assume
1128 ;             our state is messed up, and just reset the segment registers
1129 ;             and the stack forcibly.
1130 ;
1131 ;             SI    = offset (in _text) of error message to print
1132 ;
1133 abort_load:
1134                 mov ax,cs                       ; Restore CS = DS = ES
1135                 mov ds,ax
1136                 mov es,ax
1137                 cli
1138                 lss sp,[cs:Stack]               ; Reset the stack
1139                 sti
1140                 call cwritestr                  ; Expects SI -> error msg
1141 al_ok:          jmp enter_command               ; Return to command prompt
1142 ;
1143 ; End of abort_check
1144 ;
1145 ac_ret2:        popa
1146 ac_ret1:        ret
1147
1148
1149 ;
1150 ; searchdir:
1151 ;
1152 ;       Open a file
1153 ;
1154 ;            On entry:
1155 ;               DS:DI   = filename
1156 ;            If successful:
1157 ;               ZF clear
1158 ;               SI              = file pointer
1159 ;               DX:AX or EAX    = file length in bytes
1160 ;            If unsuccessful
1161 ;               ZF set
1162 ;
1163
1164 ;
1165 ; searchdir_iso is a special entry point for ISOLINUX only.  In addition
1166 ; to the above, searchdir_iso passes a file flag mask in AL.  This is useful
1167 ; for searching for directories.
1168 ;
1169 alloc_failure:
1170                 xor ax,ax                       ; ZF <- 1
1171                 ret
1172
1173 searchdir:
1174                 xor al,al
1175 searchdir_iso:
1176                 mov [ISOFlags],al
1177                 TRACER 'S'
1178                 call allocate_file              ; Temporary file structure for directory
1179                 jnz alloc_failure
1180                 push es
1181                 push ds
1182                 pop es                          ; ES = DS
1183                 mov si,CurDir
1184                 cmp byte [di],'/'               ; If filename begins with slash
1185                 jne .not_rooted
1186                 inc di                          ; Skip leading slash
1187                 mov si,RootDir                  ; Reference root directory instead
1188 .not_rooted:
1189                 mov eax,[si+dir_clust]
1190                 mov [bx+file_left],eax
1191                 mov eax,[si+dir_lba]
1192                 mov [bx+file_sector],eax
1193                 mov edx,[si+dir_len]
1194
1195 .look_for_slash:
1196                 mov ax,di
1197 .scan:
1198                 mov cl,[di]
1199                 inc di
1200                 and cl,cl
1201                 jz .isfile
1202                 cmp cl,'/'
1203                 jne .scan
1204                 mov [di-1],byte 0               ; Terminate at directory name
1205                 mov cl,02h                      ; Search for directory
1206                 xchg cl,[ISOFlags]
1207
1208                 push di                         ; Save these...
1209                 push cx
1210
1211                 ; Create recursion stack frame...
1212                 push word .resume               ; Where to "return" to
1213                 push es
1214 .isfile:        xchg ax,di
1215
1216 .getsome:
1217                 ; Get a chunk of the directory
1218                 ; This relies on the fact that ISOLINUX doesn't change SI
1219                 mov si,trackbuf
1220                 TRACER 'g'
1221                 pushad
1222                 xchg bx,si
1223                 mov cx,[BufSafe]
1224                 call getfssec
1225                 popad
1226
1227 .compare:
1228                 movzx eax,byte [si]             ; Length of directory entry
1229                 cmp al,33
1230                 jb .next_sector
1231                 TRACER 'c'
1232                 mov cl,[si+25]
1233                 xor cl,[ISOFlags]
1234                 test cl, byte 8Eh               ; Unwanted file attributes!
1235                 jnz .not_file
1236                 pusha
1237                 movzx cx,byte [si+32]           ; File identifier length
1238                 add si,byte 33                  ; File identifier offset
1239                 TRACER 'i'
1240                 call iso_compare_names
1241                 popa
1242                 je .success
1243 .not_file:
1244                 sub edx,eax                     ; Decrease bytes left
1245                 jbe .failure
1246                 add si,ax                       ; Advance pointer
1247
1248 .check_overrun:
1249                 ; Did we finish the buffer?
1250                 cmp si,trackbuf+trackbufsize
1251                 jb .compare                     ; No, keep going
1252
1253                 jmp short .getsome              ; Get some more directory
1254
1255 .next_sector:
1256                 ; Advance to the beginning of next sector
1257                 lea ax,[si+SECTOR_SIZE-1]
1258                 and ax,~(SECTOR_SIZE-1)
1259                 sub ax,si
1260                 jmp short .not_file             ; We still need to do length checks
1261
1262 .failure:       xor eax,eax                     ; ZF = 1
1263                 mov [bx+file_sector],eax
1264                 pop es
1265                 ret
1266
1267 .success:
1268                 mov eax,[si+2]                  ; Location of extent
1269                 mov [bx+file_sector],eax
1270                 mov eax,[si+10]                 ; Data length
1271                 push eax
1272                 add eax,SECTOR_SIZE-1
1273                 shr eax,SECTOR_SHIFT
1274                 mov [bx+file_left],eax
1275                 pop eax
1276                 mov edx,eax
1277                 shr edx,16
1278                 and bx,bx                       ; ZF = 0
1279                 mov si,bx
1280                 pop es
1281                 ret
1282
1283 .resume:        ; We get here if we were only doing part of a lookup
1284                 ; This relies on the fact that .success returns bx == si
1285                 xchg edx,eax                    ; Directory length in edx
1286                 pop cx                          ; Old ISOFlags
1287                 pop di                          ; Next filename pointer
1288                 mov byte [di-1], '/'            ; Restore slash
1289                 mov [ISOFlags],cl               ; Restore the flags
1290                 jz .failure                     ; Did we fail?  If so fail for real!
1291                 jmp .look_for_slash             ; Otherwise, next level
1292
1293 ;
1294 ; allocate_file: Allocate a file structure
1295 ;
1296 ;               If successful:
1297 ;                 ZF set
1298 ;                 BX = file pointer
1299 ;               In unsuccessful:
1300 ;                 ZF clear
1301 ;
1302 allocate_file:
1303                 TRACER 'a'
1304                 push cx
1305                 mov bx,Files
1306                 mov cx,MAX_OPEN
1307 .check:         cmp dword [bx], byte 0
1308                 je .found
1309                 add bx,open_file_t_size         ; ZF = 0
1310                 loop .check
1311                 ; ZF = 0 if we fell out of the loop
1312 .found:         pop cx
1313                 ret
1314
1315 ;
1316 ; iso_compare_names: 
1317 ;       Compare the names DS:SI and DS:DI and report if they are
1318 ;       equal from an ISO 9660 perspective.  SI is the name from
1319 ;       the filesystem; CX indicates its length, and ';' terminates.
1320 ;       DI is expected to end with a null.
1321 ;
1322 ;       Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
1323 ;
1324
1325 iso_compare_names:
1326                 ; First, terminate and canonicalize input filename
1327                 push di
1328                 mov di,ISOFileName
1329 .canon_loop:    jcxz .canon_end
1330                 lodsb
1331                 dec cx
1332                 cmp al,';'
1333                 je .canon_end
1334                 and al,al
1335                 je .canon_end
1336                 stosb
1337                 cmp di,ISOFileNameEnd-1         ; Guard against buffer overrun
1338                 jb .canon_loop
1339 .canon_end:
1340                 cmp di,ISOFileName
1341                 jbe .canon_done
1342                 cmp byte [di-1],'.'             ; Remove terminal dots
1343                 jne .canon_done
1344                 dec di
1345                 jmp short .canon_end
1346 .canon_done:
1347                 mov [di],byte 0                 ; Null-terminate string
1348                 pop di
1349                 mov si,ISOFileName
1350 .compare:
1351                 lodsb
1352                 mov ah,[di]
1353                 inc di
1354                 and ax,ax
1355                 jz .success                     ; End of string for both
1356                 and al,al                       ; Is either one end of string?
1357                 jz .failure                     ; If so, failure
1358                 and ah,ah
1359                 jz .failure
1360                 or ax,2020h                     ; Convert to lower case
1361                 cmp al,ah
1362                 je .compare
1363 .failure:       and ax,ax                       ; ZF = 0 (at least one will be nonzero)
1364 .success:       ret
1365
1366 ;
1367 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1368 ;              to by ES:DI; ends on encountering any whitespace.
1369 ;
1370 ;              This verifies that a filename is < FILENAME_MAX characters,
1371 ;              doesn't contain whitespace, zero-pads the output buffer,
1372 ;              and removes trailing dots and redundant slashes,
1373 ;              so "repe cmpsb" can do a compare, and the
1374 ;              path-searching routine gets a bit of an easier job.
1375 ;              
1376 mangle_name:
1377                 push bx
1378                 xor ax,ax
1379                 mov cx,FILENAME_MAX-1
1380                 mov bx,di
1381
1382 .mn_loop:
1383                 lodsb
1384                 cmp al,' '                      ; If control or space, end
1385                 jna .mn_end
1386                 cmp al,ah                       ; Repeated slash?
1387                 je .mn_skip
1388                 xor ah,ah
1389                 cmp al,'/'
1390                 jne .mn_ok
1391                 mov ah,al
1392 .mn_ok          stosb
1393 .mn_skip:       loop .mn_loop
1394 .mn_end:
1395                 cmp bx,di                       ; At the beginning of the buffer?
1396                 jbe .mn_zero
1397                 cmp byte [di-1],'.'             ; Terminal dot?
1398                 je .mn_kill
1399                 cmp byte [di-1],'/'             ; Terminal slash?
1400                 jne .mn_zero
1401 .mn_kill:       dec di                          ; If so, remove it
1402                 inc cx
1403                 jmp short .mn_end
1404 .mn_zero:
1405                 inc cx                          ; At least one null byte
1406                 xor ax,ax                       ; Zero-fill name
1407                 rep stosb
1408                 pop bx
1409                 ret                             ; Done
1410
1411 ;
1412 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1413 ;                filename to the conventional representation.  This is needed
1414 ;                for the BOOT_IMAGE= parameter for the kernel.
1415 ;                NOTE: A 13-byte buffer is mandatory, even if the string is
1416 ;                known to be shorter.
1417 ;
1418 ;                DS:SI -> input mangled file name
1419 ;                ES:DI -> output buffer
1420 ;
1421 ;                On return, DI points to the first byte after the output name,
1422 ;                which is set to a null byte.
1423 ;
1424 unmangle_name:  call strcpy
1425                 dec di                          ; Point to final null byte
1426                 ret
1427
1428 ;
1429 ; getfssec: Get multiple clusters from a file, given the file pointer.
1430 ;
1431 ;  On entry:
1432 ;       ES:BX   -> Buffer
1433 ;       SI      -> File pointer
1434 ;       CX      -> Cluster count
1435 ;  On exit:
1436 ;       SI      -> File pointer (or 0 on EOF)
1437 ;       CF = 1  -> Hit EOF
1438 ;
1439 getfssec:
1440                 TRACER 'F'
1441
1442                 push ds
1443                 push cs
1444                 pop ds                          ; DS <- CS
1445
1446                 movzx ecx,cx
1447                 cmp ecx,[si+file_left]
1448                 jna .ok_size
1449                 mov ecx,[si+file_left]
1450 .ok_size:
1451
1452                 mov bp,cx
1453                 push cx
1454                 push si
1455                 mov eax,[si+file_sector]
1456                 TRACER 'l'
1457                 call getlinsec
1458                 xor ecx,ecx
1459                 pop si
1460                 pop cx
1461
1462                 add [si+file_sector],ecx
1463                 sub [si+file_left],ecx
1464                 ja .not_eof                     ; CF = 0
1465
1466                 xor ecx,ecx
1467                 mov [si+file_sector],ecx        ; Mark as unused
1468                 xor si,si
1469                 stc
1470
1471 .not_eof:
1472                 pop ds
1473                 TRACER 'f'
1474                 ret
1475
1476 ; -----------------------------------------------------------------------------
1477 ;  Common modules
1478 ; -----------------------------------------------------------------------------
1479
1480 %include "getc.inc"             ; getc et al
1481 %include "conio.inc"            ; Console I/O
1482 %include "parseconfig.inc"      ; High-level config file handling
1483 %include "parsecmd.inc"         ; Low-level config file handling
1484 %include "bcopy32.inc"          ; 32-bit bcopy
1485 %include "loadhigh.inc"         ; Load a file into high memory
1486 %include "font.inc"             ; VGA font stuff
1487 %include "graphics.inc"         ; VGA graphics
1488 %include "highmem.inc"          ; High memory sizing
1489 %include "strcpy.inc"           ; strcpy()
1490 %include "rawcon.inc"           ; Console I/O w/o using the console functions
1491
1492 ; -----------------------------------------------------------------------------
1493 ;  Begin data section
1494 ; -----------------------------------------------------------------------------
1495
1496                 section .data
1497
1498 boot_prompt     db 'boot: ', 0
1499 wipe_char       db BS, ' ', BS, 0
1500 err_notfound    db 'Could not find kernel image: ',0
1501 err_notkernel   db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1502 err_noram       db 'It appears your computer has less than '
1503                 asciidec dosram_k
1504                 db 'K of low ("DOS")'
1505                 db CR, LF
1506                 db 'RAM.  Linux needs at least this amount to boot.  If you get'
1507                 db CR, LF
1508                 db 'this message in error, hold down the Ctrl key while'
1509                 db CR, LF
1510                 db 'booting, and I will take your word for it.', CR, LF, 0
1511 err_badcfg      db 'Unknown keyword in config file.', CR, LF, 0
1512 err_noparm      db 'Missing parameter in config file.', CR, LF, 0
1513 err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
1514 err_nohighmem   db 'Not enough memory to load specified kernel.', CR, LF, 0
1515 err_highload    db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1516 err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
1517                 db CR, LF, 0
1518 err_notdos      db ': attempted DOS system call', CR, LF, 0
1519 err_comlarge    db 'COMBOOT image too large.', CR, LF, 0
1520 err_bssimage    db 'BSS images not supported.', CR, LF, 0
1521 err_a20         db CR, LF, 'A20 gate not responding!', CR, LF, 0
1522 notfound_msg    db 'not found', CR, LF, 0
1523 localboot_msg   db 'Booting from local disk...', CR, LF, 0
1524 cmdline_msg     db 'Command line: ', CR, LF, 0
1525 ready_msg       db 'Ready.', CR, LF, 0
1526 trying_msg      db 'Trying to load: ', 0
1527 crlfloading_msg db CR, LF                       ; Fall through
1528 loading_msg     db 'Loading ', 0
1529 dotdot_msg      db '.'
1530 dot_msg         db '.', 0
1531 fourbs_msg      db BS, BS, BS, BS, 0
1532 aborted_msg     db ' aborted.', CR, LF, 0
1533 crff_msg        db CR, FF, 0
1534 default_str     db 'default', 0
1535 default_len     equ ($-default_str)
1536 boot_dir        db '/boot'                      ; /boot/isolinux
1537 isolinux_dir    db '/isolinux', 0
1538 ConfigName      equ $
1539 isolinux_cfg    db 'isolinux.cfg', 0
1540 err_disk_image  db 'Cannot load disk image (invalid file)?', CR, LF, 0
1541
1542 %ifdef DEBUG_MESSAGES
1543 dbg_rootdir_msg db 'Root directory at LBA = ', 0
1544 dbg_isodir_msg  db 'isolinux directory at LBA = ', 0
1545 dbg_config_msg  db 'About to load config file...', CR, LF, 0
1546 dbg_configok_msg        db 'Configuration file opened...', CR, LF, 0
1547 %endif
1548 ;
1549 ; Command line options we'd like to take a look at
1550 ;
1551 ; mem= and vga= are handled as normal 32-bit integer values
1552 initrd_cmd      db 'initrd='
1553 initrd_cmd_len  equ 7
1554
1555 ;
1556 ; Config file keyword table
1557 ;
1558 %include "keywords.inc"
1559
1560 ;
1561 ; Extensions to search for (in *forward* order).
1562 ;
1563                 align 4, db 0
1564 exten_table:    db '.cbt'               ; COMBOOT (specific)
1565                 db '.img'               ; Disk image
1566                 db '.bin'               ; CD boot sector
1567                 db '.com'               ; COMBOOT (same as DOS)
1568                 db '.c32'               ; COM32
1569 exten_table_end:
1570                 dd 0, 0                 ; Need 8 null bytes here
1571
1572 ;
1573 ; Floppy image table
1574 ;
1575                 align 4, db 0
1576 img_table_count equ 3
1577 img_table:
1578                 dd 1200*1024            ; 1200K floppy
1579                 db 1                    ; Emulation type
1580                 db 80-1                 ; Max cylinder
1581                 db 15                   ; Max sector
1582                 db 2-1                  ; Max head
1583
1584                 dd 1440*1024            ; 1440K floppy
1585                 db 2                    ; Emulation type
1586                 db 80-1                 ; Max cylinder
1587                 db 18                   ; Max sector
1588                 db 2-1                  ; Max head
1589
1590                 dd 2880*1024            ; 2880K floppy
1591                 db 3                    ; Emulation type
1592                 db 80-1                 ; Max cylinder
1593                 db 36                   ; Max sector
1594                 db 2-1                  ; Max head
1595
1596 ;
1597 ; Misc initialized (data) variables
1598 ;
1599
1600 ;
1601 ; Spec packet for disk image emulation
1602 ;
1603                 align 8, db 0
1604 dspec_packet:   db 13h                          ; Size of packet
1605 dsp_media:      db 0                            ; Media type
1606 dsp_drive:      db 0                            ; Drive number
1607 dsp_controller: db 0                            ; Controller index
1608 dsp_lba:        dd 0                            ; LBA for emulated disk image
1609 dsp_devspec:    dw 0                            ; IDE/SCSI information
1610 dsp_buffer:     dw 0                            ; User-provided buffer
1611 dsp_loadseg:    dw 0                            ; Load segment
1612 dsp_sectors:    dw 1                            ; Sector count
1613 dsp_chs:        db 0,0,0                        ; Simulated CHS geometry
1614 dsp_dummy:      db 0                            ; Scratch, safe to overwrite
1615
1616 ;
1617 ; Variables that are uninitialized in SYSLINUX but initialized here
1618 ;
1619 ; **** ISOLINUX:: We may have to make this flexible, based on what the
1620 ; **** BIOS expects our "sector size" to be.
1621 ;
1622                 alignb 4, db 0
1623 BufSafe         dw trackbufsize/SECTOR_SIZE     ; Clusters we can load into trackbuf
1624 BufSafeSec      dw trackbufsize/SECTOR_SIZE     ; = how many sectors?
1625 BufSafeBytes    dw trackbufsize         ; = how many bytes?
1626 EndOfGetCBuf    dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1627 %ifndef DEPEND
1628 %if ( trackbufsize % SECTOR_SIZE ) != 0
1629 %error trackbufsize must be a multiple of SECTOR_SIZE
1630 %endif
1631 %endif
1632
1633 ldlinux_end     equ $