;; ----------------------------------------------------------------------- ;; ;; 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