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;;
33ADV_BOOTONCE	equ 1
34
35;;
36;; Other ADV data...
37;;
38ADV_MAGIC1	equ 0x5a2d2fa5			; Head signature
39ADV_MAGIC2	equ 0xa3041767			; Total checksum
40ADV_MAGIC3	equ 0xdd28bf64			; Tail signature
41
42ADV_LEN		equ 500				; Data bytes
43
44adv_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
55adv0:
56.head		resd 1
57.csum		resd 1
58.data		resb ADV_LEN
59.tail		resd 1
60.end		equ $
61adv1:
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
74adv_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		;
96adv_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
163adv_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;
202adv_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;
277adv_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
305adv_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;
334adv_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;
348adv_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
510ADVSec0		dq 0			; Not specified
511ADVSec1		dq 0			; Not specified
512ADVDrive	db -1			; No ADV defined
513ADVCHSInfo	db -1			; We have CHS info for this drive
514
515		section .bss16
516ADVOp		resb 1
517
518		section .text16
519