diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-09-05 14:49:42 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2008-09-05 14:49:42 -0700 |
commit | 058dd028ac7d8ae217d1b3b00213751d63034195 (patch) | |
tree | 53bd1e95555370d939258873062db00c7d74bf7d /core/isolinux.asm | |
parent | 1eb0d6f7cfa7547470580d821ca815e20df4e93e (diff) | |
download | syslinux-058dd028ac7d8ae217d1b3b00213751d63034195.tar.gz |
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/isolinux.asm')
-rw-r--r-- | core/isolinux.asm | 255 |
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 |