Home | History | Annotate | Download | only in core
      1 ;; -----------------------------------------------------------------------
      2 ;;
      3 ;;   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
      4 ;;
      5 ;;   This program is free software; you can redistribute it and/or modify
      6 ;;   it under the terms of the GNU General Public License as published by
      7 ;;   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
      8 ;;   Boston MA 02110-1301, USA; either version 2 of the License, or
      9 ;;   (at your option) any later version; incorporated herein by reference.
     10 ;;
     11 ;; -----------------------------------------------------------------------
     12 
     13 ;;
     14 ;; adv.inc
     15 ;;
     16 ;; The auxillary data vector and its routines
     17 ;;
     18 ;; The auxillary data vector is a 512-byte aligned block that on the
     19 ;; disk-based derivatives can be part of the syslinux file itself.  It
     20 ;; exists in two copies; when written, both copies are written (with a
     21 ;; sync in between, if from the operating system.)  The first two
     22 ;; dwords are magic number and inverse checksum, then follows the data
     23 ;; area as a tagged array similar to BOOTP/DHCP, finally a tail
     24 ;; signature.
     25 ;;
     26 ;; Note that unlike BOOTP/DHCP, zero terminates the chain, and FF
     27 ;; has no special meaning.
     28 ;;
     29 
     30 ;;
     31 ;; List of ADV tags...
     32 ;;
     33 ADV_BOOTONCE	equ 1
     34 
     35 ;;
     36 ;; Other ADV data...
     37 ;;
     38 ADV_MAGIC1	equ 0x5a2d2fa5			; Head signature
     39 ADV_MAGIC2	equ 0xa3041767			; Total checksum
     40 ADV_MAGIC3	equ 0xdd28bf64			; Tail signature
     41 
     42 ADV_LEN		equ 500				; Data bytes
     43 
     44 adv_retries	equ 6				; Disk retries
     45 
     46 		section .data
     47 		global __syslinux_adv_ptr, __syslinux_adv_size
     48 __syslinux_adv_ptr:
     49 		dd adv0.data
     50 __syslinux_adv_size:
     51 		dd ADV_LEN
     52 
     53 		section .adv
     54 		; Introduce the ADVs to valid but blank
     55 adv0:
     56 .head		resd 1
     57 .csum		resd 1
     58 .data		resb ADV_LEN
     59 .tail		resd 1
     60 .end		equ $
     61 adv1:
     62 .head		resd 1
     63 .csum		resd 1
     64 .data		resb ADV_LEN
     65 .tail		resd 1
     66 .end		equ $
     67 		section .text16
     68 
     69 		;
     70 		; This is called after config file parsing, so we know
     71 		; the intended location of the ADV
     72 		;
     73 		global adv_init
     74 adv_init:
     75 		cmp byte [ADVDrive],-1
     76 		jne adv_read
     77 
     78 %if IS_SYSLINUX || IS_EXTLINUX
     79 		cmp word [ADVSectors],2		; Not present?
     80 		jb adv_verify
     81 
     82 		mov eax,[Hidden]
     83 		mov edx,[Hidden+4]
     84 		add [ADVSec0],eax
     85 		adc [ADVSec0+4],edx
     86 		add [ADVSec1],eax
     87 		adc [ADVSec1+4],edx
     88 		mov al,[DriveNumber]
     89 		mov [ADVDrive],al
     90 		jmp adv_read
     91 %endif
     92 
     93 		;
     94 		; Initialize the ADV data structure in memory
     95 		;
     96 adv_verify:
     97 		cmp byte [ADVDrive],-1		; No ADV configured, still?
     98 		je .reset			; Then unconditionally reset
     99 
    100 		mov si,adv0
    101 		call .check_adv
    102 		jz .ok				; Primary ADV okay
    103 		mov si,adv1
    104 		call .check_adv
    105 		jz .adv1ok
    106 
    107 		; Neither ADV is usable; initialize to blank
    108 .reset:
    109 		mov di,adv0
    110 		mov eax,ADV_MAGIC1
    111 		stosd
    112 		mov eax,ADV_MAGIC2
    113 		stosd
    114 		xor eax,eax
    115 		mov cx,ADV_LEN/4
    116 		rep stosd
    117 		mov eax,ADV_MAGIC3
    118 		stosd
    119 
    120 .ok:
    121 		ret
    122 
    123 		; The primary ADV is bad, but the backup is OK
    124 .adv1ok:
    125 		mov di,adv0
    126 		mov cx,512/4
    127 		rep movsd
    128 		ret
    129 
    130 
    131 		; SI points to the putative ADV; unchanged by routine
    132 		; ZF=1 on return if good
    133 .check_adv:
    134 		push si
    135 		lodsd
    136 		cmp eax,ADV_MAGIC1
    137 		jne .done			; ZF=0, i.e. bad
    138 		xor edx,edx
    139 		mov cx,ADV_LEN/4+1		; Remaining dwords
    140 .csum:
    141 		lodsd
    142 		add edx,eax
    143 		loop .csum
    144 		cmp edx,ADV_MAGIC2
    145 		jne .done
    146 		lodsd
    147 		cmp eax,ADV_MAGIC3
    148 .done:
    149 		pop si
    150 		ret
    151 
    152 ;
    153 ; adv_get: find an ADV string if present
    154 ;
    155 ; Input:	DL	= ADV ID
    156 ; Output:	CX	= byte count (zero on not found)
    157 ;		SI	= pointer to data
    158 ;		DL	= unchanged
    159 ;
    160 ; Assumes CS == DS.
    161 ;
    162 
    163 adv_get:
    164 		push ax
    165 		mov si,adv0.data
    166 		xor ax,ax			; Keep AH=0 at all times
    167 .loop:
    168 		lodsb				; Read ID
    169 		cmp al,dl
    170 		je .found
    171 		and al,al
    172 		jz .end
    173 		lodsb				; Read length
    174 		add si,ax
    175 		cmp si,adv0.tail
    176 		jb .loop
    177 		jmp .end
    178 
    179 .found:
    180 		lodsb
    181 		mov cx,ax
    182 		add ax,si			; Make sure it fits
    183 		cmp ax,adv0.tail
    184 		jbe .ok
    185 .end:
    186 		xor cx,cx
    187 .ok:
    188 		pop ax
    189 		ret
    190 
    191 ;
    192 ; adv_set: insert a string into the ADV in memory
    193 ;
    194 ; Input:	DL	= ADV ID
    195 ;		FS:BX	= input buffer
    196 ;		CX	= byte count (max = 255!)
    197 ; Output:	CF=1 on error
    198 ;		CX	clobbered
    199 ;
    200 ; Assumes CS == DS == ES.
    201 ;
    202 adv_set:
    203 		push ax
    204 		push si
    205 		push di
    206 		and ch,ch
    207 		jnz .overflow
    208 
    209 		push cx
    210 		mov si,adv0.data
    211 		xor ax,ax
    212 .loop:
    213 		lodsb
    214 		cmp al,dl
    215 		je .found
    216 		and al,al
    217 		jz .endz
    218 		lodsb
    219 		add si,ax
    220 		cmp si,adv0.tail
    221 		jb .loop
    222 		jmp .end
    223 
    224 .found:		; Found, need to delete old copy
    225 		lodsb
    226 		lea di,[si-2]
    227 		push di
    228 		add si,ax
    229 		mov cx,adv0.tail
    230 		sub cx,si
    231 		jb .nukeit
    232 		rep movsb			; Remove the old one
    233 		mov [di],ah			; Termination zero
    234 		pop si
    235 		jmp .loop
    236 .nukeit:
    237 		pop si
    238 		jmp .end
    239 .endz:
    240 		dec si
    241 .end:
    242 		; Now SI points to where we want to put our data
    243 		pop cx
    244 		mov di,si
    245 		jcxz .empty
    246 		add si,cx
    247 		cmp si,adv0.tail-2
    248 		jae .overflow			; CF=0
    249 
    250 		mov si,bx
    251 		mov al,dl
    252 		stosb
    253 		mov al,cl
    254 		stosb
    255 		fs rep movsb
    256 
    257 .empty:
    258 		mov cx,adv0.tail
    259 		sub cx,di
    260 		xor ax,ax
    261 		rep stosb			; Zero-fill remainder
    262 
    263 		clc
    264 .done:
    265 		pop di
    266 		pop si
    267 		pop ax
    268 		ret
    269 .overflow:
    270 		stc
    271 		jmp .done
    272 
    273 ;
    274 ; adv_cleanup:	checksum adv0 and copy to adv1
    275 ;		Assumes CS == DS == ES.
    276 ;
    277 adv_cleanup:
    278 		pushad
    279 		mov si,adv0.data
    280 		mov cx,ADV_LEN/4
    281 		xor edx,edx
    282 .loop:
    283 		lodsd
    284 		add edx,eax
    285 		loop .loop
    286 		mov eax,ADV_MAGIC2
    287 		sub eax,edx
    288 		lea di,[si+4]			; adv1
    289 		mov si,adv0
    290 		mov [si+4],eax			; Store checksum
    291 		mov cx,(ADV_LEN+12)/4
    292 		rep movsd
    293 		popad
    294 		ret
    295 
    296 ;
    297 ; adv_write:	write the ADV to disk.
    298 ;
    299 ;		Location is in memory variables.
    300 ;		Assumes CS == DS == ES.
    301 ;
    302 ;		Returns CF=1 if the ADV cannot be written.
    303 ;
    304 		global adv_write
    305 adv_write:
    306 		push eax
    307 		mov eax,[ADVSec0]
    308 		or eax,[ADVSec0+4]
    309 		je .bad
    310 		mov eax,[ADVSec1]
    311 		or eax,[ADVSec1+4]
    312 		je .bad
    313 		cmp byte [ADVDrive],-1
    314 		je .bad
    315 
    316 		call adv_cleanup
    317 		mov ah,3			; Write
    318 		call adv_read_write
    319 
    320 		clc
    321 		pop eax
    322 		ret
    323 .bad:						; No location for ADV set
    324 		stc
    325 		pop eax
    326 		ret
    327 
    328 ;
    329 ; adv_read:	read the ADV from disk
    330 ;
    331 ;		Location is in memory variables.
    332 ;		Assumes CS == DS == ES.
    333 ;
    334 adv_read:
    335 		push ax
    336 		mov ah,2			; Read
    337 		call adv_read_write
    338 		call adv_verify
    339 		pop ax
    340 		ret
    341 
    342 ;
    343 ; adv_read_write: disk I/O for the ADV
    344 ;
    345 ;		On input, AH=2 for read, AH=3 for write.
    346 ;		Assumes CS == DS == ES.
    347 ;
    348 adv_read_write:
    349 		mov [ADVOp],ah
    350 		pushad
    351 
    352 		; Check for EDD
    353 		mov bx,55AAh
    354 		mov ah,41h			; EDD existence query
    355 		mov dl,[ADVDrive]
    356 		int 13h
    357 		mov si,.cbios
    358 		jc .noedd
    359 		cmp bx,0AA55h
    360 		jne .noedd
    361 		test cl,1
    362 		jz .noedd
    363 		mov si,.ebios
    364 .noedd:
    365 
    366 		mov eax,[ADVSec0]
    367 		mov edx,[ADVSec0+4]
    368 		mov bx,adv0
    369 		call .doone
    370 
    371 		mov eax,[ADVSec1]
    372 		mov edx,[ADVSec1+4]
    373 		mov bx,adv1
    374 		call .doone
    375 
    376 		popad
    377 		ret
    378 
    379 .doone:
    380 		push si
    381 		jmp si
    382 
    383 .ebios:
    384 		mov cx,adv_retries
    385 .eb_retry:
    386 		; Form DAPA on stack
    387 		push edx
    388 		push eax
    389 		push es
    390 		push bx
    391 		push word 1			; Sector count
    392 		push word 16			; DAPA size
    393 		mov si,sp
    394 		pushad
    395 		mov dl,[ADVDrive]
    396 		mov ax,4000h
    397 		or ah,[ADVOp]
    398 		push ds
    399 		push ss
    400 		pop ds
    401 		int 13h
    402 		pop ds
    403 		popad
    404 		lea sp,[si+16]			; Remove DAPA
    405 		jc .eb_error
    406 		pop si
    407 		ret
    408 .eb_error:
    409 		loop .eb_retry
    410 		stc
    411 		pop si
    412 		ret
    413 
    414 .cbios:
    415 		push edx
    416 		push eax
    417 		push bp
    418 
    419 		and edx,edx			; > 2 TiB not possible
    420 		jnz .cb_overflow
    421 
    422 		mov dl,[ADVDrive]
    423 		and dl,dl
    424 		; Floppies: can't trust INT 13h 08h, we better know
    425 		; the geometry a priori, which means it better be our
    426 		; boot device...
    427 		jns .noparm			; Floppy drive... urk
    428 
    429 		mov ah,08h			; Get disk parameters
    430 		int 13h
    431 		jc .noparm
    432 		and ah,ah
    433 		jnz .noparm
    434 		shr dx,8
    435 		inc dx
    436 		movzx edi,dx			; EDI = heads
    437 		and cx,3fh
    438 		movzx esi,cx			; ESI = sectors/track
    439 		jmp .parmok
    440 
    441 .noparm:
    442 		; No CHS info... this better be our boot drive, then
    443 %if IS_SYSLINUX || IS_EXTLINUX
    444 		cmp dl,[DriveNumber]
    445 		jne .cb_overflow		; Fatal error!
    446 		movzx esi,word [bsSecPerTrack]
    447 		movzx edi,word [bsHeads]
    448 %else
    449 		; Not a disk-based derivative... there is no hope
    450 		jmp .cb_overflow
    451 %endif
    452 
    453 .parmok:
    454                 ;
    455                 ; Dividing by sectors to get (track,sector): we may have
    456                 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
    457                 ;
    458 		xor edx,edx
    459                 div esi
    460                 xor cx,cx
    461                 xchg cx,dx              ; CX <- sector index (0-based)
    462                                         ; EDX <- 0
    463                 ; eax = track #
    464                 div edi                 ; Convert track to head/cyl
    465 
    466 		; Watch out for overflow, we might be writing!
    467 		cmp eax,1023
    468                 ja .cb_overflow
    469 
    470                 ;
    471                 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
    472                 ; BP = sectors to transfer, SI = bsSecPerTrack,
    473                 ; ES:BX = data target
    474                 ;
    475 
    476                 shl ah,6                ; Because IBM was STOOPID
    477                                         ; and thought 8 bits were enough
    478                                         ; then thought 10 bits were enough...
    479                 inc cx                  ; Sector numbers are 1-based, sigh
    480                 or cl,ah
    481                 mov ch,al
    482                 mov dh,dl
    483                 mov dl,[ADVDrive]
    484 		mov al,01h		; Transfer one sector
    485                 mov ah,[ADVOp]		; Operation
    486 
    487 		mov bp,adv_retries
    488 .cb_retry:
    489                 pushad
    490                 int 13h
    491                 popad
    492                 jc .cb_error
    493 
    494 .cb_done:
    495                 pop bp
    496                 pop eax
    497                 pop edx
    498 		pop si
    499                 ret
    500 
    501 .cb_error:
    502                 dec bp
    503                 jnz .cb_retry
    504 .cb_overflow:
    505 		stc
    506 		jmp .cb_done
    507 
    508 		section .data16
    509 		alignz 8
    510 ADVSec0		dq 0			; Not specified
    511 ADVSec1		dq 0			; Not specified
    512 ADVDrive	db -1			; No ADV defined
    513 ADVCHSInfo	db -1			; We have CHS info for this drive
    514 
    515 		section .bss16
    516 ADVOp		resb 1
    517 
    518 		section .text16
    519