1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id: isolinux.asm,v 1.113 2004/12/30 21:16:04 hpa Exp $
3 ; ****************************************************************************
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
12 ; Copyright (C) 1994-2004 H. Peter Anvin
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.
20 ; ****************************************************************************
27 %include "tracers.inc"
31 ; Some semi-configurable constants... change on your own risk.
34 FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
35 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
36 NULLFILE equ 0 ; Zero byte == null file name
37 NULLOFFSET equ 0 ; Position in which to look
38 retry_count equ 6 ; How patient are we with the BIOS?
39 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
40 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
41 MAX_OPEN equ (1 << MAX_OPEN_LG2)
42 SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement)
43 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
46 ; This is what we need to do when idle
56 ; The following structure is used for "virtual kernels"; i.e. LILO-style
57 ; option labels. The options we permit here are `kernel' and `append
58 ; Since there is no room in the bottom 64K for all of these, we
59 ; stick them at vk_seg:0000 and copy them down before we need them.
62 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
63 vk_rname: resb FILENAME_MAX ; Real name
66 vk_append: resb max_cmd_len+1 ; Command line
68 vk_end: equ $ ; Should be <= vk_size
72 ; Segment assignments in the bottom 640K
73 ; 0000h - main code/data segment (and BIOS segment)
75 real_mode_seg equ 3000h
76 vk_seg equ 2000h ; Virtual kernels
77 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
78 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
81 ; File structure. This holds the information for each currently open file.
84 file_sector resd 1 ; Sector pointer (0 = structure free)
85 file_left resd 1 ; Number of sectors left
89 %if (open_file_t_size & (open_file_t_size-1))
90 %error "open_file_t is not a power of 2"
95 dir_lba resd 1 ; Directory start (LBA)
96 dir_len resd 1 ; Length in bytes
97 dir_clust resd 1 ; Length in clusters
100 ; ---------------------------------------------------------------------------
102 ; ---------------------------------------------------------------------------
105 ; Memory below this point is reserved for the BIOS and the MBR
108 trackbufsize equ 8192
109 trackbuf resb trackbufsize ; Track buffer goes here
110 getcbuf resb trackbufsize
115 ISOFileName resb 64 ; ISO filename canonicalization buffer
117 CurDir resb dir_t_size ; Current directory
118 RootDir resb dir_t_size ; Root directory
119 FirstSecSum resd 1 ; Checksum of bytes 64-2048
120 ImageDwords resd 1 ; isolinux.bin size, dwords
121 InitStack resd 1 ; Initial stack pointer (SS:SP)
122 DiskSys resw 1 ; Last INT 13h call
123 ImageSectors resw 1 ; isolinux.bin size, sectors
124 DiskError resb 1 ; Error code for disk I/O
125 DriveNo resb 1 ; CD-ROM BIOS drive number
126 ISOFlags resb 1 ; Flags for ISO directory search
127 RetryCount resb 1 ; Used for disk access retries
132 ; El Torito spec packet
136 spec_packet: resb 1 ; Size of packet
137 sp_media: resb 1 ; Media type
138 sp_drive: resb 1 ; Drive number
139 sp_controller: resb 1 ; Controller index
140 sp_lba: resd 1 ; LBA for emulated disk image
141 sp_devspec: resw 1 ; IDE/SCSI information
142 sp_buffer: resw 1 ; User-provided buffer
143 sp_loadseg: resw 1 ; Load segment
144 sp_sectors: resw 1 ; Sector count
145 sp_chs: resb 3 ; Simulated CHS geometry
146 sp_dummy: resb 1 ; Scratch, safe to overwrite
149 ; EBIOS drive parameter packet
152 drive_params: resw 1 ; Buffer size
153 dp_flags: resw 1 ; Information flags
154 dp_cyl: resd 1 ; Physical cylinders
155 dp_head: resd 1 ; Physical heads
156 dp_sec: resd 1 ; Physical sectors/track
157 dp_totalsec: resd 2 ; Total sectors
158 dp_secsize: resw 1 ; Bytes per sector
159 dp_dpte: resd 1 ; Device Parameter Table
160 dp_dpi_key: resw 1 ; 0BEDDh if rest valid
161 dp_dpi_len: resb 1 ; DPI len
164 dp_bus: resb 4 ; Host bus type
165 dp_interface: resb 8 ; Interface type
166 db_i_path: resd 2 ; Interface path
167 db_d_path: resd 2 ; Device path
169 db_dpi_csum: resb 1 ; Checksum for DPI info
172 ; EBIOS disk address packet
175 dapa: resw 1 ; Packet size
176 .count: resw 1 ; Block count
177 .off: resw 1 ; Offset of buffer
178 .seg: resw 1 ; Segment of buffer
179 .lba: resd 2 ; LBA (LSW, MSW)
182 ; Spec packet for disk image emulation
185 dspec_packet: resb 1 ; Size of packet
186 dsp_media: resb 1 ; Media type
187 dsp_drive: resb 1 ; Drive number
188 dsp_controller: resb 1 ; Controller index
189 dsp_lba: resd 1 ; LBA for emulated disk image
190 dsp_devspec: resw 1 ; IDE/SCSI information
191 dsp_buffer: resw 1 ; User-provided buffer
192 dsp_loadseg: resw 1 ; Load segment
193 dsp_sectors: resw 1 ; Sector count
194 dsp_chs: resb 3 ; Simulated CHS geometry
195 dsp_dummy: resb 1 ; Scratch, safe to overwrite
199 _spec_len equ _spec_end - _spec_start
201 alignb open_file_t_size
202 Files resb MAX_OPEN*open_file_t_size
205 ; Constants for the xfer_buf_seg
207 ; The xfer_buf_seg is also used to store message file buffers. We
208 ; need two trackbuffers (text and graphics), plus a work buffer
209 ; for the graphics decompressor.
211 xbs_textbuf equ 0 ; Also hard-coded, do not change
212 xbs_vgabuf equ trackbufsize
213 xbs_vgatmpbuf equ 2*trackbufsize
217 ;; Primary entry point. Because BIOSes are buggy, we only load the first
218 ;; CD-ROM sector (2K) of the file, so the number one priority is actually
225 _start: ; Far jump makes sure we canonicalize the address
228 times 8-($-$$) nop ; Pad to file offset 8
230 ; This table hopefully gets filled in by mkisofs using the
231 ; -boot-info-table option. If not, the values in this
232 ; table are default values that we can use to get us what
233 ; we need, at least under a certain set of assumptions.
234 bi_pvd: dd 16 ; LBA of primary volume descriptor
235 bi_file: dd 0 ; LBA of boot file
236 bi_length: dd 0xdeadbeef ; Length of boot file
237 bi_csum: dd 0xdeadbeef ; Checksum of boot file
238 bi_reserved: times 10 dd 0xdeadbeef ; Reserved
240 _start1: mov [cs:InitStack],sp ; Save initial stack pointer
241 mov [cs:InitStack+2],ss
244 mov sp,StackBuf ; Set up stack
253 mov si,syslinux_banner
255 %ifdef DEBUG_MESSAGES
261 ; Before modifying any memory, get the checksum of bytes
264 initial_csum: xor edi,edi
266 mov cx,(SECTOR_SIZE-64) >> 2
270 mov [FirstSecSum],edi
273 %ifdef DEBUG_MESSAGES
281 ; Initialize spec packet buffers
284 mov cx,_spec_len >> 2
288 ; Initialize length field of the various packets
289 mov byte [spec_packet],13h
290 mov byte [drive_params],30
292 mov byte [dspec_packet],13h
294 ; Other nonzero fields
295 inc word [dsp_sectors]
297 ; Now figure out what we're actually doing
298 ; Note: use passed-in DL value rather than 7Fh because
299 ; at least some BIOSes will get the wrong value otherwise
300 mov ax,4B01h ; Get disk emulation status
304 jc award_hack ; changed for BrokenAwardHack
306 cmp [sp_drive],dl ; Should contain the drive number
307 jne spec_query_failed
309 %ifdef DEBUG_MESSAGES
312 mov al,byte [sp_drive]
318 ; Alright, we have found the drive. Now, try to find the
319 ; boot file itself. If we have a boot info table, life is
320 ; good; if not, we have to make some assumptions, and try
321 ; to figure things out ourselves. In particular, the
322 ; assumptions we have to make are:
323 ; - single session only
324 ; - only one boot entry (no menu or other alternatives)
326 cmp dword [bi_file],0 ; Address of code to load
327 jne found_file ; Boot info table present :)
329 %ifdef DEBUG_MESSAGES
330 mov si,noinfotable_msg
334 ; No such luck. See if the the spec packet contained one.
337 jz set_file ; Good enough
339 %ifdef DEBUG_MESSAGES
340 mov si,noinfoinspec_msg
344 ; No such luck. Get the Boot Record Volume, assuming single
345 ; session disk, and that we're the first entry in the chain
346 mov eax,17 ; Assumed address of BRV
350 mov eax,[trackbuf+47h] ; Get boot catalog address
352 call getonesec ; Get boot catalog
354 mov eax,[trackbuf+28h] ; First boot entry
355 ; And hope and pray this is us...
357 ; Some BIOSes apparently have limitations on the size
358 ; that may be loaded (despite the El Torito spec being very
359 ; clear on the fact that it must all be loaded.) Therefore,
360 ; we load it ourselves, and *bleep* the BIOS.
366 ; Set up boot file sizes
368 sub eax,SECTOR_SIZE-3
369 shr eax,2 ; bytes->dwords
370 mov [ImageDwords],eax ; boot file dwords
372 shr eax,9 ; dwords->sectors
373 mov [ImageSectors],ax ; boot file sectors
375 mov eax,[bi_file] ; Address of code to load
376 inc eax ; Don't reload bootstrap code
377 %ifdef DEBUG_MESSAGES
384 ; Just in case some BIOSes have problems with
385 ; segment wraparound, use the normalized address
386 mov bx,((7C00h+2048) >> 4)
389 mov bp,[ImageSectors]
390 %ifdef DEBUG_MESSAGES
404 %ifdef DEBUG_MESSAGES
409 ; Verify the checksum on the loaded image.
413 mov ecx,[ImageDwords]
414 mov edi,[FirstSecSum] ; First sector checksum
421 ; SI wrapped around, advance ES
435 %ifdef DEBUG_MESSAGES
439 jmp all_read ; Jump to main code
441 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
442 ;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
443 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
445 ;; There is a problem with certain versions of the AWARD BIOS ...
446 ;; the boot sector will be loaded and executed correctly, but, because the
447 ;; int 13 vector points to the wrong code in the BIOS, every attempt to
448 ;; load the spec packet will fail. We scan for the equivalent of
457 ;; and use <direct far> as the new vector for int 13. The code above is
458 ;; used to load the boot code into ram, and there should be no reason
459 ;; for anybody to change it now or in the future. There are no opcodes
460 ;; that use encodings relativ to IP, so scanning is easy. If we find the
461 ;; code above in the BIOS code we can be pretty sure to run on a machine
462 ;; with an broken AWARD BIOS ...
464 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
466 %ifdef DEBUG_MESSAGES ;;
468 award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;;
469 award_not_orig db "BAH: Original Int 13 vector : ",0 ;;
470 award_not_new db "BAH: Int 13 vector changed to : ",0 ;;
471 award_not_succ db "BAH: SUCCESS",CR,LF,0 ;;
472 award_not_fail db "BAH: FAILURE" ;;
473 award_not_crlf db CR,LF,0 ;;
477 award_oldint13 dd 0 ;;
478 award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;;
480 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
481 award_hack: mov si,spec_err_msg ; Moved to this place from
482 call writemsg ; spec_query_faild
484 %ifdef DEBUG_MESSAGES ;
486 mov si,award_notice ; display our plan
488 mov si,award_not_orig ; display original int 13
489 call writemsg ; vector
492 mov [award_oldint13],eax ;
494 %ifdef DEBUG_MESSAGES ;
497 mov si,award_not_crlf ;
501 mov ax,0f000h ; ES = BIOS Seg
504 xor di,di ; start at ES:DI = f000:0
505 award_loop: push di ; save DI
506 mov si,award_string ; scan for award_string
507 mov cx,7 ; length of award_string = 7dw
510 jcxz award_found ; jmp if found
511 inc di ; not found, inc di
514 award_failed: pop es ; No, not this way :-((
517 %ifdef DEBUG_MESSAGES ;
519 mov si,award_not_fail ; display failure ...
522 mov eax,[award_oldint13] ; restore the original int
523 or eax,eax ; 13 vector if there is one
524 jz spec_query_failed ; and try other workarounds
526 jmp spec_query_failed ;
528 award_found: mov eax,[es:di+0eh] ; load possible int 13 addr
531 cmp eax,[award_oldint13] ; give up if this is the
532 jz award_failed ; active int 13 vector,
533 mov [13h*4],eax ; otherwise change 0:13h*4
536 %ifdef DEBUG_MESSAGES ;
538 push eax ; display message and
539 mov si,award_not_new ; new vector address
543 mov si,award_not_crlf ;
546 mov ax,4B01h ; try to read the spec packet
547 mov dl,[DriveNo] ; now ... it should not fail
548 mov si,spec_packet ; any longer
552 %ifdef DEBUG_MESSAGES ;
554 mov si,award_not_succ ; display our SUCCESS
557 jmp found_drive ; and leave error recovery code
559 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
560 ;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
561 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
564 ; INT 13h, AX=4B01h, DL=<passed in value> failed.
565 ; Try to scan the entire 80h-FFh from the end.
569 ; some code moved to BrokenAwardHack
575 mov byte [si],13 ; Size of buffer
586 cmp byte [sp_drive],dl
589 ; Okay, good enough...
593 .found_drive: jmp found_drive
595 ; Award BIOS 4.51 apparently passes garbage in sp_drive,
596 ; but if this was the drive number originally passed in
597 ; DL then consider it "good enough"
599 cmp byte [DriveNo],dl
602 .still_broken: dec dx
606 ; No spec packet anywhere. Some particularly pathetic
607 ; BIOSes apparently don't even implement function
608 ; 4B01h, so we can't query a spec packet no matter
609 ; what. If we got a drive number in DL, then try to
610 ; use it, and if it works, then well...
612 cmp dl,81h ; Should be 81-FF at least
613 jb fatal_error ; If not, it's hopeless
615 ; Write a warning to indicate we're on *very* thin ice now
623 jmp .found_drive ; Pray that this works...
629 .norge: jmp short .norge
631 ; Information message (DS:SI) output
632 ; Prefix with "isolinux: "
644 ; Write a character to the screen. There is a more "sophisticated"
645 ; version of this in the subsequent code, so we patch the pointer
650 jmp near writechr_simple ; 3-byte jump
663 ; Get one sector. Convenience entry point.
667 ; Fall through to getlinsec
670 ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
672 ; Note that we can't always do this as a single request, because at least
673 ; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
674 ; to 32 sectors (64K) per request.
677 ; EAX - Linear sector number
678 ; ES:BX - Target buffer
682 mov si,dapa ; Load up the DAPA
688 push bp ; Sectors left
696 mov ah,42h ; Extended Read
700 movzx eax,word [si+2] ; Sectors we read
701 add [si+8],eax ; Advance sector pointer
702 sub bp,ax ; Sectors left
703 shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment
704 add [si+6],ax ; Advance buffer pointer
707 mov eax,[si+8] ; Next sector
711 xint13: mov byte [RetryCount],retry_count
715 add sp,byte 8*4 ; Clean up stack
718 mov [DiskError],ah ; Save error code
720 mov [DiskSys],ax ; Save system call number
721 dec byte [RetryCount]
725 mov ah,[dapa+2] ; Sector transfer count
726 cmp al,2 ; Only 2 attempts left
728 mov ah,1 ; Drop transfer size to 1
732 ja .again ; First time, just try again
733 shr ah,1 ; Otherwise, try to reduce
734 adc ah,0 ; the max transfer size, but not to 0
742 .real_error: mov si,diskerr_msg
755 ; Fall through to kaboom
758 ; kaboom: write a message and bail out. Wait for a user keypress,
759 ; then do a hard reboot.
769 mov si,err_bootfailed
773 mov word [BIOS_magic],0 ; Cold reboot
774 jmp 0F000h:0FFF0h ; Reset vector address
776 ; -----------------------------------------------------------------------------
777 ; Common modules needed in the first sector
778 ; -----------------------------------------------------------------------------
780 %include "writestr.inc" ; String output
781 writestr equ cwritestr
782 %include "writehex.inc" ; Hexadecimal output
784 ; -----------------------------------------------------------------------------
785 ; Data that needs to be in the first sector
786 ; -----------------------------------------------------------------------------
788 syslinux_banner db CR, LF, 'ISOLINUX ', version_str, ' ', date, ' ', 0
789 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
791 isolinux_str db 'isolinux: ', 0
792 %ifdef DEBUG_MESSAGES
793 startup_msg: db 'Starting up, DL = ', 0
794 spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0
795 secsize_msg: db 'Sector size appears to be ', 0
796 offset_msg: db 'Loading main image from LBA = ', 0
797 size_msg: db 'Sectors to load = ', 0
798 loaded_msg: db 'Loaded boot image, verifying...', CR, LF, 0
799 verify_msg: db 'Image checksum verified.', CR, LF, 0
800 allread_msg db 'Main image read, jumping to main code...', CR, LF, 0
802 noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0
803 noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0
804 spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
805 maybe_msg: db 'Found something at drive = ', 0
806 alright_msg: db 'Looks like it might be right, continuing...', CR, LF, 0
807 nospec_msg db 'Extremely broken BIOS detected, last ditch attempt with drive = ', 0
808 nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF
809 trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0
810 diskerr_msg: db 'Disk error ', 0
811 oncall_str: db ', AX = ',0
812 ondrive_str: db ', drive ', 0
813 checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0
815 err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'
816 bailmsg equ err_bootfailed
821 Stack dw _start, 0 ; SS:SP for stack reset
822 MaxTransfer dw 32 ; Max sectors per transfer
824 rl_checkpt equ $ ; Must be <= 800h
826 rl_checkpt_off equ ($-$$)
828 ;%if rl_checkpt_off > 0x800
829 ;%error "Sector 0 overflow"
833 ; ----------------------------------------------------------------------------
834 ; End of code and data that have to be in the first sector
835 ; ----------------------------------------------------------------------------
839 ; Initialize screen (if we're using one)
841 ; Now set up screen parameters
844 ; Wipe the F-key area
847 mov cx,10*(1 << FILENAME_MAX_LG2)
850 ; Patch the writechr routine to point to the full code
851 mov word [writechr+1], writechr_full-(writechr+3)
853 ; Tell the user we got this far...
854 %ifndef DEBUG_MESSAGES ; Gets messy with debugging on
864 ; Common initialization code
867 %include "cpuinit.inc"
870 ; Now we're all set to start with our *real* business. First load the
871 ; configuration file (if any) and parse it.
873 ; In previous versions I avoided using 32-bit registers because of a
874 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
875 ; random. I figure, though, that if there are any of those still left
876 ; they probably won't be trying to install Linux on them...
878 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
879 ; to take'm out. In fact, we may want to put them back if we're going
880 ; to boot ELKS at some point.
884 ; Now, we need to sniff out the actual filesystem data structures.
885 ; mkisofs gave us a pointer to the primary volume descriptor
886 ; (which will be at 16 only for a single-session disk!); from the PVD
887 ; we should be able to find the rest of what we need to know.
894 mov eax,[trackbuf+156+2]
895 mov [RootDir+dir_lba],eax
896 mov [CurDir+dir_lba],eax
897 %ifdef DEBUG_MESSAGES
898 mov si,dbg_rootdir_msg
903 mov eax,[trackbuf+156+10]
904 mov [RootDir+dir_len],eax
905 mov [CurDir+dir_len],eax
906 add eax,SECTOR_SIZE-1
908 mov [RootDir+dir_clust],eax
909 mov [CurDir+dir_clust],eax
911 ; Look for an isolinux directory, and if found,
912 ; make it the current directory instead of the root
914 mov di,boot_dir ; Search for /boot/isolinux
919 mov al,02h ; Search for /isolinux
923 mov [CurDir+dir_len],eax
924 mov eax,[si+file_left]
925 mov [CurDir+dir_clust],eax
926 xor eax,eax ; Free this file pointer entry
927 xchg eax,[si+file_sector]
928 mov [CurDir+dir_lba],eax
929 %ifdef DEBUG_MESSAGES
931 mov si,dbg_isodir_msg
940 ; Locate the configuration file
943 %ifdef DEBUG_MESSAGES
944 mov si,dbg_config_msg
950 jz no_config_file ; Not found or empty
952 %ifdef DEBUG_MESSAGES
953 mov si,dbg_configok_msg
958 ; Now we have the config file open. Parse the config file and
959 ; run the user interface.
964 ; Linux kernel loading code is common.
966 %include "runkernel.inc"
969 ; COMBOOT-loading code
971 %include "comboot.inc"
973 %include "cmdline.inc"
976 ; Boot sector loading code
978 %include "bootsect.inc"
981 ; Enable disk emulation. The kind of disk we emulate is dependent on the size of
982 ; the file: 1200K, 1440K or 2880K floppy, otherwise harddisk.
991 mov dx,ax ; Set EDX <- file size
993 mov cx,img_table_count
994 mov eax,[si+file_sector] ; Starting LBA of file
995 mov [dsp_lba],eax ; Location of file
996 mov byte [dsp_drive], 0 ; 00h floppy, 80h hard disk
1005 ; Hard disk image. Need to examine the partition table
1006 ; in order to deduce the C/H/S geometry. Sigh.
1013 mov cx,1 ; Load 1 sector
1016 cmp word [trackbuf+510],0aa55h ; Boot signature
1017 jne .bad_image ; Image not bootable
1019 mov cx,4 ; 4 partition entries
1020 mov di,trackbuf+446 ; Start of partition table
1022 xor ax,ax ; Highest sector(al) head(ah)
1036 push edx ; File size
1039 inc bx ; # of heads in BX
1040 xor ah,ah ; # of sectors in AX
1041 cwde ; EAX[31:16] <- 0
1043 shl eax,9 ; Convert to bytes
1044 ; Now eax contains the number of bytes per cylinder
1050 inc eax ; Fractional cylinder...
1051 ; Now (e)ax contains the number of cylinders
1052 .no_remainder: cmp eax,1024
1054 mov ax,1024 ; Max possible #
1055 .ok_cyl: dec ax ; Convert to max cylinder no
1056 pop ebx ; S(bl) H(bh)
1062 mov al,4 ; Hard disk boot
1063 mov byte [dsp_drive], 80h ; Drive 80h = hard disk
1068 and bl,0F0h ; Copy controller info bits
1070 mov [dsp_media],al ; Emulation type
1072 mov [dsp_chs],eax ; C/H/S geometry
1073 mov ax,[sp_devspec] ; Copy device spec
1074 mov [dsp_devspec],ax
1075 mov al,[sp_controller] ; Copy controller index
1076 mov [dsp_controller],al
1079 call vgaclearmode ; Reset video
1081 mov ax,4C00h ; Enable emulation and boot
1089 ; If this returns, we have problems
1091 mov si,err_disk_image
1096 ; Look for the highest seen H/S geometry
1097 ; We compute cylinders separately
1100 mov bl,[si] ; Head #
1103 mov ah,bl ; New highest head #
1104 .done_track: mov bl,[si+1]
1105 and bl,3Fh ; Sector #
1112 ; Boot a specified local disk. AX specifies the BIOS disk number; or
1113 ; 0xFFFF in case we should execute INT 18h ("next device.")
1117 lss sp,[cs:Stack] ; Restore stack pointer
1123 mov si,localboot_msg
1128 ; Load boot sector from the specified BIOS device and jump to it.
1132 xor ax,ax ; Reset drive
1134 mov ax,0201h ; Read one sector
1135 mov cx,0001h ; C/H/S = 0/0/1 (first sector)
1139 cli ; Abandon hope, ye who enter here
1142 mov cx,512 ; Probably overkill, but should be safe
1144 lss sp,[cs:InitStack]
1145 jmp 0:07C00h ; Jump to new boot sector
1148 int 18h ; Hope this does the right thing...
1149 jmp kaboom ; If we returned, oh boy...
1152 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1163 ac_kill: mov si,aborted_msg
1166 ; abort_load: Called by various routines which wants to print a fatal
1167 ; error message and return to the command prompt. Since this
1168 ; may happen at just about any stage of the boot process, assume
1169 ; our state is messed up, and just reset the segment registers
1170 ; and the stack forcibly.
1172 ; SI = offset (in _text) of error message to print
1175 mov ax,cs ; Restore CS = DS = ES
1179 lss sp,[cs:Stack] ; Reset the stack
1181 call cwritestr ; Expects SI -> error msg
1182 al_ok: jmp enter_command ; Return to command prompt
1184 ; End of abort_check
1200 ; DX:AX or EAX = file length in bytes
1206 ; searchdir_iso is a special entry point for ISOLINUX only. In addition
1207 ; to the above, searchdir_iso passes a file flag mask in AL. This is useful
1208 ; for searching for directories.
1219 call allocate_file ; Temporary file structure for directory
1225 cmp byte [di],'/' ; If filename begins with slash
1227 inc di ; Skip leading slash
1228 mov si,RootDir ; Reference root directory instead
1230 mov eax,[si+dir_clust]
1231 mov [bx+file_left],eax
1232 mov eax,[si+dir_lba]
1233 mov [bx+file_sector],eax
1234 mov edx,[si+dir_len]
1245 mov [di-1],byte 0 ; Terminate at directory name
1246 mov cl,02h ; Search for directory
1249 push di ; Save these...
1252 ; Create recursion stack frame...
1253 push word .resume ; Where to "return" to
1258 ; Get a chunk of the directory
1259 ; This relies on the fact that ISOLINUX doesn't change SI
1269 movzx eax,byte [si] ; Length of directory entry
1275 test cl, byte 8Eh ; Unwanted file attributes!
1278 movzx cx,byte [si+32] ; File identifier length
1279 add si,byte 33 ; File identifier offset
1281 call iso_compare_names
1285 sub edx,eax ; Decrease bytes left
1287 add si,ax ; Advance pointer
1290 ; Did we finish the buffer?
1291 cmp si,trackbuf+trackbufsize
1292 jb .compare ; No, keep going
1294 jmp short .getsome ; Get some more directory
1297 ; Advance to the beginning of next sector
1298 lea ax,[si+SECTOR_SIZE-1]
1299 and ax,~(SECTOR_SIZE-1)
1301 jmp short .not_file ; We still need to do length checks
1303 .failure: xor eax,eax ; ZF = 1
1304 mov [bx+file_sector],eax
1309 mov eax,[si+2] ; Location of extent
1310 mov [bx+file_sector],eax
1311 mov eax,[si+10] ; Data length
1313 add eax,SECTOR_SIZE-1
1314 shr eax,SECTOR_SHIFT
1315 mov [bx+file_left],eax
1324 .resume: ; We get here if we were only doing part of a lookup
1325 ; This relies on the fact that .success returns bx == si
1326 xchg edx,eax ; Directory length in edx
1327 pop cx ; Old ISOFlags
1328 pop di ; Next filename pointer
1329 mov byte [di-1], '/' ; Restore slash
1330 mov [ISOFlags],cl ; Restore the flags
1331 jz .failure ; Did we fail? If so fail for real!
1332 jmp .look_for_slash ; Otherwise, next level
1335 ; allocate_file: Allocate a file structure
1348 .check: cmp dword [bx], byte 0
1350 add bx,open_file_t_size ; ZF = 0
1352 ; ZF = 0 if we fell out of the loop
1357 ; iso_compare_names:
1358 ; Compare the names DS:SI and DS:DI and report if they are
1359 ; equal from an ISO 9660 perspective. SI is the name from
1360 ; the filesystem; CX indicates its length, and ';' terminates.
1361 ; DI is expected to end with a null.
1363 ; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
1367 ; First, terminate and canonicalize input filename
1370 .canon_loop: jcxz .canon_end
1378 cmp di,ISOFileNameEnd-1 ; Guard against buffer overrun
1383 cmp byte [di-1],'.' ; Remove terminal dots
1386 jmp short .canon_end
1388 mov [di],byte 0 ; Null-terminate string
1396 jz .success ; End of string for both
1397 and al,al ; Is either one end of string?
1398 jz .failure ; If so, failure
1401 or ax,2020h ; Convert to lower case
1404 .failure: and ax,ax ; ZF = 0 (at least one will be nonzero)
1408 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1409 ; to by ES:DI; ends on encountering any whitespace.
1411 ; This verifies that a filename is < FILENAME_MAX characters,
1412 ; doesn't contain whitespace, zero-pads the output buffer,
1413 ; and removes trailing dots and redundant slashes,
1414 ; so "repe cmpsb" can do a compare, and the
1415 ; path-searching routine gets a bit of an easier job.
1420 mov cx,FILENAME_MAX-1
1425 cmp al,' ' ; If control or space, end
1427 cmp al,ah ; Repeated slash?
1434 .mn_skip: loop .mn_loop
1436 cmp bx,di ; At the beginning of the buffer?
1438 cmp byte [di-1],'.' ; Terminal dot?
1440 cmp byte [di-1],'/' ; Terminal slash?
1442 .mn_kill: dec di ; If so, remove it
1446 inc cx ; At least one null byte
1447 xor ax,ax ; Zero-fill name
1453 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1454 ; filename to the conventional representation. This is needed
1455 ; for the BOOT_IMAGE= parameter for the kernel.
1456 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1457 ; known to be shorter.
1459 ; DS:SI -> input mangled file name
1460 ; ES:DI -> output buffer
1462 ; On return, DI points to the first byte after the output name,
1463 ; which is set to a null byte.
1465 unmangle_name: call strcpy
1466 dec di ; Point to final null byte
1470 ; getfssec: Get multiple clusters from a file, given the file pointer.
1474 ; SI -> File pointer
1475 ; CX -> Cluster count
1477 ; SI -> File pointer (or 0 on EOF)
1488 cmp ecx,[si+file_left]
1490 mov ecx,[si+file_left]
1496 mov eax,[si+file_sector]
1503 add [si+file_sector],ecx
1504 sub [si+file_left],ecx
1505 ja .not_eof ; CF = 0
1508 mov [si+file_sector],ecx ; Mark as unused
1517 ; -----------------------------------------------------------------------------
1519 ; -----------------------------------------------------------------------------
1521 %include "getc.inc" ; getc et al
1522 %include "conio.inc" ; Console I/O
1523 %include "parseconfig.inc" ; High-level config file handling
1524 %include "parsecmd.inc" ; Low-level config file handling
1525 %include "bcopy32.inc" ; 32-bit bcopy
1526 %include "loadhigh.inc" ; Load a file into high memory
1527 %include "font.inc" ; VGA font stuff
1528 %include "graphics.inc" ; VGA graphics
1529 %include "highmem.inc" ; High memory sizing
1530 %include "strcpy.inc" ; strcpy()
1531 %include "rawcon.inc" ; Console I/O w/o using the console functions
1533 ; -----------------------------------------------------------------------------
1534 ; Begin data section
1535 ; -----------------------------------------------------------------------------
1539 boot_prompt db 'boot: ', 0
1540 wipe_char db BS, ' ', BS, 0
1541 err_notfound db 'Could not find kernel image: ',0
1542 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1543 err_noram db 'It appears your computer has less than '
1545 db 'K of low ("DOS")'
1547 db 'RAM. Linux needs at least this amount to boot. If you get'
1549 db 'this message in error, hold down the Ctrl key while'
1551 db 'booting, and I will take your word for it.', CR, LF, 0
1552 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
1553 err_noparm db 'Missing parameter in config file.', CR, LF, 0
1554 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1555 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1556 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1557 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1559 err_notdos db ': attempted DOS system call', CR, LF, 0
1560 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1561 err_bssimage db 'BSS images not supported.', CR, LF, 0
1562 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1563 notfound_msg db 'not found', CR, LF, 0
1564 localboot_msg db 'Booting from local disk...', CR, LF, 0
1565 cmdline_msg db 'Command line: ', CR, LF, 0
1566 ready_msg db 'Ready.', CR, LF, 0
1567 trying_msg db 'Trying to load: ', 0
1568 crlfloading_msg db CR, LF ; Fall through
1569 loading_msg db 'Loading ', 0
1572 fourbs_msg db BS, BS, BS, BS, 0
1573 aborted_msg db ' aborted.', CR, LF, 0
1574 crff_msg db CR, FF, 0
1575 default_str db 'default', 0
1576 default_len equ ($-default_str)
1577 boot_dir db '/boot' ; /boot/isolinux
1578 isolinux_dir db '/isolinux', 0
1580 isolinux_cfg db 'isolinux.cfg', 0
1581 err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
1583 %ifdef DEBUG_MESSAGES
1584 dbg_rootdir_msg db 'Root directory at LBA = ', 0
1585 dbg_isodir_msg db 'isolinux directory at LBA = ', 0
1586 dbg_config_msg db 'About to load config file...', CR, LF, 0
1587 dbg_configok_msg db 'Configuration file opened...', CR, LF, 0
1590 ; Command line options we'd like to take a look at
1592 ; mem= and vga= are handled as normal 32-bit integer values
1593 initrd_cmd db 'initrd='
1594 initrd_cmd_len equ 7
1597 ; Config file keyword table
1599 %include "keywords.inc"
1602 ; Extensions to search for (in *forward* order).
1605 exten_table: db '.cbt' ; COMBOOT (specific)
1606 db '.img' ; Disk image
1607 db '.bin' ; CD boot sector
1608 db '.com' ; COMBOOT (same as DOS)
1611 dd 0, 0 ; Need 8 null bytes here
1614 ; Floppy image table
1617 img_table_count equ 3
1619 dd 1200*1024 ; 1200K floppy
1620 db 1 ; Emulation type
1621 db 80-1 ; Max cylinder
1625 dd 1440*1024 ; 1440K floppy
1626 db 2 ; Emulation type
1627 db 80-1 ; Max cylinder
1631 dd 2880*1024 ; 2880K floppy
1632 db 3 ; Emulation type
1633 db 80-1 ; Max cylinder
1638 ; Misc initialized (data) variables
1642 ; Variables that are uninitialized in SYSLINUX but initialized here
1644 ; **** ISOLINUX:: We may have to make this flexible, based on what the
1645 ; **** BIOS expects our "sector size" to be.
1648 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1649 BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors?
1650 BufSafeBytes dw trackbufsize ; = how many bytes?
1651 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1653 %if ( trackbufsize % SECTOR_SIZE ) != 0
1654 %error trackbufsize must be a multiple of SECTOR_SIZE