blob: a18020c5e1364f0df7387c722ecb481874bc7749 [file] [log] [blame]
;; -----------------------------------------------------------------------
;;
;; Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
;;
;; 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., 51 Franklin St, Fifth Floor,
;; Boston MA 02110-1301, USA; either version 2 of the License, or
;; (at your option) any later version; incorporated herein by reference.
;;
;; -----------------------------------------------------------------------
;;
;; adv.inc
;;
;; The auxiliary data vector and its routines
;;
;; The auxiliary data vector is a 512-byte aligned block that on the
;; disk-based derivatives can be part of the syslinux file itself. It
;; exists in two copies; when written, both copies are written (with a
;; sync in between, if from the operating system.) The first two
;; dwords are magic number and inverse checksum, then follows the data
;; area as a tagged array similar to BOOTP/DHCP, finally a tail
;; signature.
;;
;; Note that unlike BOOTP/DHCP, zero terminates the chain, and FF
;; has no special meaning.
;;
;;
;; List of ADV tags...
;;
ADV_BOOTONCE equ 1
;;
;; Other ADV data...
;;
ADV_MAGIC1 equ 0x5a2d2fa5 ; Head signature
ADV_MAGIC2 equ 0xa3041767 ; Total checksum
ADV_MAGIC3 equ 0xdd28bf64 ; Tail signature
ADV_LEN equ 500 ; Data bytes
adv_retries equ 6 ; Disk retries
section .data
global __syslinux_adv_ptr, __syslinux_adv_size
__syslinux_adv_ptr:
dd adv0.data
__syslinux_adv_size:
dd ADV_LEN
section .adv
; Introduce the ADVs to valid but blank
adv0:
.head resd 1
.csum resd 1
.data resb ADV_LEN
.tail resd 1
.end equ $
adv1:
.head resd 1
.csum resd 1
.data resb ADV_LEN
.tail resd 1
.end equ $
section .text16
;
; This is called after config file parsing, so we know
; the intended location of the ADV
;
global adv_init
adv_init:
cmp byte [ADVDrive],-1
jne adv_read
%if IS_SYSLINUX || IS_EXTLINUX
cmp word [ADVSectors],2 ; Not present?
jb adv_verify
mov eax,[Hidden]
mov edx,[Hidden+4]
add [ADVSec0],eax
adc [ADVSec0+4],edx
add [ADVSec1],eax
adc [ADVSec1+4],edx
mov al,[DriveNumber]
mov [ADVDrive],al
jmp adv_read
%endif
;
; Initialize the ADV data structure in memory
;
adv_verify:
cmp byte [ADVDrive],-1 ; No ADV configured, still?
je .reset ; Then unconditionally reset
mov si,adv0
call .check_adv
jz .ok ; Primary ADV okay
mov si,adv1
call .check_adv
jz .adv1ok
; Neither ADV is usable; initialize to blank
.reset:
mov di,adv0
mov eax,ADV_MAGIC1
stosd
mov eax,ADV_MAGIC2
stosd
xor eax,eax
mov cx,ADV_LEN/4
rep stosd
mov eax,ADV_MAGIC3
stosd
.ok:
ret
; The primary ADV is bad, but the backup is OK
.adv1ok:
mov di,adv0
mov cx,512/4
rep movsd
ret
; SI points to the putative ADV; unchanged by routine
; ZF=1 on return if good
.check_adv:
push si
lodsd
cmp eax,ADV_MAGIC1
jne .done ; ZF=0, i.e. bad
xor edx,edx
mov cx,ADV_LEN/4+1 ; Remaining dwords
.csum:
lodsd
add edx,eax
loop .csum
cmp edx,ADV_MAGIC2
jne .done
lodsd
cmp eax,ADV_MAGIC3
.done:
pop si
ret
;
; adv_get: find an ADV string if present
;
; Input: DL = ADV ID
; Output: CX = byte count (zero on not found)
; SI = pointer to data
; DL = unchanged
;
; Assumes CS == DS.
;
adv_get:
push ax
mov si,adv0.data
xor ax,ax ; Keep AH=0 at all times
.loop:
lodsb ; Read ID
cmp al,dl
je .found
and al,al
jz .end
lodsb ; Read length
add si,ax
cmp si,adv0.tail
jb .loop
jmp .end
.found:
lodsb
mov cx,ax
add ax,si ; Make sure it fits
cmp ax,adv0.tail
jbe .ok
.end:
xor cx,cx
.ok:
pop ax
ret
;
; adv_set: insert a string into the ADV in memory
;
; Input: DL = ADV ID
; FS:BX = input buffer
; CX = byte count (max = 255!)
; Output: CF=1 on error
; CX clobbered
;
; Assumes CS == DS == ES.
;
adv_set:
push ax
push si
push di
and ch,ch
jnz .overflow
push cx
mov si,adv0.data
xor ax,ax
.loop:
lodsb
cmp al,dl
je .found
and al,al
jz .endz
lodsb
add si,ax
cmp si,adv0.tail
jb .loop
jmp .end
.found: ; Found, need to delete old copy
lodsb
lea di,[si-2]
push di
add si,ax
mov cx,adv0.tail
sub cx,si
jb .nukeit
rep movsb ; Remove the old one
mov [di],ah ; Termination zero
pop si
jmp .loop
.nukeit:
pop si
jmp .end
.endz:
dec si
.end:
; Now SI points to where we want to put our data
pop cx
mov di,si
jcxz .empty
add si,cx
cmp si,adv0.tail-2
jae .overflow ; CF=0
mov si,bx
mov al,dl
stosb
mov al,cl
stosb
fs rep movsb
.empty:
mov cx,adv0.tail
sub cx,di
xor ax,ax
rep stosb ; Zero-fill remainder
clc
.done:
pop di
pop si
pop ax
ret
.overflow:
stc
jmp .done
;
; adv_cleanup: checksum adv0 and copy to adv1
; Assumes CS == DS == ES.
;
adv_cleanup:
pushad
mov si,adv0.data
mov cx,ADV_LEN/4
xor edx,edx
.loop:
lodsd
add edx,eax
loop .loop
mov eax,ADV_MAGIC2
sub eax,edx
lea di,[si+4] ; adv1
mov si,adv0
mov [si+4],eax ; Store checksum
mov cx,(ADV_LEN+12)/4
rep movsd
popad
ret
;
; adv_write: write the ADV to disk.
;
; Location is in memory variables.
; Assumes CS == DS == ES.
;
; Returns CF=1 if the ADV cannot be written.
;
global adv_write
adv_write:
push eax
mov eax,[ADVSec0]
or eax,[ADVSec0+4]
je .bad
mov eax,[ADVSec1]
or eax,[ADVSec1+4]
je .bad
cmp byte [ADVDrive],-1
je .bad
call adv_cleanup
mov ah,3 ; Write
call adv_read_write
clc
pop eax
ret
.bad: ; No location for ADV set
stc
pop eax
ret
;
; adv_read: read the ADV from disk
;
; Location is in memory variables.
; Assumes CS == DS == ES.
;
adv_read:
push ax
mov ah,2 ; Read
call adv_read_write
call adv_verify
pop ax
ret
;
; adv_read_write: disk I/O for the ADV
;
; On input, AH=2 for read, AH=3 for write.
; Assumes CS == DS == ES.
;
adv_read_write:
mov [ADVOp],ah
pushad
; Check for EDD
mov bx,55AAh
mov ah,41h ; EDD existence query
mov dl,[ADVDrive]
int 13h
mov si,.cbios
jc .noedd
cmp bx,0AA55h
jne .noedd
test cl,1
jz .noedd
mov si,.ebios
.noedd:
mov eax,[ADVSec0]
mov edx,[ADVSec0+4]
mov bx,adv0
call .doone
mov eax,[ADVSec1]
mov edx,[ADVSec1+4]
mov bx,adv1
call .doone
popad
ret
.doone:
push si
jmp si
.ebios:
mov cx,adv_retries
.eb_retry:
; Form DAPA on stack
push edx
push eax
push es
push bx
push word 1 ; Sector count
push word 16 ; DAPA size
mov si,sp
pushad
mov dl,[ADVDrive]
mov ax,4000h
or ah,[ADVOp]
push ds
push ss
pop ds
int 13h
pop ds
popad
lea sp,[si+16] ; Remove DAPA
jc .eb_error
pop si
ret
.eb_error:
loop .eb_retry
stc
pop si
ret
.cbios:
push edx
push eax
push bp
and edx,edx ; > 2 TiB not possible
jnz .cb_overflow
mov dl,[ADVDrive]
and dl,dl
; Floppies: can't trust INT 13h 08h, we better know
; the geometry a priori, which means it better be our
; boot device...
jns .noparm ; Floppy drive... urk
mov ah,08h ; Get disk parameters
int 13h
jc .noparm
and ah,ah
jnz .noparm
shr dx,8
inc dx
movzx edi,dx ; EDI = heads
and cx,3fh
movzx esi,cx ; ESI = sectors/track
jmp .parmok
.noparm:
; No CHS info... this better be our boot drive, then
%if IS_SYSLINUX || IS_EXTLINUX
cmp dl,[DriveNumber]
jne .cb_overflow ; Fatal error!
movzx esi,word [bsSecPerTrack]
movzx edi,word [bsHeads]
%else
; Not a disk-based derivative... there is no hope
jmp .cb_overflow
%endif
.parmok:
;
; Dividing by sectors to get (track,sector): we may have
; up to 2^18 tracks, so we need to use 32-bit arithmetric.
;
xor edx,edx
div esi
xor cx,cx
xchg cx,dx ; CX <- sector index (0-based)
; EDX <- 0
; eax = track #
div edi ; Convert track to head/cyl
; Watch out for overflow, we might be writing!
cmp eax,1023
ja .cb_overflow
;
; Now we have AX = cyl, DX = head, CX = sector (0-based),
; BP = sectors to transfer, SI = bsSecPerTrack,
; ES:BX = data target
;
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,[ADVDrive]
mov al,01h ; Transfer one sector
mov ah,[ADVOp] ; Operation
mov bp,adv_retries
.cb_retry:
pushad
int 13h
popad
jc .cb_error
.cb_done:
pop bp
pop eax
pop edx
pop si
ret
.cb_error:
dec bp
jnz .cb_retry
.cb_overflow:
stc
jmp .cb_done
section .data16
alignz 8
ADVSec0 dq 0 ; Not specified
ADVSec1 dq 0 ; Not specified
ADVDrive db -1 ; No ADV defined
ADVCHSInfo db -1 ; We have CHS info for this drive
section .bss16
ADVOp resb 1
section .text16