blob: 50d9fe1cef0a69252d9bdf2fc5aed4bac00d69ef [file] [log] [blame]
; -*- fundamental -*- (asm-mode sucks)
; ****************************************************************************
;
; 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 1994-2009 H. Peter Anvin - All Rights Reserved
; Copyright 2009 Intel Corporation; author: 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., 53 Temple Place Ste 330,
; Boston MA 02111-1307, USA; either version 2 of the License, or
; (at your option) any later version; incorporated herein by reference.
;
; ****************************************************************************
%define IS_ISOLINUX 1
%include "head.inc"
;
; Some semi-configurable constants... change on your own risk.
;
my_id equ isolinux_id
NULLFILE equ 0 ; Zero byte == null file name
NULLOFFSET equ 0 ; Position in which to look
retry_count equ 6 ; How patient are we with the BIOS?
%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement)
SECTOR_SIZE equ (1 << SECTOR_SHIFT)
ROOT_DIR_WORD equ 0x002F
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
;
; Memory below this point is reserved for the BIOS and the MBR
;
section .earlybss
global trackbuf
trackbufsize equ 8192
trackbuf resb trackbufsize ; Track buffer goes here
; ends at 2800h
; Some of these are touched before the whole image
; is loaded. DO NOT move this to .bss16/.uibss.
section .earlybss
global BIOSName
alignb 4
FirstSecSum resd 1 ; Checksum of bytes 64-2048
ImageDwords resd 1 ; isolinux.bin size, dwords
InitStack resd 1 ; Initial stack pointer (SS:SP)
DiskSys resw 1 ; Last INT 13h call
ImageSectors resw 1 ; isolinux.bin size, sectors
; These following two are accessed as a single dword...
GetlinsecPtr resw 1 ; The sector-read pointer
BIOSName resw 1 ; Display string for BIOS type
%define HAVE_BIOSNAME 1
global BIOSType
BIOSType resw 1
DiskError resb 1 ; Error code for disk I/O
global DriveNumber
DriveNumber resb 1 ; CD-ROM BIOS drive number
ISOFlags resb 1 ; Flags for ISO directory search
RetryCount resb 1 ; Used for disk access retries
alignb 8
global Hidden
Hidden resq 1 ; Used in hybrid mode
bsSecPerTrack resw 1 ; Used in hybrid mode
bsHeads resw 1 ; Used in hybrid mode
;
; El Torito spec packet
;
alignb 8
_spec_start equ $
global spec_packet
spec_packet: resb 1 ; Size of packet
sp_media: resb 1 ; Media type
sp_drive: resb 1 ; Drive number
sp_controller: resb 1 ; Controller index
sp_lba: resd 1 ; LBA for emulated disk image
sp_devspec: resw 1 ; IDE/SCSI information
sp_buffer: resw 1 ; User-provided buffer
sp_loadseg: resw 1 ; Load segment
sp_sectors: resw 1 ; Sector count
sp_chs: resb 3 ; Simulated CHS geometry
sp_dummy: resb 1 ; Scratch, safe to overwrite
;
; EBIOS drive parameter packet
;
alignb 8
drive_params: resw 1 ; Buffer size
dp_flags: resw 1 ; Information flags
dp_cyl: resd 1 ; Physical cylinders
dp_head: resd 1 ; Physical heads
dp_sec: resd 1 ; Physical sectors/track
dp_totalsec: resd 2 ; Total sectors
dp_secsize: resw 1 ; Bytes per sector
dp_dpte: resd 1 ; Device Parameter Table
dp_dpi_key: resw 1 ; 0BEDDh if rest valid
dp_dpi_len: resb 1 ; DPI len
resb 1
resw 1
dp_bus: resb 4 ; Host bus type
dp_interface: resb 8 ; Interface type
db_i_path: resd 2 ; Interface path
db_d_path: resd 2 ; Device path
resb 1
db_dpi_csum: resb 1 ; Checksum for DPI info
;
; EBIOS disk address packet
;
alignb 8
dapa: resw 1 ; Packet size
.count: resw 1 ; Block count
.off: resw 1 ; Offset of buffer
.seg: resw 1 ; Segment of buffer
.lba: resd 2 ; LBA (LSW, MSW)
;
; Spec packet for disk image emulation
;
alignb 8
dspec_packet: resb 1 ; Size of packet
dsp_media: resb 1 ; Media type
dsp_drive: resb 1 ; Drive number
dsp_controller: resb 1 ; Controller index
dsp_lba: resd 1 ; LBA for emulated disk image
dsp_devspec: resw 1 ; IDE/SCSI information
dsp_buffer: resw 1 ; User-provided buffer
dsp_loadseg: resw 1 ; Load segment
dsp_sectors: resw 1 ; Sector count
dsp_chs: resb 3 ; Simulated CHS geometry
dsp_dummy: resb 1 ; Scratch, safe to overwrite
alignb 4
_spec_end equ $
_spec_len equ _spec_end - _spec_start
section .init
;;
;; Primary entry point. Because BIOSes are buggy, we only load the first
;; CD-ROM sector (2K) of the file, so the number one priority is actually
;; loading the rest.
;;
global StackBuf
StackBuf equ STACK_TOP-44 ; 44 bytes needed for
; the bootsector chainloading
; code!
global OrigESDI
OrigESDI equ StackBuf-4 ; The high dword on the stack
StackHome equ OrigESDI
bootsec equ $
_start: ; Far jump makes sure we canonicalize the address
cli
jmp 0:_start1
times 8-($-$$) nop ; Pad to file offset 8
; This table hopefully gets filled in by mkisofs using the
; -boot-info-table option. If not, the values in this
; table are default values that we can use to get us what
; we need, at least under a certain set of assumptions.
global iso_boot_info
iso_boot_info:
bi_pvd: dd 16 ; LBA of primary volume descriptor
bi_file: dd 0 ; LBA of boot file
bi_length: dd 0xdeadbeef ; Length of boot file
bi_csum: dd 0xdeadbeef ; Checksum of boot file
bi_reserved: times 10 dd 0xdeadbeef ; Reserved
bi_end:
; Custom entry point for the hybrid-mode disk.
; The following values will have been pushed onto the
; entry stack:
; - partition offset (qword)
; - ES
; - DI
; - DX (including drive number)
; - CBIOS Heads
; - CBIOS Sectors
; - EBIOS flag
; (top of stack)
;
; If we had an old isohybrid, the partition offset will
; be missing; we can check for that with sp >= 0x7c00.
; Serious hack alert.
%ifndef DEBUG_MESSAGES
_hybrid_signature:
dd 0x7078c0fb ; An arbitrary number...
_start_hybrid:
pop cx ; EBIOS flag
pop word [cs:bsSecPerTrack]
pop word [cs:bsHeads]
pop dx
pop di
pop es
xor eax,eax
xor ebx,ebx
cmp sp,7C00h
jae .nooffset
pop eax
pop ebx
.nooffset:
mov si,bios_cbios
jcxz _start_common
mov si,bios_ebios
jmp _start_common
%endif
_start1:
mov si,bios_cdrom
xor eax,eax
xor ebx,ebx
_start_common:
mov [cs:InitStack],sp ; Save initial stack pointer
mov [cs:InitStack+2],ss
xor cx,cx
mov ss,cx
mov sp,StackBuf ; Set up stack
push es ; Save initial ES:DI -> $PnP pointer
push di
mov ds,cx
mov es,cx
mov fs,cx
mov gs,cx
sti
cld
mov [Hidden],eax
mov [Hidden+4],ebx
mov [BIOSType],si
mov eax,[si]
mov [GetlinsecPtr],eax
; Show signs of life
mov si,syslinux_banner
call writestr_early
%ifdef DEBUG_MESSAGES
mov si,copyright_str
%else
mov si,[BIOSName]
%endif
call writestr_early
;
; Before modifying any memory, get the checksum of bytes
; 64-2048
;
initial_csum: xor edi,edi
mov si,bi_end
mov cx,(SECTOR_SIZE-64) >> 2
.loop: lodsd
add edi,eax
loop .loop
mov [FirstSecSum],edi
mov [DriveNumber],dl
%ifdef DEBUG_MESSAGES
mov si,startup_msg
call writemsg
mov al,dl
call writehex2
call crlf_early
%endif
;
; Initialize spec packet buffers
;
mov di,_spec_start
mov cx,_spec_len >> 2
xor eax,eax
rep stosd
; Initialize length field of the various packets
mov byte [spec_packet],13h
mov byte [drive_params],30
mov byte [dapa],16
mov byte [dspec_packet],13h
; Other nonzero fields
inc word [dsp_sectors]
; Are we just pretending to be a CD-ROM?
cmp word [BIOSType],bios_cdrom
jne found_drive ; If so, no spec packet...
; 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,[DriveNumber]
mov si,spec_packet
call int13
jc award_hack ; changed for BrokenAwardHack
mov dl,[DriveNumber]
cmp [sp_drive],dl ; Should contain the drive number
jne spec_query_failed
%ifdef DEBUG_MESSAGES
mov si,spec_ok_msg
call writemsg
mov al,byte [sp_drive]
call writehex2
call crlf_early
%endif
found_drive:
; Alright, we have found the drive. Now, try to find the
; boot file itself. If we have a boot info table, life is
; good; if not, we have to make some assumptions, and try
; to figure things out ourselves. In particular, the
; assumptions we have to make are:
; - single session only
; - only one boot entry (no menu or other alternatives)
cmp dword [bi_file],0 ; Address of code to load
jne found_file ; Boot info table present :)
%ifdef DEBUG_MESSAGES
mov si,noinfotable_msg
call writemsg
%endif
; No such luck. See if the spec packet contained one.
mov eax,[sp_lba]
and eax,eax
jz set_file ; Good enough
%ifdef DEBUG_MESSAGES
mov si,noinfoinspec_msg
call writemsg
%endif
; No such luck. Get the Boot Record Volume, assuming single
; session disk, and that we're the first entry in the chain.
mov eax,17 ; Assumed address of BRV
mov bx,trackbuf
call getonesec
mov eax,[trackbuf+47h] ; Get boot catalog address
mov bx,trackbuf
call getonesec ; Get boot catalog
mov eax,[trackbuf+28h] ; First boot entry
; And hope and pray this is us...
; Some BIOSes apparently have limitations on the size
; that may be loaded (despite the El Torito spec being very
; clear on the fact that it must all be loaded.) Therefore,
; we load it ourselves, and *bleep* the BIOS.
set_file:
mov [bi_file],eax
found_file:
; Set up boot file sizes
mov eax,[bi_length]
sub eax,SECTOR_SIZE-3 ; ... minus sector loaded
shr eax,2 ; bytes->dwords
mov [ImageDwords],eax ; boot file dwords
add eax,((SECTOR_SIZE-1) >> 2)
shr eax,SECTOR_SHIFT-2 ; dwords->sectors
mov [ImageSectors],ax ; boot file sectors
mov eax,[bi_file] ; Address of code to load
inc eax ; Don't reload bootstrap code
%ifdef DEBUG_MESSAGES
mov si,offset_msg
call writemsg
call writehex8
call crlf_early
%endif
; Load the rest of the file. However, just in case there
; are still BIOSes with 64K wraparound problems, we have to
; take some extra precautions. Since the normal load
; address (TEXT_START) is *not* 2K-sector-aligned, we round
; the target address upward to a sector boundary,
; and then move the entire thing down as a unit.
MaxLMA equ 384*1024 ; Reasonable limit (384K)
mov bx,((TEXT_START+2*SECTOR_SIZE-1) & ~(SECTOR_SIZE-1)) >> 4
mov bp,[ImageSectors]
push bx ; Load segment address
.more:
push bx ; Segment address
push bp ; Sector count
mov es,bx
mov cx,0xfff
and bx,cx
inc cx
sub cx,bx
shr cx,SECTOR_SHIFT - 4
jnz .notaligned
mov cx,0x10000 >> SECTOR_SHIFT ; Full 64K segment possible
.notaligned:
cmp bp,cx
jbe .ok
mov bp,cx
.ok:
xor bx,bx
push bp
push eax
call getlinsec
pop eax
pop cx
movzx edx,cx
pop bp
pop bx
shl cx,SECTOR_SHIFT - 4
add bx,cx
add eax,edx
sub bp,dx
jnz .more
; Move the image into place, and also verify the
; checksum
pop ax ; Load segment address
mov bx,(TEXT_START + SECTOR_SIZE) >> 4
mov ecx,[ImageDwords]
mov edi,[FirstSecSum] ; First sector checksum
xor si,si
move_verify_image:
.setseg:
mov ds,ax
mov es,bx
.loop:
mov edx,[si]
add edi,edx
dec ecx
mov [es:si],edx
jz .done
add si,4
jnz .loop
add ax,1000h
add bx,1000h
jmp .setseg
.done:
mov ax,cs
mov ds,ax
mov es,ax
; Verify the checksum on the loaded image.
cmp [bi_csum],edi
je integrity_ok
mov si,checkerr_msg
call writemsg
jmp kaboom
integrity_ok:
%ifdef DEBUG_MESSAGES
mov si,allread_msg
call writemsg
%endif
jmp all_read ; Jump to main code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; There is a problem with certain versions of the AWARD BIOS ...
;; the boot sector will be loaded and executed correctly, but, because the
;; int 13 vector points to the wrong code in the BIOS, every attempt to
;; load the spec packet will fail. We scan for the equivalent of
;;
;; mov ax,0201h
;; mov bx,7c00h
;; mov cx,0006h
;; mov dx,0180h
;; pushf
;; call <direct far>
;;
;; and use <direct far> as the new vector for int 13. The code above is
;; used to load the boot code into ram, and there should be no reason
;; for anybody to change it now or in the future. There are no opcodes
;; that use encodings relativ to IP, so scanning is easy. If we find the
;; code above in the BIOS code we can be pretty sure to run on a machine
;; with an broken AWARD BIOS ...
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
%ifdef DEBUG_MESSAGES ;;
;;
award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;;
award_not_orig db "BAH: Original Int 13 vector : ",0 ;;
award_not_new db "BAH: Int 13 vector changed to : ",0 ;;
award_not_succ db "BAH: SUCCESS",CR,LF,0 ;;
award_not_fail db "BAH: FAILURE" ;;
award_not_crlf db CR,LF,0 ;;
;;
%endif ;;
;;
award_oldint13 dd 0 ;;
award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
award_hack: mov si,spec_err_msg ; Moved to this place from
call writemsg ; spec_query_failed
;
%ifdef DEBUG_MESSAGES ;
;
mov si,award_notice ; display our plan
call writemsg ;
mov si,award_not_orig ; display original int 13
call writemsg ; vector
%endif ;
mov eax,[13h*4] ;
mov [award_oldint13],eax ;
;
%ifdef DEBUG_MESSAGES ;
;
call writehex8 ;
mov si,award_not_crlf ;
call writestr_early ;
%endif ;
push es ; save ES
mov ax,0f000h ; ES = BIOS Seg
mov es,ax ;
cld ;
xor di,di ; start at ES:DI = f000:0
award_loop: push di ; save DI
mov si,award_string ; scan for award_string
mov cx,7 ; length of award_string = 7dw
repz cmpsw ; compare
pop di ; restore DI
jcxz award_found ; jmp if found
inc di ; not found, inc di
jno award_loop ;
;
award_failed: pop es ; No, not this way :-((
award_fail2: ;
;
%ifdef DEBUG_MESSAGES ;
;
mov si,award_not_fail ; display failure ...
call writemsg ;
%endif ;
mov eax,[award_oldint13] ; restore the original int
or eax,eax ; 13 vector if there is one
jz spec_query_failed ; and try other workarounds
mov [13h*4],eax ;
jmp spec_query_failed ;
;
award_found: mov eax,[es:di+0eh] ; load possible int 13 addr
pop es ; restore ES
;
cmp eax,[award_oldint13] ; give up if this is the
jz award_failed ; active int 13 vector,
mov [13h*4],eax ; otherwise change 0:13h*4
;
;
%ifdef DEBUG_MESSAGES ;
;
push eax ; display message and
mov si,award_not_new ; new vector address
call writemsg ;
pop eax ;
call writehex8 ;
mov si,award_not_crlf ;
call writestr_early ;
%endif ;
mov ax,4B01h ; try to read the spec packet
mov dl,[DriveNumber] ; now ... it should not fail
mov si,spec_packet ; any longer
int 13h ;
jc award_fail2 ;
;
%ifdef DEBUG_MESSAGES ;
;
mov si,award_not_succ ; display our SUCCESS
call writemsg ;
%endif ;
jmp found_drive ; and leave error recovery code
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INT 13h, AX=4B01h, DL=<passed in value> failed.
; Try to scan the entire 80h-FFh from the end.
spec_query_failed:
; some code moved to BrokenAwardHack
mov dl,0FFh
.test_loop: pusha
mov ax,4B01h
mov si,spec_packet
mov byte [si],13h ; Size of buffer
call int13
popa
jc .still_broken
mov si,maybe_msg
call writemsg
mov al,dl
call writehex2
call crlf_early
cmp byte [sp_drive],dl
jne .maybe_broken
; Okay, good enough...
mov si,alright_msg
call writemsg
.found_drive0: mov [DriveNumber],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:
mov al,[DriveNumber]
cmp al,dl
je .found_drive
; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
; passes garbage in sp_drive, and the drive number originally
; passed in DL does not have 80h bit set.
or al,80h
cmp al,dl
je .found_drive0
.still_broken: dec dx
cmp dl, 80h
jnb .test_loop
; No spec packet anywhere. Some particularly pathetic
; BIOSes apparently don't even implement function
; 4B01h, so we can't query a spec packet no matter
; what. If we got a drive number in DL, then try to
; use it, and if it works, then well...
mov dl,[DriveNumber]
cmp dl,81h ; Should be 81-FF at least
jb fatal_error ; If not, it's hopeless
; Write a warning to indicate we're on *very* thin ice now
mov si,nospec_msg
call writemsg
mov al,dl
call writehex2
call crlf_early
mov si,trysbm_msg
call writemsg
jmp .found_drive ; Pray that this works...
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_early
pop si
call writestr_early
pop ax
ret
writestr_early:
pushfd
pushad
.top: lodsb
and al,al
jz .end
call writechr
jmp short .top
.end: popad
popfd
ret
crlf_early: push ax
mov al,CR
call writechr
mov al,LF
call writechr
pop ax
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:
.simple:
pushfd
pushad
mov ah,0Eh
xor bx,bx
int 10h
popad
popfd
ret
;
; int13: save all the segment registers and call INT 13h.
; Some CD-ROM BIOSes have been found to corrupt segment registers
; and/or disable interrupts.
;
int13:
pushf
push bp
push ds
push es
push fs
push gs
int 13h
mov bp,sp
setc [bp+10] ; Propagate CF to the caller
pop gs
pop fs
pop es
pop ds
pop bp
popf
ret
;
; Get one sector. Convenience entry point.
;
getonesec:
mov bp,1
; Fall through to getlinsec
;
; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
;
; Input:
; EAX - Linear sector number
; ES:BX - Target buffer
; BP - Sector count
;
global getlinsec
getlinsec: jmp word [cs:GetlinsecPtr]
%ifndef DEBUG_MESSAGES
;
; First, the variants that we use when actually loading off a disk
; (hybrid mode.) These are adapted versions of the equivalent routines
; in ldlinux.asm.
;
;
; getlinsec_ebios:
;
; getlinsec implementation for floppy/HDD EBIOS (EDD)
;
getlinsec_ebios:
xor edx,edx
shld edx,eax,2
shl eax,2 ; Convert to HDD sectors
add eax,[Hidden]
adc edx,[Hidden+4]
shl bp,2
.loop:
push bp ; Sectors left
.retry2:
call maxtrans ; Enforce maximum transfer size
movzx edi,bp ; Sectors we are about to read
mov cx,retry_count
.retry:
; Form DAPA on stack
push edx
push eax
push es
push bx
push di
push word 16
mov si,sp
pushad
mov dl,[DriveNumber]
push ds
push ss
pop ds ; DS <- SS
mov ah,42h ; Extended Read
call int13
pop ds
popad
lea sp,[si+16] ; Remove DAPA
jc .error
pop bp
add eax,edi ; Advance sector pointer
adc edx,0
sub bp,di ; Sectors left
shl di,9 ; 512-byte sectors
add bx,di ; Advance buffer pointer
and bp,bp
jnz .loop
ret
.error:
; Some systems seem to get "stuck" in an error state when
; using EBIOS. Doesn't happen when using CBIOS, which is
; good, since some other systems get timeout failures
; waiting for the floppy disk to spin up.
pushad ; Try resetting the device
xor ax,ax
mov dl,[DriveNumber]
call int13
popad
loop .retry ; CX-- and jump if not zero
;shr word [MaxTransfer],1 ; Reduce the transfer size
;jnz .retry2
; Total failure. Try falling back to CBIOS.
mov word [GetlinsecPtr], getlinsec_cbios
;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
pop bp
jmp getlinsec_cbios.loop
;
; getlinsec_cbios:
;
; getlinsec implementation for legacy CBIOS
;
getlinsec_cbios:
xor edx,edx
shl eax,2 ; Convert to HDD sectors
add eax,[Hidden]
shl bp,2
.loop:
push edx
push eax
push bp
push bx
movzx esi,word [bsSecPerTrack]
movzx edi,word [bsHeads]
;
; Dividing by sectors to get (track,sector): we may have
; up to 2^18 tracks, so we need to use 32-bit arithmetric.
;
div esi
xor cx,cx
xchg cx,dx ; CX <- sector index (0-based)
; EDX <- 0
; eax = track #
div edi ; Convert track to head/cyl
; We should test this, but it doesn't fit...
; cmp eax,1023
; ja .error
;
; Now we have AX = cyl, DX = head, CX = sector (0-based),
; BP = sectors to transfer, SI = bsSecPerTrack,
; ES:BX = data target
;
call maxtrans ; Enforce maximum transfer size
; Must not cross track boundaries, so BP <= SI-CX
sub si,cx
cmp bp,si
jna .bp_ok
mov bp,si
.bp_ok:
shl ah,6 ; Because IBM was STOOPID
; and thought 8 bits were enough
; then thought 10 bits were enough...
inc cx ; Sector numbers are 1-based, sigh
or cl,ah
mov ch,al
mov dh,dl
mov dl,[DriveNumber]
xchg ax,bp ; Sector to transfer count
mov ah,02h ; Read sectors
mov bp,retry_count
.retry:
pushad
call int13
popad
jc .error
.resume:
movzx ecx,al ; ECX <- sectors transferred
shl ax,9 ; Convert sectors in AL to bytes in AX
pop bx
add bx,ax
pop bp
pop eax
pop edx
add eax,ecx
sub bp,cx
jnz .loop
ret
.error:
dec bp
jnz .retry
xchg ax,bp ; Sectors transferred <- 0
shr word [MaxTransfer],1
jnz .resume
jmp disk_error
;
; Truncate BP to MaxTransfer
;
maxtrans:
cmp bp,[MaxTransfer]
jna .ok
mov bp,[MaxTransfer]
.ok: ret
%endif
;
; This is the variant we use for real CD-ROMs:
; LBA, 2K sectors, some special error handling.
;
getlinsec_cdrom:
mov si,dapa ; Load up the DAPA
mov [si+4],bx
mov [si+6],es
mov [si+8],eax
.loop:
push bp ; Sectors left
cmp bp,[MaxTransferCD]
jbe .bp_ok
mov bp,[MaxTransferCD]
.bp_ok:
mov [si+2],bp
push si
mov dl,[DriveNumber]
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,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment
add [si+6],ax ; Advance buffer pointer
and bp,bp
jnz .loop
mov eax,[si+8] ; Next sector
ret
; INT 13h with retry
xint13: mov byte [RetryCount],retry_count
.try: pushad
call int13
jc .error
add sp,byte 8*4 ; Clean up stack
ret
.error:
mov [DiskError],ah ; Save error code
popad
mov [DiskSys],ax ; Save system call number
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 [MaxTransferCD],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,oncall_str
call writestr_early
mov ax,[DiskSys]
call writehex4
mov si,ondrive_str
call writestr_early
mov al,dl
call writehex2
call crlf_early
; Fall through to kaboom
;
; kaboom: write a message and bail out. Wait for a user keypress,
; then do a hard reboot.
;
global kaboom
disk_error:
kaboom:
RESET_STACK_AND_SEGS AX
mov si,bailmsg
pm_call pm_writestr
pm_call pm_getchar
cli
mov word [BIOS_magic],0 ; Cold reboot
jmp 0F000h:0FFF0h ; Reset vector address
; -----------------------------------------------------------------------------
; Common modules needed in the first sector
; -----------------------------------------------------------------------------
%include "writehex.inc" ; Hexadecimal output
; -----------------------------------------------------------------------------
; Data that needs to be in the first sector
; -----------------------------------------------------------------------------
global syslinux_banner, copyright_str
syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0
copyright_str db ' Copyright (C) 1994-'
asciidec YEAR
db ' H. Peter Anvin et al', CR, LF, 0
isolinux_str db 'isolinux: ', 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 ', 0
offset_msg: db 'Main image LBA = ', 0
verify_msg: db 'Image csum verified.', CR, LF, 0
allread_msg db 'Image read, jumping to main code...', CR, LF, 0
%endif
noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0
noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', 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 reasonable, continuing...', CR, LF, 0
nospec_msg db 'Extremely broken BIOS detected, last attempt with drive = ', 0
nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF
trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0
diskerr_msg: db 'Disk error ', 0
oncall_str: db ', AX = ',0
ondrive_str: db ', drive ', 0
checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0
err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'
bailmsg equ err_bootfailed
crlf_msg db CR, LF
null_msg db 0
bios_cdrom_str db 'ETCD', 0
%ifndef DEBUG_MESSAGES
bios_cbios_str db 'CHDD', 0
bios_ebios_str db 'EHDD' ,0
%endif
alignz 4
global bios_cdrom
bios_cdrom: dw getlinsec_cdrom, bios_cdrom_str
%ifndef DEBUG_MESSAGES
bios_cbios: dw getlinsec_cbios, bios_cbios_str
bios_ebios: dw getlinsec_ebios, bios_ebios_str
%endif
; Maximum transfer size
MaxTransfer dw 127 ; Hard disk modes
MaxTransferCD dw 32 ; CD mode
rl_checkpt equ $ ; Must be <= 800h
; This pads to the end of sector 0 and errors out on
; overflow.
times 2048-($-$$) db 0
; ----------------------------------------------------------------------------
; End of code and data that have to be in the first sector
; ----------------------------------------------------------------------------
section .text16
all_read:
; Test tracers
TRACER 'T'
TRACER '>'
;
; Common initialization code
;
%include "init.inc"
; Tell the user we got this far...
%ifndef DEBUG_MESSAGES ; Gets messy with debugging on
mov si,copyright_str
pm_call pm_writestr
%endif
;
; Now we're all set to start with our *real* business. First load the
; configuration file (if any) and parse it.
;
; In previous versions I avoided using 32-bit registers because of a
; rumour some BIOSes clobbered the upper half of 32-bit registers at
; random. I figure, though, that if there are any of those still left
; they probably won't be trying to install Linux on them...
;
; The code is still ripe with 16-bitisms, though. Not worth the hassle
; to take'm out. In fact, we may want to put them back if we're going
; to boot ELKS at some point.
;
;
; 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.
;
init_fs:
pushad
mov eax,ROOT_FS_OPS
mov dl,[DriveNumber]
cmp word [BIOSType],bios_cdrom
sete dh ; 1 for cdrom, 0 for hybrid mode
jne .hybrid
movzx ebp,word [MaxTransferCD]
jmp .common
.hybrid:
movzx ebp,word [MaxTransfer]
.common:
mov ecx,[Hidden]
mov ebx,[Hidden+4]
mov si,[bsHeads]
mov di,[bsSecPerTrack]
pm_call pm_fs_init
pm_call load_env32
enter_command:
auto_boot:
jmp kaboom ; load_env32() should never return. If
; it does, then kaboom!
popad
section .rodata
alignz 4
ROOT_FS_OPS:
extern iso_fs_ops
dd iso_fs_ops
dd 0
section .text16
%ifdef DEBUG_TRACERS
;
; debug hack to print a character with minimal code impact
;
debug_tracer: pushad
pushfd
mov bp,sp
mov bx,[bp+9*4] ; Get return address
mov al,[cs:bx] ; Get data byte
inc word [bp+9*4] ; Return to after data byte
call writechr
popfd
popad
ret
%endif ; DEBUG_TRACERS
section .bss16
alignb 4
ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
KernelExtPtr resw 1 ; During search, final null pointer
FuncFlag resb 1 ; Escape sequences received from keyboard
KernelType resb 1 ; Kernel type, from vkernel, if known
global KernelName
KernelName resb FILENAME_MAX ; Mangled name for kernel
section .text16
;
; COM32 vestigial data structure
;
%include "com32.inc"
;
; Common local boot code
;
%include "localboot.inc"
; -----------------------------------------------------------------------------
; Common modules
; -----------------------------------------------------------------------------
%include "common.inc" ; Universal modules
; -----------------------------------------------------------------------------
; Begin data section
; -----------------------------------------------------------------------------
section .data16
err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
section .bss16
global OrigFDCTabPtr
OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest