aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-09-05 14:49:42 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-09-05 14:49:42 -0700
commit058dd028ac7d8ae217d1b3b00213751d63034195 (patch)
tree53bd1e95555370d939258873062db00c7d74bf7d /core
parent1eb0d6f7cfa7547470580d821ca815e20df4e93e (diff)
downloadsyslinux-elf-058dd028ac7d8ae217d1b3b00213751d63034195.tar.gz
syslinux-elf-058dd028ac7d8ae217d1b3b00213751d63034195.tar.xz
syslinux-elf-058dd028ac7d8ae217d1b3b00213751d63034195.zip
ISOLINUX: support for hybrid mode (CD-ROM/USB key)
Still a work in progress. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core')
-rw-r--r--core/isolinux.asm255
1 files changed, 238 insertions, 17 deletions
diff --git a/core/isolinux.asm b/core/isolinux.asm
index f93b24e6..060a3cf8 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -118,10 +118,13 @@ 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
+GetlinsecPtr resw 1 ; The sector-read pointer
DiskError resb 1 ; Error code for disk I/O
-DriveNumber resb 1 ; CD-ROM BIOS drive number
+DriveNumber resb 1 ; CD-ROM BIOS drive number
ISOFlags resb 1 ; Flags for ISO directory search
RetryCount resb 1 ; Used for disk access retries
+bsSecPerTrack resw 1 ; Used in hybrid mode
+bsHeads resw 1 ; Used in hybrid mode
_spec_start equ $
@@ -225,12 +228,46 @@ 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:
+ ; - CBIOS Heads
+ ; - CBIOS Sectors
+ ; - CBIOS flag
+ ; - DX (including drive number)
+ ; - DI
+ ; - ES
+ ; (top of stack)
+%ifndef DEBUG_MESSAGES
+_hybrid_signature:
+ dd 0x7078c0fb ; An arbitrary number...
+
+_start_hybrid:
+ pop ax
+ mov si,getlinsec_ebios
+ and ax,ax
+ jnz .ebios
+ mov si,getlinsec_cbios
+.ebios:
+ pop word [cs:bsSecPerTrack]
+ pop word [cs:bsHeads]
+
+ pop dx
+ pop di
+ pop es
+ jmp _start_common
+%endif
-_start1: mov [cs:InitStack],sp ; Save initial stack pointer
+_start1:
+ mov si,getlinsec_cdrom
+_start_common:
+ mov [cs:InitStack],sp ; Save initial stack pointer
mov [cs:InitStack+2],ss
xor ax,ax
mov ss,ax
- mov sp,StackBuf ; Set up stack
+ mov sp,StackBuf ; Set up stack
push es ; Save initial ES:DI -> $PnP pointer
push di
mov ds,ax
@@ -238,8 +275,10 @@ _start1: mov [cs:InitStack],sp ; Save initial stack pointer
mov fs,ax
mov gs,ax
sti
-
cld
+
+ mov [GetlinsecPtr],si
+
; Show signs of life
mov si,syslinux_banner
call writestr_early
@@ -253,7 +292,7 @@ _start1: mov [cs:InitStack],sp ; Save initial stack pointer
; 64-2048
;
initial_csum: xor edi,edi
- mov si,_start1
+ mov si,bi_end
mov cx,(SECTOR_SIZE-64) >> 2
.loop: lodsd
add edi,eax
@@ -285,6 +324,10 @@ initial_csum: xor edi,edi
; Other nonzero fields
inc word [dsp_sectors]
+ ; Are we just pretending to be a CD-ROM?
+ cmp word [GetlinsecPtr],getlinsec_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
@@ -333,7 +376,7 @@ found_drive:
%endif
; No such luck. Get the Boot Record Volume, assuming single
- ; session disk, and that we're the first entry in the chain
+ ; session disk, and that we're the first entry in the chain.
mov eax,17 ; Assumed address of BRV
mov bx,trackbuf
call getonesec
@@ -356,7 +399,7 @@ set_file:
found_file:
; Set up boot file sizes
mov eax,[bi_length]
- sub eax,SECTOR_SIZE-3
+ sub eax,SECTOR_SIZE-3 ; ... minus sector loaded
shr eax,2 ; bytes->dwords
mov [ImageDwords],eax ; boot file dwords
add eax,(2047 >> 2)
@@ -685,16 +728,191 @@ getonesec:
;
; 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:
+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:
+ shl eax,2 ; Convert to HDD sectors
+ 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
+ int 13h
+ pop ds
+ popad
+ lea sp,[si+16] ; Remove DAPA
+ jc .error
+ pop bp
+ add eax,edi ; Advance sector pointer
+ 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]
+ int 13h
+ 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:
+ shl eax,2 ; Convert to HDD sectors
+ 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
+ int 13h
+ 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 bx,es
@@ -774,6 +992,7 @@ xint13: mov byte [RetryCount],retry_count
; kaboom: write a message and bail out. Wait for a user keypress,
; then do a hard reboot.
;
+disk_error:
kaboom:
RESET_STACK_AND_SEGS AX
mov si,err_bootfailed
@@ -833,11 +1052,13 @@ MaxTransfer dw 32 ; Max sectors per transfer
rl_checkpt equ $ ; Must be <= 800h
rl_checkpt_off equ ($-$$)
-;%ifndef DEPEND
-;%if rl_checkpt_off > 0x800
-;%error "Sector 0 overflow"
-;%endif
-;%endif
+%ifndef DEPEND
+%if rl_checkpt_off > 0x800
+; This only works for NASM 2.03+, but it's really nice then...
+%assign SPILL rl_checkpt_off-0x800
+%error Sector 0 overflow by SPILL bytes
+%endif
+%endif
; ----------------------------------------------------------------------------
; End of code and data that have to be in the first sector