2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Bootsector for ISO file system
4 * FILE: boot/freeldr/bootsect/isoboot.S
9 /* INCLUDES ******************************************************************/
12 #include <freeldr/include/arch/pc/x86common.h>
16 // ****************************************************************************
20 // A program to boot Linux kernels off a CD-ROM using the El Torito
21 // boot standard in "no emulation" mode, making the entire filesystem
22 // available. It is based on the SYSLINUX boot loader for MS-DOS
25 // Copyright (C) 1994-2001 H. Peter Anvin
27 // This program is free software; you can redistribute it and/or modify
28 // it under the terms of the GNU General Public License as published by
29 // the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
30 // USA; either version 2 of the License, or (at your option) any later
31 // version; incorporated herein by reference.
33 // ****************************************************************************
35 // THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
36 // MODIFICATION DONE BY MICHAEL K TER LOUW
37 // LAST UPDATED 3-9-2002
38 // SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
40 // ****************************************************************************
42 // This file is a modified version of ISOLINUX.ASM.
43 // Modification done by Eric Kohl
44 // Last update 04-25-2002
46 // ****************************************************************************
48 //#define DEBUG_MESSAGES /* Uncomment to get debugging messages */
53 // ****************************************************************************
54 // BEGIN THE BIOS/CODE/DATA SEGMENT
55 // ****************************************************************************
56 serial_base = HEX(0400) // Base addresses for 4 serial ports (4 words)
57 BIOS_fbm = HEX(0413) // Free Base Memory (kilobytes) (1 word)
58 BIOS_timer = HEX(046C) // Timer ticks (1 word)
59 BIOS_magic = HEX(0472) // BIOS reset magic (1 word)
60 BIOS_vidrows = HEX(0484) // Number of screen rows (1 byte)
62 // Memory below this point is reserved for the BIOS and the MBR
63 trackbuf = HEX(1000) // Track buffer goes here (8192 bytes)
64 trackbufsize = 8192 // trackbuf ends at 3000h
67 file_sector = 0 // Sector pointer (0 = structure free)
68 file_left = 4 // Number of sectors left
71 dir_lba = 0 // Directory start (LBA)
72 dir_len = 4 // Length in bytes
73 dir_clust = 8 // Length in clusters
76 #define open_file_t_size 8
78 MAX_OPEN_LG2 = 2 // log2(Max number of open files)
80 SECTORSIZE_LG2 = 11 // 2048 bytes/sector (El Torito requirement)
82 retry_count = 6 // How patient are we with the BIOS?
84 /******************************************************************************/
85 absolute HEX(5000) // Here we keep our BSS stuff
87 resb DriveNo, 1 // CD-ROM BIOS drive number (BYTE)
88 resb DiskError, 1 // Error code for disk I/O (BYTE)
89 resb RetryCount, 1 // Used for disk access retries (BYTE)
90 resb TimeoutCount, 1 // Timeout counter (BYTE)
91 resb ISOFlags, 1 // Flags for ISO directory search (BYTE)
92 resb RootDir, dir_t_size // Root directory (dir_t_size BYTES)
93 resb CurDir, dir_t_size // Current directory (dir_t_size BYTES)
94 resb ISOFileName, 64 // ISO filename canonicalization buffer
95 resb ISOFileNameEnd, 1
97 //align open_file_t_size
99 resb Files, (MAX_OPEN * open_file_t_size)
102 /******************************************************************************/
106 cli // Disable interrupts
107 xor ax, ax // ax = segment zero
108 mov ss, ax // Initialize stack segment
109 mov sp, offset start // Set up stack
110 mov ds, ax // Initialize other segment registers
114 sti // Enable interrupts
115 cld // Increment pointers
117 mov cx, 2048 / 4 // Copy the bootsector
118 mov si, HEX(7C00) // from 0000:7C00
119 mov di, HEX(7000) // to 0000:7000
120 rep movsd // copy the program
122 ljmp16 0, relocate // jump into relocated code
125 #ifdef DEBUG_MESSAGES
126 // Display the banner and copyright
127 mov si, offset isolinux_banner // si points to hello message
128 call writestr // display the message
129 mov si, offset copyright_str
133 // Make sure the keyboard buffer is empty
134 call pollchar_and_empty
136 // Check for MBR on harddisk
144 jc .boot_cdrom // could not read hdd
147 #ifdef ROS_REGTEST // this change is taken from the original isobtrt.asm
148 mov ax, word ptr ds:[trackbuf+510]
150 mov ax, word ptr ds:[trackbuf]
153 je .boot_cdrom // no boot sector found (hopefully there are no weird bootsectors which begin with 0)
157 // Display the 'Press key' message and wait for a maximum of 5 seconds
159 mov si, offset presskey_msg // si points to 'Press key' message
160 call writestr // display the message
162 mov byte ptr ds:[TimeoutCount], 5
164 mov eax, ds:[BIOS_timer] // load current tick counter
168 call pollchar_and_empty
171 mov ebx, ds:[BIOS_timer]
175 mov si, offset dot_msg // print '.'
177 dec byte ptr ds:[TimeoutCount] // decrement timeout counter
185 // Boot first harddisk (drive 0x80)
209 // Save and display the boot drive number
210 mov byte ptr ds:[DriveNo], dl
211 #ifdef DEBUG_MESSAGES
212 mov si, offset startup_msg
219 // Now figure out what we're actually doing
220 // Note: use passed-in DL value rather than 7Fh because
221 // at least some BIOSes will get the wrong value otherwise
222 mov ax, HEX(4B01) // Get disk emulation status
223 mov dl, byte ptr ds:[DriveNo]
224 mov si, offset spec_packet
226 jc spec_query_failed // Shouldn't happen (BIOS bug)
227 mov dl, byte ptr ds:[DriveNo]
228 cmp byte ptr ds:[sp_drive], dl // Should contain the drive number
229 jne spec_query_failed
231 #ifdef DEBUG_MESSAGES
232 mov si, offset spec_ok_msg
234 mov al, byte ptr ds:[sp_drive]
240 // Get drive information
242 mov dl, byte ptr ds:[DriveNo]
243 mov si, offset drive_params
247 // mov si, nosecsize_msg No use in reporting this
251 // Check for the sector size (should be 2048, but
252 // some BIOSes apparently think we're 512-byte media)
254 // FIX: We need to check what the proper behaviour
255 // is for getlinsec when the BIOS thinks the sector
256 // size is 512!!! For that, we need such a BIOS, though...
257 #ifdef DEBUG_MESSAGES
258 mov si, offset secsize_msg
260 mov ax, word ptr ds:[dp_secsize]
267 // Clear Files structures
270 mov cx, (MAX_OPEN*open_file_t_size)/4
275 // Now, we need to sniff out the actual filesystem data structures.
276 // mkisofs gave us a pointer to the primary volume descriptor
277 // (which will be at 16 only for a single-session disk!); from the PVD
278 // we should be able to find the rest of what we need to know.
281 mov eax, 16 // Primary Volume Descriptor (sector 16)
285 mov eax, dword ptr ds:[trackbuf+156+2]
286 mov dword ptr ds:[RootDir+dir_lba],eax
287 mov dword ptr ds:[CurDir+dir_lba],eax
288 #ifdef DEBUG_MESSAGES
289 mov si, offset rootloc_msg
295 mov eax, dword ptr ds:[trackbuf+156+10]
296 mov dword ptr ds:[RootDir+dir_len],eax
297 mov dword ptr ds:[CurDir+dir_len],eax
298 #ifdef DEBUG_MESSAGES
299 mov si, offset rootlen_msg
305 shr eax,SECTORSIZE_LG2
306 mov dword ptr ds:[RootDir+dir_clust],eax
307 mov dword ptr ds:[CurDir+dir_clust],eax
308 #ifdef DEBUG_MESSAGES
309 mov si, offset rootsect_msg
315 // Look for the "REACTOS" directory, and if found,
316 // make it the current directory instead of the root
318 mov di, offset isolinux_dir
319 mov al, 2 // Search for a directory
322 mov si, offset no_dir_msg
327 mov dword ptr ds:[CurDir+dir_len],eax
328 mov eax, dword ptr ds:[si+file_left]
329 mov dword ptr ds:[CurDir+dir_clust],eax
330 xor eax,eax // Free this file pointer entry
331 xchg eax,dword ptr ds:[si+file_sector]
332 mov dword ptr ds:[CurDir+dir_lba],eax
335 mov di, offset isolinux_bin // di points to Isolinux filename
336 call searchdir // look for the file
337 jnz .isolinux_opened // got the file
338 mov si, offset no_isolinux_msg // si points to error message
339 call writemsg // display the message
340 jmp kaboom // fail boot
343 mov di, si // save file pointer
345 #ifdef DEBUG_MESSAGES
346 mov si, offset filelen_msg
352 mov ecx, eax // calculate sector count
359 #ifdef DEBUG_MESSAGES
361 mov si, offset filesect_msg
367 // use high segment, as some bios can fail, when offset is too big
368 mov bx, FREELDR_BASE / 16 // es = load segment
370 xor ebx, ebx // bx = load offset
371 mov si, di // restore file pointer
372 mov cx, HEX(0FFFF) // load the whole file
373 call getfssec // get the whole file
375 #ifdef DEBUG_MESSAGES
376 mov si, offset startldr_msg
381 mov dl, byte ptr ds:[DriveNo] // dl = boot drive
382 mov dh, 0 // dh = boot partition
384 /* Transfer execution to the bootloader */
385 ljmp16 0, FREELDR_BASE
397 // DX:AX or EAX = file length in bytes
403 // searchdir_iso is a special entry point for ISOLINUX only. In addition
404 // to the above, searchdir_iso passes a file flag mask in AL. This is useful
405 // for searching for directories.
414 mov byte ptr ds:[ISOFlags],al
415 call allocate_file // Temporary file structure for directory
420 mov si, offset CurDir
421 cmp byte ptr ds:[di], 92 //'\' // If filename begins with slash
423 inc di // Skip leading slash
424 mov si, offset RootDir // Reference root directory instead
426 mov eax, dword ptr ds:[si+dir_clust]
427 mov dword ptr ds:[bx+file_left],eax
428 mov eax,dword ptr ds:[si+dir_lba]
429 mov dword ptr ds:[bx+file_sector],eax
430 mov edx,dword ptr ds:[si+dir_len]
435 mov cl, byte ptr ds:[di]
441 mov byte ptr ds:[di-1], 0 // Terminate at directory name
442 mov cl,2 // Search for directory
443 xchg cl, byte ptr ds:[ISOFlags]
446 push offset .resume // Where to "return" to
452 // Get a chunk of the directory
456 mov cx,1 // load one sector
461 movzx eax, byte ptr ds:[si] // Length of directory entry
464 mov cl, byte ptr ds:[si+25]
465 xor cl, byte ptr ds:[ISOFlags]
466 test cl, HEX(8E) // Unwanted file attributes!
469 movzx cx, byte ptr ds:[si+32] // File identifier length
470 add si, 33 // File identifier offset
471 call iso_compare_names
475 sub edx, eax // Decrease bytes left
477 add si, ax // Advance pointer
480 // Did we finish the buffer?
481 cmp si, trackbuf+trackbufsize
482 jb .compare // No, keep going
484 jmp .getsome // Get some more directory
487 // Advance to the beginning of next sector
488 lea ax, [si+SECTORSIZE-1]
489 and ax, not (SECTORSIZE-1)
491 jmp .not_file // We still need to do length checks
494 #ifdef DEBUG_MESSAGES
495 mov si, offset findfail_msg
499 xor eax, eax // ZF = 1
500 mov dword ptr ds:[bx+file_sector], eax
505 mov eax, dword ptr ds:[si+2] // Location of extent
506 mov dword ptr ds:[bx+file_sector], eax
507 mov eax, dword ptr ds:[si+10] // Data length
509 add eax, SECTORSIZE-1
510 shr eax, SECTORSIZE_LG2
511 mov dword ptr ds:[bx+file_left], eax
521 // We get here if we were only doing part of a lookup
522 // This relies on the fact that .success returns bx == si
523 xchg edx, eax // Directory length in edx
524 pop cx // Old ISOFlags
525 pop di // Next filename pointer
527 // restore the backslash in the filename
528 mov byte ptr ds:[di-1], 92 // '\'
530 mov byte ptr ds:[ISOFlags], cl // Restore the flags
531 jz .failure // Did we fail? If so fail for real!
532 jmp .look_for_slash // Otherwise, next level
535 // allocate_file: Allocate a file structure
548 cmp dword ptr ds:[bx], 0
550 add bx, open_file_t_size // ZF = 0
552 // ZF = 0 if we fell out of the loop
558 // iso_compare_names:
559 // Compare the names DS:SI and DS:DI and report if they are
560 // equal from an ISO 9660 perspective. SI is the name from
561 // the filesystem; CX indicates its length, and ';' terminates.
562 // DI is expected to end with a null.
564 // Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
567 // First, terminate and canonicalize input filename
569 mov di, offset ISOFileName
579 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun
584 cmp byte ptr ds:[di-1], '.' // Remove terminal dots
589 mov byte ptr ds:[di], 0 // Null-terminate string
594 mov ah, byte ptr ds:[di]
597 jz .success2 // End of string for both
598 and al, al // Is either one end of string?
599 jz .failure2 // If so, failure
602 or ax, HEX(2020) // Convert to lower case
606 and ax, ax // ZF = 0 (at least one will be nonzero)
617 // getfssec: Get multiple clusters from a file, given the file pointer.
621 // SI -> File pointer
622 // CX -> Cluster count; 0FFFFh = until end of file
624 // SI -> File pointer (or 0 on EOF)
628 cmp cx, word ptr ds:[si+file_left]
630 mov cx, word ptr ds:[si+file_left]
636 mov eax, dword ptr ds:[si+file_sector]
642 add dword ptr ds:[si+file_sector], ecx
643 sub dword ptr ds:[si+file_left], ecx
644 ja .not_eof // CF = 0
647 mov dword ptr ds:[si+file_sector], ecx // Mark as unused
655 // INT 13h, AX=4B01h, DL=<passed in value> failed.
656 // Try to scan the entire 80h-FFh from the end.
658 mov si, offset spec_err_msg
665 mov si, offset spec_packet
666 mov byte ptr ds:[si], 13 // Size of buffer
671 mov si, offset maybe_msg
677 cmp byte ptr ds:[sp_drive], dl
680 // Okay, good enough...
681 mov si, offset alright_msg
683 mov byte ptr ds:[DriveNo], dl
687 // Award BIOS 4.51 apparently passes garbage in sp_drive,
688 // but if this was the drive number originally passed in
689 // DL then consider it "good enough"
691 cmp byte ptr ds:[DriveNo], dl
700 mov si, offset nothing_msg
708 // Information message (DS:SI) output
709 // Prefix with "isolinux: "
713 mov si, offset isolinux_str
721 // crlf: Print a newline
723 mov si, offset crlf_msg
727 // writestr: write a null-terminated string to the console, saving
728 // registers on entry.
738 jmp short writestr_top
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 // writechr: Write a character to the screen. There is a more "sophisticated"
785 // version of this in the subsequent code, so we patch the pointer
798 // Get one sector. Convenience entry point.
802 // Fall through to getlinsec
805 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
807 // Note that we can't always do this as a single request, because at least
808 // Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
809 // to 32 sectors (64K) per request.
812 // EAX - Linear sector number
813 // ES:BX - Target buffer
817 push es // save es, we reset it later to 0
819 mov si, offset dapa // Load up the DAPA
820 mov word ptr ds:[si+4], bx
822 mov word ptr ds:[si+6], bx
823 xor bx, bx // reset es to 0, some bioses (KVM) require that
825 mov dword ptr ds:[si+8], eax
827 push bp // Sectors left
828 cmp bp, word ptr ds:[MaxTransfer]
830 mov bp, word ptr ds:[MaxTransfer]
832 mov word ptr ds:[si+2], bp
834 mov dl, byte ptr ds:[DriveNo]
835 mov ah, HEX(42) // Extended Read
839 movzx eax,word ptr ds:[si+2] // Sectors we read
840 add dword ptr ds:[si+8], eax // Advance sector pointer
841 sub bp, ax // Sectors left
842 shl ax, SECTORSIZE_LG2-4 // 2048-byte sectors -> segment
843 add word ptr ds:[si+6], ax // Advance buffer pointer
846 mov eax, dword ptr ds:[si+8] // Next sector
851 // INT 13h with retry
853 mov byte ptr ds:[RetryCount], retry_count
858 add sp, 8*4 // Clean up stack
861 mov byte ptr ds:[DiskError], ah // Save error code
863 dec byte ptr ds:[RetryCount]
866 mov al, byte ptr ds:[RetryCount]
867 mov ah, byte ptr ds:[dapa+2] // Sector transfer count
868 cmp al,2 // Only 2 attempts left
870 mov ah,1 // Drop transfer size to 1
873 cmp al, retry_count-2
874 ja .again // First time, just try again
875 shr ah,1 // Otherwise, try to reduce
876 adc ah,0 // the max transfer size, but not to 0
878 mov byte ptr ds:[MaxTransfer],ah
879 mov byte ptr ds:[dapa+2],ah
885 mov si, offset diskerr_msg
887 mov al, byte ptr ds:[DiskError]
889 mov si, offset ondrive_str
894 // Fall through to kaboom
897 // kaboom: write a message and bail out. Wait for a user keypress,
898 // then do a hard reboot.
907 mov si, offset err_bootfailed
909 xor ax, ax // Wait for keypress
912 mov word ptr ds:[BIOS_magic], 0 // Cold reboot
913 ljmp16 HEX(0F000), HEX(0FFF0) // Reset vector address
917 // pollchar_and_empty: check if we have an input character pending (ZF = 0) and empty the input buffer afterwards
921 mov ah, 1 // Did the user press a key?
923 jz .end // No, then we're done
924 mov ah, 0 // Otherwise empty the buffer by reading it
932 .ascii CR, LF, "Loading IsoBoot...", CR, LF, NUL
934 .ascii " (C) 1994-2002 H. Peter Anvin", CR, LF, NUL
936 .ascii "Press any key to boot from CD", NUL
940 #ifdef DEBUG_MESSAGES
942 .ascii "Startup, DL = '", NUL
944 .ascii "packet OK, drive = ", NUL
946 .ascii "size appears to be ", NUL
948 .ascii "Root dir loc: ", NUL
950 .ascii "Root dir len: ", NUL
952 .ascii "Root dir len(sect): ", NUL
954 .ascii "SETUPLDR loc: ", NUL
956 .ascii "SETUPLDR len: ", NUL
958 .ascii "SETUPLDR len(sect): ", NUL
960 .ascii "Failed to find file!", NUL
962 .ascii "Starting SETUPLDR.SYS", NUL
966 .ascii "Load spec failed, trying wing ...", CR, LF, NUL
968 .ascii "Found smth at drive = ", NUL
970 .ascii "might be ok, continuing...", CR, LF, NUL
972 .ascii "Failed locate CD-ROM; boot failed.", CR, LF, NUL
975 .ascii "IsoBoot: ", NUL
979 .ascii "Disk error ", NUL
981 .ascii ", drive ", NUL
984 .ascii CR, LF, "failed..", NUL
986 .ascii "\\LOADER", NUL
988 .ascii "LOADER dir not found.", CR, LF, NUL
990 .ascii "SETUPLDR.SYS", NUL
992 .ascii "SETUPLDR not found.", CR, LF, NUL
996 // El Torito spec packet
1000 .byte HEX(13) // Size of packet
1002 .byte 0 // Media type
1004 .byte 0 // Drive number
1006 .byte 0 // Controller index
1008 .long 0 // LBA for emulated disk image
1010 .word 0 // IDE/SCSI information
1012 .word 0 // User-provided buffer
1014 .word 0 // Load segment
1016 .word 0 // Sector count
1018 .byte 0,0,0 // Simulated CHS geometry
1020 .byte 0 // Scratch, safe to overwrite
1023 // EBIOS drive parameter packet
1027 .word 30 // Buffer size
1029 .word 0 // Information flags
1031 .long 0 // Physical cylinders
1033 .long 0 // Physical heads
1035 .long 0 // Physical sectors/track
1037 .long 0,0 // Total sectors
1039 .word 0 // Bytes per sector
1041 .long 0 // Device Parameter Table
1043 .word 0 // 0BEDDh if rest valid
1049 .byte 0,0,0,0 // Host bus type
1051 .byte 0,0,0,0,0,0,0,0 // Interface type
1053 .long 0,0 // Interface path
1055 .long 0,0 // Device path
1058 .byte 0 // Checksum for DPI info
1061 // EBIOS disk address packet
1065 .word 16 // Packet size
1067 .word 0 // Block count
1069 .word 0 // Offset of buffer
1071 .word 0 // Segment of buffer
1073 .long 0 // LBA (LSW)
1074 .long 0 // LBA (MSW)
1078 .word 2 //32 // Max sectors per transfer
1080 .org 2046 // Pad to file offset 2046
1081 .word HEX(0aa55) // BootSector signature