mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 10:52:52 +00:00
1010 lines
21 KiB
NASM
1010 lines
21 KiB
NASM
; ****************************************************************************
|
|
;
|
|
; isolinux.asm
|
|
;
|
|
; A program to boot Linux kernels off a CD-ROM using the El Torito
|
|
; boot standard in "no emulation" mode, making the entire filesystem
|
|
; available. It is based on the SYSLINUX boot loader for MS-DOS
|
|
; floppies.
|
|
;
|
|
; Copyright (C) 1994-2001 H. Peter Anvin
|
|
;
|
|
; This program is free software; you can redistribute it and/or modify
|
|
; it under the terms of the GNU General Public License as published by
|
|
; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
|
|
; USA; either version 2 of the License, or (at your option) any later
|
|
; version; incorporated herein by reference.
|
|
;
|
|
; ****************************************************************************
|
|
;
|
|
; THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
|
|
; MODIFICATION DONE BY MICHAEL K TER LOUW
|
|
; LAST UPDATED 3-9-2002
|
|
; SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
|
|
;
|
|
; ****************************************************************************
|
|
;
|
|
; This file is a modified version of ISOLINUX.ASM.
|
|
; Modification done by Eric Kohl
|
|
; Last update 04-25-2002
|
|
;
|
|
; ****************************************************************************
|
|
|
|
; Note: The Makefile builds one version with DEBUG_MESSAGES automatically.
|
|
;%define DEBUG_MESSAGES ; Uncomment to get debugging messages
|
|
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; BEGIN THE BIOS/CODE/DATA SEGMENT
|
|
; ---------------------------------------------------------------------------
|
|
|
|
absolute 0400h
|
|
serial_base resw 4 ; Base addresses for 4 serial ports
|
|
absolute 0413h
|
|
BIOS_fbm resw 1 ; Free Base Memory (kilobytes)
|
|
absolute 046Ch
|
|
BIOS_timer resw 1 ; Timer ticks
|
|
absolute 0472h
|
|
BIOS_magic resw 1 ; BIOS reset magic
|
|
absolute 0484h
|
|
BIOS_vidrows resb 1 ; Number of screen rows
|
|
|
|
;
|
|
; Memory below this point is reserved for the BIOS and the MBR
|
|
;
|
|
absolute 1000h
|
|
trackbuf resb 8192 ; Track buffer goes here
|
|
trackbufsize equ $-trackbuf
|
|
; trackbuf ends at 3000h
|
|
|
|
struc open_file_t
|
|
file_sector resd 1 ; Sector pointer (0 = structure free)
|
|
file_left resd 1 ; Number of sectors left
|
|
endstruc
|
|
|
|
struc dir_t
|
|
dir_lba resd 1 ; Directory start (LBA)
|
|
dir_len resd 1 ; Length in bytes
|
|
dir_clust resd 1 ; Length in clusters
|
|
endstruc
|
|
|
|
|
|
MAX_OPEN_LG2 equ 2 ; log2(Max number of open files)
|
|
MAX_OPEN equ (1 << MAX_OPEN_LG2)
|
|
SECTORSIZE_LG2 equ 11 ; 2048 bytes/sector (El Torito requirement)
|
|
SECTORSIZE equ (1 << SECTORSIZE_LG2)
|
|
CR equ 13 ; Carriage Return
|
|
LF equ 10 ; Line Feed
|
|
retry_count equ 6 ; How patient are we with the BIOS?
|
|
|
|
|
|
|
|
absolute 5000h ; Here we keep our BSS stuff
|
|
|
|
DriveNo resb 1 ; CD-ROM BIOS drive number
|
|
DiskError resb 1 ; Error code for disk I/O
|
|
RetryCount resb 1 ; Used for disk access retries
|
|
TimeoutCount resb 1 ; Timeout counter
|
|
ISOFlags resb 1 ; Flags for ISO directory search
|
|
RootDir resb dir_t_size ; Root directory
|
|
CurDir resb dir_t_size ; Current directory
|
|
ISOFileName resb 64 ; ISO filename canonicalization buffer
|
|
ISOFileNameEnd equ $
|
|
|
|
|
|
alignb open_file_t_size
|
|
Files resb MAX_OPEN*open_file_t_size
|
|
|
|
|
|
|
|
section .text
|
|
org 7000h
|
|
|
|
start:
|
|
cli ; Disable interrupts
|
|
xor ax, ax ; ax = segment zero
|
|
mov ss, ax ; Initialize stack segment
|
|
mov sp, start ; Set up stack
|
|
mov ds, ax ; Initialize other segment registers
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
sti ; Enable interrupts
|
|
cld ; Increment pointers
|
|
|
|
mov cx, 2048 >> 2 ; Copy the bootsector
|
|
mov si, 0x7C00 ; from 0000:7C00
|
|
mov di, 0x7000 ; to 0000:7000
|
|
rep movsd ; copy the program
|
|
jmp 0:relocate ; jump into relocated code
|
|
|
|
relocate:
|
|
; Display the banner and copyright
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, isolinux_banner ; si points to hello message
|
|
call writestr ; display the message
|
|
mov si,copyright_str
|
|
call writestr
|
|
%endif
|
|
|
|
|
|
; Make sure the keyboard buffer is empty
|
|
%ifdef WAIT_FOR_KEY
|
|
.kbd_buffer_test:
|
|
call pollchar
|
|
jz .kbd_buffer_empty
|
|
call getchar
|
|
jmp .kbd_buffer_test
|
|
.kbd_buffer_empty:
|
|
|
|
; Display the 'Press key' message and wait for a maximum of 5 seconds
|
|
call crlf
|
|
mov si, presskey_msg ; si points to 'Press key' message
|
|
call writestr ; display the message
|
|
|
|
mov byte [TimeoutCount], 5
|
|
.next_second:
|
|
mov eax, [BIOS_timer] ; load current tick counter
|
|
add eax, 19 ;
|
|
|
|
.poll_again:
|
|
call pollchar
|
|
jnz .boot_cdrom
|
|
|
|
mov ebx, [BIOS_timer]
|
|
cmp eax, ebx
|
|
jnz .poll_again
|
|
|
|
mov si, dot_msg ; print '.'
|
|
call writestr
|
|
dec byte [TimeoutCount] ; decrement timeout counter
|
|
jz .boot_harddisk
|
|
jmp .next_second
|
|
|
|
.boot_harddisk:
|
|
; Boot first harddisk (drive 0x80)
|
|
mov ax, 0201h
|
|
mov dx, 0080h
|
|
mov cx, 0001h
|
|
mov bx, 7C00h
|
|
int 13h
|
|
jnc .go_hd
|
|
jmp kaboom
|
|
.go_hd:
|
|
mov ax, cs
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
mov dx, 0080h
|
|
|
|
jmp 0:0x7C00
|
|
%endif
|
|
|
|
.boot_cdrom:
|
|
; Save and display the boot drive number
|
|
mov [DriveNo], dl
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, startup_msg
|
|
call writemsg
|
|
mov al, dl
|
|
call writehex2
|
|
call crlf
|
|
%endif
|
|
|
|
; Now figure out what we're actually doing
|
|
; Note: use passed-in DL value rather than 7Fh because
|
|
; at least some BIOSes will get the wrong value otherwise
|
|
mov ax, 4B01h ; Get disk emulation status
|
|
mov dl, [DriveNo]
|
|
mov si, spec_packet
|
|
int 13h
|
|
jc near spec_query_failed ; Shouldn't happen (BIOS bug)
|
|
mov dl, [DriveNo]
|
|
cmp [sp_drive], dl ; Should contain the drive number
|
|
jne near spec_query_failed
|
|
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, spec_ok_msg
|
|
call writemsg
|
|
mov al, byte [sp_drive]
|
|
call writehex2
|
|
call crlf
|
|
%endif
|
|
|
|
found_drive:
|
|
; Get drive information
|
|
mov ah, 48h
|
|
mov dl, [DriveNo]
|
|
mov si, drive_params
|
|
int 13h
|
|
jnc params_ok
|
|
|
|
mov si, nosecsize_msg
|
|
call writemsg
|
|
|
|
params_ok:
|
|
; Check for the sector size (should be 2048, but
|
|
; some BIOSes apparently think we're 512-byte media)
|
|
;
|
|
; FIX: We need to check what the proper behaviour
|
|
; is for getlinsec when the BIOS thinks the sector
|
|
; size is 512!!! For that, we need such a BIOS, though...
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, secsize_msg
|
|
call writemsg
|
|
mov ax, [dp_secsize]
|
|
call writehex4
|
|
call crlf
|
|
%endif
|
|
|
|
|
|
;
|
|
; Clear Files structures
|
|
;
|
|
mov di, Files
|
|
mov cx, (MAX_OPEN*open_file_t_size)/4
|
|
xor eax, eax
|
|
rep stosd
|
|
|
|
;
|
|
; Now, we need to sniff out the actual filesystem data structures.
|
|
; mkisofs gave us a pointer to the primary volume descriptor
|
|
; (which will be at 16 only for a single-session disk!); from the PVD
|
|
; we should be able to find the rest of what we need to know.
|
|
;
|
|
get_fs_structures:
|
|
mov eax, 16 ; Primary Volume Descriptor (sector 16)
|
|
mov bx, trackbuf
|
|
call getonesec
|
|
|
|
mov eax, [trackbuf+156+2]
|
|
mov [RootDir+dir_lba],eax
|
|
mov [CurDir+dir_lba],eax
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, rootloc_msg
|
|
call writemsg
|
|
call writehex8
|
|
call crlf
|
|
%endif
|
|
|
|
mov eax,[trackbuf+156+10]
|
|
mov [RootDir+dir_len],eax
|
|
mov [CurDir+dir_len],eax
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, rootlen_msg
|
|
call writemsg
|
|
call writehex8
|
|
call crlf
|
|
%endif
|
|
add eax,SECTORSIZE-1
|
|
shr eax,SECTORSIZE_LG2
|
|
mov [RootDir+dir_clust],eax
|
|
mov [CurDir+dir_clust],eax
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, rootsect_msg
|
|
call writemsg
|
|
call writehex8
|
|
call crlf
|
|
%endif
|
|
|
|
; Look for the "REACTOS" directory, and if found,
|
|
; make it the current directory instead of the root
|
|
; directory.
|
|
mov di,isolinux_dir
|
|
mov al,02h ; Search for a directory
|
|
call searchdir_iso
|
|
jnz .dir_found
|
|
mov si,no_dir_msg
|
|
call writemsg
|
|
jmp kaboom
|
|
|
|
.dir_found:
|
|
mov [CurDir+dir_len],eax
|
|
mov eax,[si+file_left]
|
|
mov [CurDir+dir_clust],eax
|
|
xor eax,eax ; Free this file pointer entry
|
|
xchg eax,[si+file_sector]
|
|
mov [CurDir+dir_lba],eax
|
|
|
|
|
|
mov di, isolinux_bin ; di points to Isolinux filename
|
|
call searchdir ; look for the file
|
|
jnz .isolinux_opened ; got the file
|
|
mov si, no_isolinux_msg ; si points to error message
|
|
call writemsg ; display the message
|
|
jmp kaboom ; fail boot
|
|
|
|
.isolinux_opened:
|
|
mov di, si ; save file pointer
|
|
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, filelen_msg
|
|
call writemsg
|
|
call writehex8
|
|
call crlf
|
|
%endif
|
|
|
|
mov ecx, eax ; calculate sector count
|
|
shr ecx, 11
|
|
test eax, 0x7FF
|
|
jz .full_sector
|
|
inc ecx
|
|
.full_sector:
|
|
|
|
%ifdef DEBUG_MESSAGES
|
|
mov eax, ecx
|
|
mov si, filesect_msg
|
|
call writemsg
|
|
call writehex8
|
|
call crlf
|
|
%endif
|
|
|
|
mov bx, 0x8000 ; bx = load address
|
|
mov si, di ; restore file pointer
|
|
mov cx, 0xFFFF ; load the whole file
|
|
call getfssec ; get the whole file
|
|
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, startldr_msg
|
|
call writemsg
|
|
call crlf
|
|
%endif
|
|
|
|
mov dl, [DriveNo] ; dl = boot drive
|
|
mov dh, 0 ; dh = boot partition
|
|
jmp 0:0x8000 ; jump into OSLoader
|
|
|
|
|
|
|
|
;
|
|
; searchdir:
|
|
;
|
|
; Open a file
|
|
;
|
|
; On entry:
|
|
; DS:DI = filename
|
|
; If successful:
|
|
; ZF clear
|
|
; SI = file pointer
|
|
; DX:AX or EAX = file length in bytes
|
|
; If unsuccessful
|
|
; ZF set
|
|
;
|
|
|
|
;
|
|
; searchdir_iso is a special entry point for ISOLINUX only. In addition
|
|
; to the above, searchdir_iso passes a file flag mask in AL. This is useful
|
|
; for searching for directories.
|
|
;
|
|
alloc_failure:
|
|
xor ax,ax ; ZF <- 1
|
|
ret
|
|
|
|
searchdir:
|
|
xor al,al
|
|
searchdir_iso:
|
|
mov [ISOFlags],al
|
|
call allocate_file ; Temporary file structure for directory
|
|
jnz alloc_failure
|
|
push es
|
|
push ds
|
|
pop es ; ES = DS
|
|
mov si,CurDir
|
|
cmp byte [di],'\' ; If filename begins with slash
|
|
jne .not_rooted
|
|
inc di ; Skip leading slash
|
|
mov si,RootDir ; Reference root directory instead
|
|
.not_rooted:
|
|
mov eax,[si+dir_clust]
|
|
mov [bx+file_left],eax
|
|
mov eax,[si+dir_lba]
|
|
mov [bx+file_sector],eax
|
|
mov edx,[si+dir_len]
|
|
|
|
.look_for_slash:
|
|
mov ax,di
|
|
.scan:
|
|
mov cl,[di]
|
|
inc di
|
|
and cl,cl
|
|
jz .isfile
|
|
cmp cl,'\'
|
|
jne .scan
|
|
mov [di-1],byte 0 ; Terminate at directory name
|
|
mov cl,02h ; Search for directory
|
|
xchg cl,[ISOFlags]
|
|
push di
|
|
push cx
|
|
push word .resume ; Where to "return" to
|
|
push es
|
|
.isfile:
|
|
xchg ax,di
|
|
|
|
.getsome:
|
|
; Get a chunk of the directory
|
|
mov si,trackbuf
|
|
pushad
|
|
xchg bx,si
|
|
mov cx,1 ; load one sector
|
|
call getfssec
|
|
popad
|
|
|
|
.compare:
|
|
movzx eax, byte [si] ; Length of directory entry
|
|
cmp al, 33
|
|
jb .next_sector
|
|
mov cl, [si+25]
|
|
xor cl, [ISOFlags]
|
|
test cl, byte 8Eh ; Unwanted file attributes!
|
|
jnz .not_file
|
|
pusha
|
|
movzx cx, byte [si+32] ; File identifier length
|
|
add si, byte 33 ; File identifier offset
|
|
call iso_compare_names
|
|
popa
|
|
je .success
|
|
.not_file:
|
|
sub edx, eax ; Decrease bytes left
|
|
jbe .failure
|
|
add si, ax ; Advance pointer
|
|
|
|
.check_overrun:
|
|
; Did we finish the buffer?
|
|
cmp si, trackbuf+trackbufsize
|
|
jb .compare ; No, keep going
|
|
|
|
jmp short .getsome ; Get some more directory
|
|
|
|
.next_sector:
|
|
; Advance to the beginning of next sector
|
|
lea ax, [si+SECTORSIZE-1]
|
|
and ax, ~(SECTORSIZE-1)
|
|
sub ax, si
|
|
jmp short .not_file ; We still need to do length checks
|
|
|
|
.failure:
|
|
%ifdef DEBUG_MESSAGES
|
|
mov si, findfail_msg
|
|
call writemsg
|
|
call crlf
|
|
%endif
|
|
xor eax, eax ; ZF = 1
|
|
mov [bx+file_sector], eax
|
|
pop es
|
|
ret
|
|
|
|
.success:
|
|
mov eax, [si+2] ; Location of extent
|
|
mov [bx+file_sector], eax
|
|
mov eax, [si+10] ; Data length
|
|
push eax
|
|
add eax, SECTORSIZE-1
|
|
shr eax, SECTORSIZE_LG2
|
|
mov [bx+file_left], eax
|
|
pop eax
|
|
mov edx, eax
|
|
shr edx, 16
|
|
and bx, bx ; ZF = 0
|
|
mov si, bx
|
|
pop es
|
|
ret
|
|
|
|
.resume:
|
|
; We get here if we were only doing part of a lookup
|
|
; This relies on the fact that .success returns bx == si
|
|
xchg edx, eax ; Directory length in edx
|
|
pop cx ; Old ISOFlags
|
|
pop di ; Next filename pointer
|
|
|
|
mov byte [di-1], '\' ; restore the backslash in the filename
|
|
|
|
mov [ISOFlags], cl ; Restore the flags
|
|
jz .failure ; Did we fail? If so fail for real!
|
|
jmp .look_for_slash ; Otherwise, next level
|
|
|
|
;
|
|
; allocate_file: Allocate a file structure
|
|
;
|
|
; If successful:
|
|
; ZF set
|
|
; BX = file pointer
|
|
; In unsuccessful:
|
|
; ZF clear
|
|
;
|
|
allocate_file:
|
|
push cx
|
|
mov bx, Files
|
|
mov cx, MAX_OPEN
|
|
.check:
|
|
cmp dword [bx], byte 0
|
|
je .found
|
|
add bx, open_file_t_size ; ZF = 0
|
|
loop .check
|
|
; ZF = 0 if we fell out of the loop
|
|
.found:
|
|
pop cx
|
|
ret
|
|
|
|
;
|
|
; iso_compare_names:
|
|
; Compare the names DS:SI and DS:DI and report if they are
|
|
; equal from an ISO 9660 perspective. SI is the name from
|
|
; the filesystem; CX indicates its length, and ';' terminates.
|
|
; DI is expected to end with a null.
|
|
;
|
|
; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
|
|
;
|
|
iso_compare_names:
|
|
; First, terminate and canonicalize input filename
|
|
push di
|
|
mov di, ISOFileName
|
|
.canon_loop:
|
|
jcxz .canon_end
|
|
lodsb
|
|
dec cx
|
|
cmp al, ';'
|
|
je .canon_end
|
|
and al, al
|
|
je .canon_end
|
|
stosb
|
|
cmp di, ISOFileNameEnd-1 ; Guard against buffer overrun
|
|
jb .canon_loop
|
|
.canon_end:
|
|
cmp di, ISOFileName
|
|
jbe .canon_done
|
|
cmp byte [di-1], '.' ; Remove terminal dots
|
|
jne .canon_done
|
|
dec di
|
|
jmp short .canon_end
|
|
.canon_done:
|
|
mov [di], byte 0 ; Null-terminate string
|
|
pop di
|
|
mov si, ISOFileName
|
|
.compare:
|
|
lodsb
|
|
mov ah, [di]
|
|
inc di
|
|
and ax, ax
|
|
jz .success ; End of string for both
|
|
and al, al ; Is either one end of string?
|
|
jz .failure ; If so, failure
|
|
and ah, ah
|
|
jz .failure
|
|
or ax, 2020h ; Convert to lower case
|
|
cmp al, ah
|
|
je .compare
|
|
.failure:
|
|
and ax, ax ; ZF = 0 (at least one will be nonzero)
|
|
.success:
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;
|
|
; getfssec: Get multiple clusters from a file, given the file pointer.
|
|
;
|
|
; On entry:
|
|
; ES:BX -> Buffer
|
|
; SI -> File pointer
|
|
; CX -> Cluster count; 0FFFFh = until end of file
|
|
; On exit:
|
|
; SI -> File pointer (or 0 on EOF)
|
|
; CF = 1 -> Hit EOF
|
|
;
|
|
getfssec:
|
|
cmp cx, [si+file_left]
|
|
jna .ok_size
|
|
mov cx, [si+file_left]
|
|
|
|
.ok_size:
|
|
mov bp, cx
|
|
push cx
|
|
push si
|
|
mov eax, [si+file_sector]
|
|
call getlinsec
|
|
xor ecx, ecx
|
|
pop si
|
|
pop cx
|
|
|
|
add [si+file_sector], ecx
|
|
sub [si+file_left], ecx
|
|
ja .not_eof ; CF = 0
|
|
|
|
xor ecx, ecx
|
|
mov [si+file_sector], ecx ; Mark as unused
|
|
xor si,si
|
|
stc
|
|
|
|
.not_eof:
|
|
ret
|
|
|
|
|
|
|
|
; INT 13h, AX=4B01h, DL=<passed in value> failed.
|
|
; Try to scan the entire 80h-FFh from the end.
|
|
spec_query_failed:
|
|
mov si,spec_err_msg
|
|
call writemsg
|
|
|
|
mov dl, 0FFh
|
|
.test_loop:
|
|
pusha
|
|
mov ax, 4B01h
|
|
mov si, spec_packet
|
|
mov byte [si], 13 ; Size of buffer
|
|
int 13h
|
|
popa
|
|
jc .still_broken
|
|
|
|
mov si, maybe_msg
|
|
call writemsg
|
|
mov al, dl
|
|
call writehex2
|
|
call crlf
|
|
|
|
cmp byte [sp_drive], dl
|
|
jne .maybe_broken
|
|
|
|
; Okay, good enough...
|
|
mov si, alright_msg
|
|
call writemsg
|
|
mov [DriveNo], dl
|
|
.found_drive:
|
|
jmp found_drive
|
|
|
|
; Award BIOS 4.51 apparently passes garbage in sp_drive,
|
|
; but if this was the drive number originally passed in
|
|
; DL then consider it "good enough"
|
|
.maybe_broken:
|
|
cmp byte [DriveNo], dl
|
|
je .found_drive
|
|
|
|
.still_broken:
|
|
dec dx
|
|
cmp dl, 80h
|
|
jnb .test_loop
|
|
|
|
fatal_error:
|
|
mov si, nothing_msg
|
|
call writemsg
|
|
|
|
.norge:
|
|
jmp short .norge
|
|
|
|
|
|
|
|
; Information message (DS:SI) output
|
|
; Prefix with "isolinux: "
|
|
;
|
|
writemsg:
|
|
push ax
|
|
push si
|
|
mov si, isolinux_str
|
|
call writestr
|
|
pop si
|
|
call writestr
|
|
pop ax
|
|
ret
|
|
|
|
;
|
|
; crlf: Print a newline
|
|
;
|
|
crlf:
|
|
mov si, crlf_msg
|
|
; Fall through
|
|
|
|
;
|
|
; writestr: write a null-terminated string to the console, saving
|
|
; registers on entry.
|
|
;
|
|
writestr:
|
|
pushfd
|
|
pushad
|
|
.top:
|
|
lodsb
|
|
and al, al
|
|
jz .end
|
|
call writechr
|
|
jmp short .top
|
|
.end:
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
|
|
;
|
|
; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
|
|
;
|
|
writehex2:
|
|
pushfd
|
|
pushad
|
|
shl eax, 24
|
|
mov cx, 2
|
|
jmp short writehex_common
|
|
writehex4:
|
|
pushfd
|
|
pushad
|
|
shl eax, 16
|
|
mov cx, 4
|
|
jmp short writehex_common
|
|
writehex8:
|
|
pushfd
|
|
pushad
|
|
mov cx, 8
|
|
writehex_common:
|
|
.loop:
|
|
rol eax, 4
|
|
push eax
|
|
and al, 0Fh
|
|
cmp al, 10
|
|
jae .high
|
|
.low:
|
|
add al, '0'
|
|
jmp short .ischar
|
|
.high:
|
|
add al, 'A'-10
|
|
.ischar:
|
|
call writechr
|
|
pop eax
|
|
loop .loop
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
;
|
|
; Write a character to the screen. There is a more "sophisticated"
|
|
; version of this in the subsequent code, so we patch the pointer
|
|
; when appropriate.
|
|
;
|
|
|
|
writechr:
|
|
pushfd
|
|
pushad
|
|
mov ah, 0Eh
|
|
xor bx, bx
|
|
int 10h
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
;
|
|
; Get one sector. Convenience entry point.
|
|
;
|
|
getonesec:
|
|
mov bp, 1
|
|
; Fall through to getlinsec
|
|
|
|
;
|
|
; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
|
|
;
|
|
; Note that we can't always do this as a single request, because at least
|
|
; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
|
|
; to 32 sectors (64K) per request.
|
|
;
|
|
; Input:
|
|
; EAX - Linear sector number
|
|
; ES:BX - Target buffer
|
|
; BP - Sector count
|
|
;
|
|
getlinsec:
|
|
mov si,dapa ; Load up the DAPA
|
|
mov [si+4],bx
|
|
mov bx,es
|
|
mov [si+6],bx
|
|
mov [si+8],eax
|
|
.loop2:
|
|
push bp ; Sectors left
|
|
cmp bp,[MaxTransfer]
|
|
jbe .bp_ok
|
|
mov bp,[MaxTransfer]
|
|
.bp_ok:
|
|
mov [si+2],bp
|
|
push si
|
|
mov dl,[DriveNo]
|
|
mov ah,42h ; Extended Read
|
|
call xint13
|
|
pop si
|
|
pop bp
|
|
movzx eax,word [si+2] ; Sectors we read
|
|
add [si+8],eax ; Advance sector pointer
|
|
sub bp,ax ; Sectors left
|
|
shl ax,SECTORSIZE_LG2-4 ; 2048-byte sectors -> segment
|
|
add [si+6],ax ; Advance buffer pointer
|
|
and bp,bp
|
|
jnz .loop2
|
|
mov eax,[si+8] ; Next sector
|
|
ret
|
|
|
|
; INT 13h with retry
|
|
xint13:
|
|
mov byte [RetryCount], retry_count
|
|
.try:
|
|
pushad
|
|
int 13h
|
|
jc .error
|
|
add sp, byte 8*4 ; Clean up stack
|
|
ret
|
|
.error:
|
|
mov [DiskError], ah ; Save error code
|
|
popad
|
|
dec byte [RetryCount]
|
|
jz .real_error
|
|
push ax
|
|
mov al,[RetryCount]
|
|
mov ah,[dapa+2] ; Sector transfer count
|
|
cmp al,2 ; Only 2 attempts left
|
|
ja .nodanger
|
|
mov ah,1 ; Drop transfer size to 1
|
|
jmp short .setsize
|
|
.nodanger:
|
|
cmp al,retry_count-2
|
|
ja .again ; First time, just try again
|
|
shr ah,1 ; Otherwise, try to reduce
|
|
adc ah,0 ; the max transfer size, but not to 0
|
|
.setsize:
|
|
mov [MaxTransfer],ah
|
|
mov [dapa+2],ah
|
|
.again:
|
|
pop ax
|
|
jmp .try
|
|
|
|
.real_error:
|
|
mov si, diskerr_msg
|
|
call writemsg
|
|
mov al, [DiskError]
|
|
call writehex2
|
|
mov si, ondrive_str
|
|
call writestr
|
|
mov al, dl
|
|
call writehex2
|
|
call crlf
|
|
; Fall through to kaboom
|
|
|
|
;
|
|
; kaboom: write a message and bail out. Wait for a user keypress,
|
|
; then do a hard reboot.
|
|
;
|
|
kaboom:
|
|
mov ax, cs
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
sti
|
|
mov si, err_bootfailed
|
|
call writestr
|
|
call getchar
|
|
cli
|
|
mov word [BIOS_magic], 0 ; Cold reboot
|
|
jmp 0F000h:0FFF0h ; Reset vector address
|
|
|
|
getchar:
|
|
.again:
|
|
mov ah, 1 ; Poll keyboard
|
|
int 16h
|
|
jz .again
|
|
.kbd:
|
|
xor ax, ax ; Get keyboard input
|
|
int 16h
|
|
.func_key:
|
|
ret
|
|
|
|
|
|
;
|
|
; pollchar: check if we have an input character pending (ZF = 0)
|
|
;
|
|
pollchar:
|
|
pushad
|
|
mov ah,1 ; Poll keyboard
|
|
int 16h
|
|
popad
|
|
ret
|
|
|
|
|
|
|
|
isolinux_banner db CR, LF, 'Loading IsoBoot...', CR, LF, 0
|
|
copyright_str db ' Copyright (C) 1994-2002 H. Peter Anvin', CR, LF, 0
|
|
presskey_msg db 'Press any key to boot from CD', 0
|
|
dot_msg db '.',0
|
|
|
|
%ifdef DEBUG_MESSAGES
|
|
startup_msg: db 'Starting up, DL = ', 0
|
|
spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0
|
|
secsize_msg: db 'Sector size appears to be ', 0
|
|
rootloc_msg: db 'Root directory location: ', 0
|
|
rootlen_msg: db 'Root directory length: ', 0
|
|
rootsect_msg: db 'Root directory length(sectors): ', 0
|
|
fileloc_msg: db 'SETUPLDR.SYS location: ', 0
|
|
filelen_msg: db 'SETUPLDR.SYS length: ', 0
|
|
filesect_msg: db 'SETUPLDR.SYS length(sectors): ', 0
|
|
findfail_msg: db 'Failed to find file!', 0
|
|
startldr_msg: db 'Starting SETUPLDR.SYS', 0
|
|
%endif
|
|
|
|
nosecsize_msg: db 'Failed to get sector size, assuming 0800', CR, LF, 0
|
|
spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
|
|
maybe_msg: db 'Found something at drive = ', 0
|
|
alright_msg: db 'Looks like it might be right, continuing...', CR, LF, 0
|
|
nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF, 0
|
|
isolinux_str db 'IsoBoot: ', 0
|
|
crlf_msg db CR, LF, 0
|
|
diskerr_msg: db 'Disk error ', 0
|
|
ondrive_str: db ', drive ', 0
|
|
err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'
|
|
isolinux_dir db '\LOADER', 0
|
|
no_dir_msg db 'Could not find the LOADER directory.', CR, LF, 0
|
|
isolinux_bin db 'SETUPLDR.SYS', 0
|
|
no_isolinux_msg db 'Could not find SETUPLDR.SYS.', CR, LF, 0
|
|
|
|
;
|
|
; El Torito spec packet
|
|
;
|
|
align 8, db 0
|
|
spec_packet: db 13h ; Size of packet
|
|
sp_media: db 0 ; Media type
|
|
sp_drive: db 0 ; Drive number
|
|
sp_controller: db 0 ; Controller index
|
|
sp_lba: dd 0 ; LBA for emulated disk image
|
|
sp_devspec: dw 0 ; IDE/SCSI information
|
|
sp_buffer: dw 0 ; User-provided buffer
|
|
sp_loadseg: dw 0 ; Load segment
|
|
sp_sectors: dw 0 ; Sector count
|
|
sp_chs: db 0,0,0 ; Simulated CHS geometry
|
|
sp_dummy: db 0 ; Scratch, safe to overwrite
|
|
|
|
;
|
|
; EBIOS drive parameter packet
|
|
;
|
|
align 8, db 0
|
|
drive_params: dw 30 ; Buffer size
|
|
dp_flags: dw 0 ; Information flags
|
|
dp_cyl: dd 0 ; Physical cylinders
|
|
dp_head: dd 0 ; Physical heads
|
|
dp_sec: dd 0 ; Physical sectors/track
|
|
dp_totalsec: dd 0,0 ; Total sectors
|
|
dp_secsize: dw 0 ; Bytes per sector
|
|
dp_dpte: dd 0 ; Device Parameter Table
|
|
dp_dpi_key: dw 0 ; 0BEDDh if rest valid
|
|
dp_dpi_len: db 0 ; DPI len
|
|
db 0
|
|
dw 0
|
|
dp_bus: times 4 db 0 ; Host bus type
|
|
dp_interface: times 8 db 0 ; Interface type
|
|
db_i_path: dd 0,0 ; Interface path
|
|
db_d_path: dd 0,0 ; Device path
|
|
db 0
|
|
db_dpi_csum: db 0 ; Checksum for DPI info
|
|
|
|
;
|
|
; EBIOS disk address packet
|
|
;
|
|
align 8, db 0
|
|
dapa: dw 16 ; Packet size
|
|
.count: dw 0 ; Block count
|
|
.off: dw 0 ; Offset of buffer
|
|
.seg: dw 0 ; Segment of buffer
|
|
.lba: dd 0 ; LBA (LSW)
|
|
dd 0 ; LBA (MSW)
|
|
|
|
alignb 4, db 0
|
|
MaxTransfer dw 2 ;32 ; Max sectors per transfer
|
|
|
|
times 2046-($-$$) db 0 ; Pad to file offset 2046
|
|
dw 0aa55h ; BootSector signature
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|