1 ; ****************************************************************************
5 ; A program to boot Linux kernels off a CD-ROM using the El Torito
6 ; boot standard in "no emulation" mode, making the entire filesystem
7 ; available. It is based on the SYSLINUX boot loader for MS-DOS
10 ; Copyright (C) 1994-2001 H. Peter Anvin
12 ; This program is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
15 ; USA; either version 2 of the License, or (at your option) any later
16 ; version; incorporated herein by reference.
18 ; ****************************************************************************
20 ; THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
21 ; MODIFICATION DONE BY MICHAEL K TER LOUW
22 ; LAST UPDATED 3-9-2002
23 ; SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
25 ; ****************************************************************************
27 ; This file is a modified version of ISOLINUX.ASM.
28 ; Modification done by Eric Kohl
29 ; Last update 04-25-2002
31 ; ****************************************************************************
33 ; Note: The Makefile builds one version with DEBUG_MESSAGES automatically.
34 ;%define DEBUG_MESSAGES ; Uncomment to get debugging messages
39 ; ---------------------------------------------------------------------------
40 ; BEGIN THE BIOS/CODE/DATA SEGMENT
41 ; ---------------------------------------------------------------------------
44 serial_base resw 4 ; Base addresses for 4 serial ports
46 BIOS_fbm resw 1 ; Free Base Memory (kilobytes)
48 BIOS_timer resw 1 ; Timer ticks
50 BIOS_magic resw 1 ; BIOS reset magic
52 BIOS_vidrows resb 1 ; Number of screen rows
55 ; Memory below this point is reserved for the BIOS and the MBR
58 trackbuf resb 8192 ; Track buffer goes here
59 trackbufsize equ $-trackbuf
60 ; trackbuf ends at 3000h
63 file_sector resd 1 ; Sector pointer (0 = structure free)
64 file_left resd 1 ; Number of sectors left
68 dir_lba resd 1 ; Directory start (LBA)
69 dir_len resd 1 ; Length in bytes
70 dir_clust resd 1 ; Length in clusters
74 MAX_OPEN_LG2 equ 2 ; log2(Max number of open files)
75 MAX_OPEN equ (1 << MAX_OPEN_LG2)
76 SECTORSIZE_LG2 equ 11 ; 2048 bytes/sector (El Torito requirement)
77 SECTORSIZE equ (1 << SECTORSIZE_LG2)
78 CR equ 13 ; Carriage Return
80 retry_count equ 6 ; How patient are we with the BIOS?
84 absolute 5000h ; Here we keep our BSS stuff
86 DriveNo resb 1 ; CD-ROM BIOS drive number
87 DiskError resb 1 ; Error code for disk I/O
88 RetryCount resb 1 ; Used for disk access retries
89 TimeoutCount resb 1 ; Timeout counter
90 ISOFlags resb 1 ; Flags for ISO directory search
91 RootDir resb dir_t_size ; Root directory
92 CurDir resb dir_t_size ; Current directory
93 ISOFileName resb 64 ; ISO filename canonicalization buffer
97 alignb open_file_t_size
98 Files resb MAX_OPEN*open_file_t_size
106 cli ; Disable interrupts
107 xor ax, ax ; ax = segment zero
108 mov ss, ax ; Initialize stack segment
109 mov sp, start ; Set up stack
110 mov ds, ax ; Initialize other segment registers
114 sti ; Enable interrupts
115 cld ; Increment pointers
117 mov cx, 2048 >> 2 ; Copy the bootsector
118 mov si, 0x7C00 ; from 0000:7C00
119 mov di, 0x7000 ; to 0000:7000
120 rep movsd ; copy the program
121 jmp 0:relocate ; jump into relocated code
124 ; Display the banner and copyright
125 %ifdef DEBUG_MESSAGES
126 mov si, isolinux_banner ; si points to hello message
127 call writestr ; display the message
133 ; Make sure the keyboard buffer is empty
135 call pollchar_and_empty
137 ; Check for MBR on harddisk
145 jc .boot_cdrom ; could not read hdd
148 mov ax, word [trackbuf]
150 je .boot_cdrom ; no boot sector found (hopefully there are no weird bootsectors which begin with 0)
153 ; Display the 'Press key' message and wait for a maximum of 5 seconds
155 mov si, presskey_msg ; si points to 'Press key' message
156 call writestr ; display the message
158 mov byte [TimeoutCount], 5
160 mov eax, [BIOS_timer] ; load current tick counter
164 call pollchar_and_empty
167 mov ebx, [BIOS_timer]
171 mov si, dot_msg ; print '.'
173 dec byte [TimeoutCount] ; decrement timeout counter
180 ; Boot first harddisk (drive 0x80)
205 ; Save and display the boot drive number
207 %ifdef DEBUG_MESSAGES
215 ; Now figure out what we're actually doing
216 ; Note: use passed-in DL value rather than 7Fh because
217 ; at least some BIOSes will get the wrong value otherwise
218 mov ax, 4B01h ; Get disk emulation status
222 jc near spec_query_failed ; Shouldn't happen (BIOS bug)
224 cmp [sp_drive], dl ; Should contain the drive number
225 jne near spec_query_failed
227 %ifdef DEBUG_MESSAGES
230 mov al, byte [sp_drive]
236 ; Get drive information
243 ; mov si, nosecsize_msg No use in reporting this
247 ; Check for the sector size (should be 2048, but
248 ; some BIOSes apparently think we're 512-byte media)
250 ; FIX: We need to check what the proper behaviour
251 ; is for getlinsec when the BIOS thinks the sector
252 ; size is 512!!! For that, we need such a BIOS, though...
253 %ifdef DEBUG_MESSAGES
263 ; Clear Files structures
266 mov cx, (MAX_OPEN*open_file_t_size)/4
271 ; Now, we need to sniff out the actual filesystem data structures.
272 ; mkisofs gave us a pointer to the primary volume descriptor
273 ; (which will be at 16 only for a single-session disk!); from the PVD
274 ; we should be able to find the rest of what we need to know.
277 mov eax, 16 ; Primary Volume Descriptor (sector 16)
281 mov eax, [trackbuf+156+2]
282 mov [RootDir+dir_lba],eax
283 mov [CurDir+dir_lba],eax
284 %ifdef DEBUG_MESSAGES
291 mov eax,[trackbuf+156+10]
292 mov [RootDir+dir_len],eax
293 mov [CurDir+dir_len],eax
294 %ifdef DEBUG_MESSAGES
301 shr eax,SECTORSIZE_LG2
302 mov [RootDir+dir_clust],eax
303 mov [CurDir+dir_clust],eax
304 %ifdef DEBUG_MESSAGES
311 ; Look for the "REACTOS" directory, and if found,
312 ; make it the current directory instead of the root
315 mov al,02h ; Search for a directory
323 mov [CurDir+dir_len],eax
324 mov eax,[si+file_left]
325 mov [CurDir+dir_clust],eax
326 xor eax,eax ; Free this file pointer entry
327 xchg eax,[si+file_sector]
328 mov [CurDir+dir_lba],eax
331 mov di, isolinux_bin ; di points to Isolinux filename
332 call searchdir ; look for the file
333 jnz .isolinux_opened ; got the file
334 mov si, no_isolinux_msg ; si points to error message
335 call writemsg ; display the message
336 jmp kaboom ; fail boot
339 mov di, si ; save file pointer
341 %ifdef DEBUG_MESSAGES
348 mov ecx, eax ; calculate sector count
355 %ifdef DEBUG_MESSAGES
363 mov bx, 0x8000 ; bx = load address
364 mov si, di ; restore file pointer
365 mov cx, 0xFFFF ; load the whole file
366 call getfssec ; get the whole file
368 %ifdef DEBUG_MESSAGES
374 mov dl, [DriveNo] ; dl = boot drive
375 mov dh, 0 ; dh = boot partition
376 push 0 ; push segment (0x0000)
377 mov eax, [0x8000 + 0xA8] ; load the RVA of the EntryPoint into eax
378 add eax, 0x8000 ; RVA -> VA
379 push ax ; push offset
380 retf ; Transfer control to ROSLDR
394 ; DX:AX or EAX = file length in bytes
400 ; searchdir_iso is a special entry point for ISOLINUX only. In addition
401 ; to the above, searchdir_iso passes a file flag mask in AL. This is useful
402 ; for searching for directories.
412 call allocate_file ; Temporary file structure for directory
418 cmp byte [di],'\' ; If filename begins with slash
420 inc di ; Skip leading slash
421 mov si,RootDir ; Reference root directory instead
423 mov eax,[si+dir_clust]
424 mov [bx+file_left],eax
426 mov [bx+file_sector],eax
438 mov [di-1],byte 0 ; Terminate at directory name
439 mov cl,02h ; Search for directory
443 push word .resume ; Where to "return" to
449 ; Get a chunk of the directory
453 mov cx,1 ; load one sector
458 movzx eax, byte [si] ; Length of directory entry
463 test cl, byte 8Eh ; Unwanted file attributes!
466 movzx cx, byte [si+32] ; File identifier length
467 add si, byte 33 ; File identifier offset
468 call iso_compare_names
472 sub edx, eax ; Decrease bytes left
474 add si, ax ; Advance pointer
477 ; Did we finish the buffer?
478 cmp si, trackbuf+trackbufsize
479 jb .compare ; No, keep going
481 jmp short .getsome ; Get some more directory
484 ; Advance to the beginning of next sector
485 lea ax, [si+SECTORSIZE-1]
486 and ax, ~(SECTORSIZE-1)
488 jmp short .not_file ; We still need to do length checks
491 %ifdef DEBUG_MESSAGES
496 xor eax, eax ; ZF = 1
497 mov [bx+file_sector], eax
502 mov eax, [si+2] ; Location of extent
503 mov [bx+file_sector], eax
504 mov eax, [si+10] ; Data length
506 add eax, SECTORSIZE-1
507 shr eax, SECTORSIZE_LG2
508 mov [bx+file_left], eax
518 ; We get here if we were only doing part of a lookup
519 ; This relies on the fact that .success returns bx == si
520 xchg edx, eax ; Directory length in edx
521 pop cx ; Old ISOFlags
522 pop di ; Next filename pointer
524 mov byte [di-1], '\' ; restore the backslash in the filename
526 mov [ISOFlags], cl ; Restore the flags
527 jz .failure ; Did we fail? If so fail for real!
528 jmp .look_for_slash ; Otherwise, next level
531 ; allocate_file: Allocate a file structure
544 cmp dword [bx], byte 0
546 add bx, open_file_t_size ; ZF = 0
548 ; ZF = 0 if we fell out of the loop
555 ; Compare the names DS:SI and DS:DI and report if they are
556 ; equal from an ISO 9660 perspective. SI is the name from
557 ; the filesystem; CX indicates its length, and ';' terminates.
558 ; DI is expected to end with a null.
560 ; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
563 ; First, terminate and canonicalize input filename
575 cmp di, ISOFileNameEnd-1 ; Guard against buffer overrun
580 cmp byte [di-1], '.' ; Remove terminal dots
585 mov [di], byte 0 ; Null-terminate string
593 jz .success ; End of string for both
594 and al, al ; Is either one end of string?
595 jz .failure ; If so, failure
598 or ax, 2020h ; Convert to lower case
602 and ax, ax ; ZF = 0 (at least one will be nonzero)
613 ; getfssec: Get multiple clusters from a file, given the file pointer.
618 ; CX -> Cluster count; 0FFFFh = until end of file
620 ; SI -> File pointer (or 0 on EOF)
624 cmp cx, [si+file_left]
626 mov cx, [si+file_left]
632 mov eax, [si+file_sector]
638 add [si+file_sector], ecx
639 sub [si+file_left], ecx
643 mov [si+file_sector], ecx ; Mark as unused
652 ; INT 13h, AX=4B01h, DL=<passed in value> failed.
653 ; Try to scan the entire 80h-FFh from the end.
663 mov byte [si], 13 ; Size of buffer
674 cmp byte [sp_drive], dl
677 ; Okay, good enough...
684 ; Award BIOS 4.51 apparently passes garbage in sp_drive,
685 ; but if this was the drive number originally passed in
686 ; DL then consider it "good enough"
688 cmp byte [DriveNo], dl
705 ; Information message (DS:SI) output
706 ; Prefix with "isolinux: "
719 ; crlf: Print a newline
726 ; writestr: write a null-terminated string to the console, saving
727 ; registers on entry.
745 ; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
752 jmp short writehex_common
758 jmp short writehex_common
784 ; Write a character to the screen. There is a more "sophisticated"
785 ; version of this in the subsequent code, so we patch the pointer
800 ; Get one sector. Convenience entry point.
804 ; Fall through to getlinsec
807 ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
809 ; Note that we can't always do this as a single request, because at least
810 ; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
811 ; to 32 sectors (64K) per request.
814 ; EAX - Linear sector number
815 ; ES:BX - Target buffer
819 mov si,dapa ; Load up the DAPA
825 push bp ; Sectors left
833 mov ah,42h ; Extended Read
837 movzx eax,word [si+2] ; Sectors we read
838 add [si+8],eax ; Advance sector pointer
839 sub bp,ax ; Sectors left
840 shl ax,SECTORSIZE_LG2-4 ; 2048-byte sectors -> segment
841 add [si+6],ax ; Advance buffer pointer
844 mov eax,[si+8] ; Next sector
849 mov byte [RetryCount], retry_count
854 add sp, byte 8*4 ; Clean up stack
857 mov [DiskError], ah ; Save error code
859 dec byte [RetryCount]
863 mov ah,[dapa+2] ; Sector transfer count
864 cmp al,2 ; Only 2 attempts left
866 mov ah,1 ; Drop transfer size to 1
870 ja .again ; First time, just try again
871 shr ah,1 ; Otherwise, try to reduce
872 adc ah,0 ; the max transfer size, but not to 0
890 ; Fall through to kaboom
893 ; kaboom: write a message and bail out. Wait for a user keypress,
894 ; then do a hard reboot.
903 mov si, err_bootfailed
905 xor ax, ax ; Wait for keypress
908 mov word [BIOS_magic], 0 ; Cold reboot
909 jmp 0F000h:0FFF0h ; Reset vector address
913 ; pollchar_and_empty: check if we have an input character pending (ZF = 0) and empty the input buffer afterwards
917 mov ah, 1 ; Did the user press a key?
919 jz .end ; No, then we're done
920 mov ah, 0 ; Otherwise empty the buffer by reading it
928 isolinux_banner db CR, LF, 'Loading IsoBoot...', CR, LF, 0
929 copyright_str db ' (C) 1994-2002 H. Peter Anvin', CR, LF, 0
930 presskey_msg db 'Press any key to boot from CD', 0
933 %ifdef DEBUG_MESSAGES
934 startup_msg: db 'Startup, DL = ', 0
935 spec_ok_msg: db 'packet OK, drive = ', 0
936 secsize_msg: db 'size appears to be ', 0
937 rootloc_msg: db 'Root dir loc: ', 0
938 rootlen_msg: db 'Root dir len: ', 0
939 rootsect_msg: db 'Root dir len(sect): ', 0
940 fileloc_msg: db 'SETUPLDR loc: ', 0
941 filelen_msg: db 'SETUPLDR len: ', 0
942 filesect_msg: db 'SETUPLDR len(sect): ', 0
943 findfail_msg: db 'Failed to find file!', 0
944 startldr_msg: db 'Starting SETUPLDR.SYS', 0
947 nosecsize_msg: db 'No sector size, assume 0800', CR, LF, 0
948 spec_err_msg: db 'Load spec failed, trying wing ...', CR, LF, 0
949 maybe_msg: db 'Found smth at drive = ', 0
950 alright_msg: db 'might be ok, continuing...', CR, LF, 0
951 nothing_msg: db 'Failed locate CD-ROM; boot failed.', CR, LF, 0
952 isolinux_str db 'IsoBoot: ', 0
953 crlf_msg db CR, LF, 0
954 diskerr_msg: db 'Disk error ', 0
955 ondrive_str: db ', drive ', 0
956 err_bootfailed db CR, LF, 'failed..', 0
957 isolinux_dir db '\LOADER', 0
958 no_dir_msg db 'LOADER dir not found.', CR, LF, 0
959 isolinux_bin db 'SETUPLDR.SYS', 0
960 no_isolinux_msg db 'SETUPLDR not found.', CR, LF, 0
963 ; El Torito spec packet
966 spec_packet: db 13h ; Size of packet
967 sp_media: db 0 ; Media type
968 sp_drive: db 0 ; Drive number
969 sp_controller: db 0 ; Controller index
970 sp_lba: dd 0 ; LBA for emulated disk image
971 sp_devspec: dw 0 ; IDE/SCSI information
972 sp_buffer: dw 0 ; User-provided buffer
973 sp_loadseg: dw 0 ; Load segment
974 sp_sectors: dw 0 ; Sector count
975 sp_chs: db 0,0,0 ; Simulated CHS geometry
976 sp_dummy: db 0 ; Scratch, safe to overwrite
979 ; EBIOS drive parameter packet
982 drive_params: dw 30 ; Buffer size
983 dp_flags: dw 0 ; Information flags
984 dp_cyl: dd 0 ; Physical cylinders
985 dp_head: dd 0 ; Physical heads
986 dp_sec: dd 0 ; Physical sectors/track
987 dp_totalsec: dd 0,0 ; Total sectors
988 dp_secsize: dw 0 ; Bytes per sector
989 dp_dpte: dd 0 ; Device Parameter Table
990 dp_dpi_key: dw 0 ; 0BEDDh if rest valid
991 dp_dpi_len: db 0 ; DPI len
994 dp_bus: times 4 db 0 ; Host bus type
995 dp_interface: times 8 db 0 ; Interface type
996 db_i_path: dd 0,0 ; Interface path
997 db_d_path: dd 0,0 ; Device path
999 db_dpi_csum: db 0 ; Checksum for DPI info
1002 ; EBIOS disk address packet
1005 dapa: dw 16 ; Packet size
1006 .count: dw 0 ; Block count
1007 .off: dw 0 ; Offset of buffer
1008 .seg: dw 0 ; Segment of buffer
1009 .lba: dd 0 ; LBA (LSW)
1013 MaxTransfer dw 2 ;32 ; Max sectors per transfer
1015 times 2046-($-$$) db 0 ; Pad to file offset 2046
1016 dw 0aa55h ; BootSector signature