Merge commit 'origin/master' into nolen
[profile/ivi/syslinux.git] / adv.inc
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 .adv
47                 ; Introduce the ADVs to valid but blank
48 adv0:
49 .head           resd 1
50 .csum           resd 1
51 .data           resb ADV_LEN
52 .tail           resd 1
53 .end            equ $
54 adv1:
55 .head           resd 1
56 .csum           resd 1
57 .data           resb ADV_LEN
58 .tail           resd 1
59 .end            equ $
60                 section .text
61
62                 ;
63                 ; This is called after config file parsing, so we know
64                 ; the intended location of the ADV
65                 ;
66 adv_init:
67                 cmp byte [ADVDrive],-1
68                 jne adv_read
69
70 ;%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
71 %if IS_EXTLINUX         ; Not yet implemented for the other derivatives
72                 ;
73                 ; Update pointers to default ADVs...
74                 ;
75                 mov bx,[LDLSectors]
76                 shl bx,2
77                 mov ecx,[bsHidden]
78                 mov eax,[bx+SectorPtrs-8]
79                 mov edx,[bx+SectorPtrs-4]
80                 add eax,ecx
81                 add edx,ecx
82                 mov [ADVSec0],eax
83                 mov [ADVSec1],edx
84                 mov al,[DriveNumber]
85                 mov [ADVDrive],al
86 %endif
87                 ; ** fall through to adv_verify **
88
89                 ;
90                 ; Initialize the ADV data structure in memory
91                 ;
92 adv_verify:
93                 cmp byte [ADVDrive],-1          ; No ADV configured, still?
94                 je .reset                       ; Then unconditionally reset
95
96                 mov si,adv0
97                 call .check_adv
98                 jz .ok                          ; Primary ADV okay
99                 mov si,adv1
100                 call .check_adv
101                 jz .adv1ok
102
103                 ; Neither ADV is usable; initialize to blank
104 .reset:
105                 mov di,adv0
106                 mov eax,ADV_MAGIC1
107                 stosd
108                 mov eax,ADV_MAGIC2
109                 stosd
110                 xor eax,eax
111                 mov cx,ADV_LEN/4
112                 rep stosd
113                 mov eax,ADV_MAGIC3
114                 stosd
115
116 .ok:
117                 ret
118
119                 ; The primary ADV is bad, but the backup is OK
120 .adv1ok:
121                 mov di,adv0
122                 mov cx,512/4
123                 rep movsd
124                 ret
125
126
127                 ; SI points to the putative ADV; unchanged by routine
128                 ; ZF=1 on return if good
129 .check_adv:
130                 push si
131                 lodsd
132                 cmp eax,ADV_MAGIC1
133                 jne .done                       ; ZF=0, i.e. bad
134                 xor edx,edx
135                 mov cx,ADV_LEN/4+1              ; Remaining dwords
136 .csum:
137                 lodsd
138                 add edx,eax
139                 loop .csum
140                 cmp edx,ADV_MAGIC2
141                 jne .done
142                 lodsd
143                 cmp eax,ADV_MAGIC3
144 .done:
145                 pop si
146                 ret
147
148 ;
149 ; adv_get: find an ADV string if present
150 ;
151 ; Input:        DL      = ADV ID
152 ; Output:       CX      = byte count (zero on not found)
153 ;               SI      = pointer to data
154 ;               DL      = unchanged
155 ;
156 ; Assumes CS == DS.
157 ;
158
159 adv_get:
160                 push ax
161                 mov si,adv0.data
162                 xor ax,ax                       ; Keep AH=0 at all times
163 .loop:
164                 lodsb                           ; Read ID
165                 cmp al,dl
166                 je .found
167                 and al,al
168                 jz .end
169                 lodsb                           ; Read length
170                 add si,ax
171                 cmp si,adv0.tail
172                 jb .loop
173                 jmp .end
174
175 .found:
176                 lodsb
177                 mov cx,ax
178                 add ax,si                       ; Make sure it fits
179                 cmp ax,adv0.tail
180                 jbe .ok
181 .end:
182                 xor cx,cx
183 .ok:
184                 pop ax
185                 ret
186
187 ;
188 ; adv_set: insert a string into the ADV in memory
189 ;
190 ; Input:        DL      = ADV ID
191 ;               FS:BX   = input buffer
192 ;               CX      = byte count (max = 255!)
193 ; Output:       CF=1 on error
194 ;               CX      clobbered
195 ;
196 ; Assumes CS == DS == ES.
197 ;
198 adv_set:
199                 push ax
200                 push si
201                 push di
202                 and ch,ch
203                 jnz .overflow
204
205                 push cx
206                 mov si,adv0.data
207                 xor ax,ax
208 .loop:
209                 lodsb
210                 cmp al,dl
211                 je .found
212                 and al,al
213                 jz .endz
214                 lodsb
215                 add si,ax
216                 cmp si,adv0.tail
217                 jb .loop
218                 jmp .end
219
220 .found:         ; Found, need to delete old copy
221                 lodsb
222                 lea di,[si-2]
223                 push di
224                 add si,ax
225                 mov cx,adv0.tail
226                 sub cx,si
227                 jb .nukeit
228                 rep movsb                       ; Remove the old one
229                 mov [di],ah                     ; Termination zero
230                 pop si
231                 jmp .loop
232 .nukeit:
233                 pop si
234                 jmp .end
235 .endz:
236                 dec si
237 .end:
238                 ; Now SI points to where we want to put our data
239                 pop cx
240                 mov di,si
241                 jcxz .empty
242                 add si,cx
243                 cmp si,adv0.tail-2
244                 jae .overflow                   ; CF=0
245
246                 mov si,bx
247                 mov al,dl
248                 stosb
249                 mov al,cl
250                 stosb
251                 fs rep movsb
252
253 .empty:
254                 mov cx,adv0.tail
255                 sub cx,di
256                 xor ax,ax
257                 rep stosb                       ; Zero-fill remainder
258
259                 clc
260 .done:
261                 pop di
262                 pop si
263                 pop ax
264                 ret
265 .overflow:
266                 stc
267                 jmp .done
268
269 ;
270 ; adv_cleanup:  checksum adv0 and copy to adv1
271 ;               Assumes CS == DS == ES.
272 ;
273 adv_cleanup:
274                 pushad
275                 mov si,adv0.data
276                 mov cx,ADV_LEN/4
277                 xor edx,edx
278 .loop:
279                 lodsd
280                 add edx,eax
281                 loop .loop
282                 mov eax,ADV_MAGIC2
283                 sub eax,edx
284                 lea di,[si+4]                   ; adv1
285                 mov si,adv0
286                 mov [si+4],eax                  ; Store checksum
287                 mov cx,(ADV_LEN+12)/4
288                 rep movsd
289                 popad
290                 ret
291
292 ;
293 ; adv_write:    write the ADV to disk.
294 ;
295 ;               Location is in memory variables.
296 ;               Assumes CS == DS == ES.
297 ;
298 ;               Returns CF=1 if the ADV cannot be written.
299 ;
300 adv_write:
301                 cmp dword [ADVSec0],0
302                 je .bad
303                 cmp dword [ADVSec1],0
304                 je .bad
305                 cmp byte [ADVDrive],-1
306                 je .bad
307
308                 push ax
309                 call adv_cleanup
310                 mov ah,3                        ; Write
311                 call adv_read_write
312                 pop ax
313
314                 clc
315                 ret
316 .bad:                                           ; No location for ADV set
317                 stc
318                 ret
319
320 ;
321 ; adv_read:     read the ADV from disk
322 ;
323 ;               Location is in memory variables.
324 ;               Assumes CS == DS == ES.
325 ;
326 adv_read:
327                 push ax
328                 mov ah,2                        ; Read
329                 call adv_read_write
330                 call adv_verify
331                 pop ax
332                 ret
333
334 ;
335 ; adv_read_write: disk I/O for the ADV
336 ;
337 ;               On input, AH=2 for read, AH=3 for write.
338 ;               Assumes CS == DS == ES.
339 ;
340 adv_read_write:
341                 mov [ADVOp],ah
342                 pushad
343
344                 mov dl,[ADVDrive]
345                 and dl,dl
346                 ; Floppies: can't trust INT 13h 08h, we better know
347                 ; the geometry a priori, which means it better be our
348                 ; boot device.  Handle that later.
349                 ; jns .floppy                   ; Floppy drive... urk
350
351                 mov ah,08h                      ; Get disk parameters
352                 int 13h
353                 jc .noparm
354                 and ah,ah
355                 jnz .noparm
356                 shr dx,8
357                 inc dx
358                 mov [ADVHeads],dx
359                 and cx,3fh
360                 mov [ADVSecPerTrack],cx
361
362 .noparm:
363                 ; Check for EDD
364                 mov bx,55AAh
365                 mov ah,41h                      ; EDD existence query
366                 mov dl,[ADVDrive]
367                 int 13h
368                 mov si,.cbios
369                 jc .noedd
370                 mov si,.ebios
371 .noedd:
372
373                 mov eax,[ADVSec0]
374                 mov bx,adv0
375                 call .doone
376
377                 mov eax,[ADVSec1]
378                 mov bx,adv1
379                 call .doone
380
381                 popad
382                 ret
383
384 .doone:
385                 xor edx,edx                     ; Zero-extend LBA
386                 push si
387                 jmp si
388
389 .ebios:
390                 mov cx,adv_retries
391 .eb_retry:
392                 ; Form DAPA on stack
393                 push edx
394                 push eax
395                 push es
396                 push bx
397                 push word 1                     ; Sector count
398                 push word 16                    ; DAPA size
399                 mov si,sp
400                 pushad
401                 mov dl,[ADVDrive]
402                 mov ax,4080h
403                 or ah,[ADVOp]
404                 push ds
405                 push ss
406                 pop ds
407                 int 13h
408                 pop ds
409                 popad
410                 lea sp,[si+16]                  ; Remove DAPA
411                 jc .eb_error
412                 pop si
413                 ret
414 .eb_error:
415                 loop .eb_retry
416                 stc
417                 pop si
418                 ret
419
420 .cbios:
421                 push edx
422                 push eax
423                 push bp
424
425                 movzx esi,word [ADVSecPerTrack]
426                 movzx edi,word [ADVHeads]
427                 ;
428                 ; Dividing by sectors to get (track,sector): we may have
429                 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
430                 ;
431                 div esi
432                 xor cx,cx
433                 xchg cx,dx              ; CX <- sector index (0-based)
434                                         ; EDX <- 0
435                 ; eax = track #
436                 div edi                 ; Convert track to head/cyl
437
438                 ; Watch out for overflow, we might be writing!
439                 cmp eax,1023
440                 ja .cb_overflow
441
442                 ;
443                 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
444                 ; BP = sectors to transfer, SI = bsSecPerTrack,
445                 ; ES:BX = data target
446                 ;
447
448                 shl ah,6                ; Because IBM was STOOPID
449                                         ; and thought 8 bits were enough
450                                         ; then thought 10 bits were enough...
451                 inc cx                  ; Sector numbers are 1-based, sigh
452                 or cl,ah
453                 mov ch,al
454                 mov dh,dl
455                 mov dl,[ADVDrive]
456                 xchg ax,bp              ; Sector to transfer count
457                 mov ah,[ADVOp]          ; Operation
458
459                 mov bp,adv_retries
460 .cb_retry:
461                 pushad
462                 int 13h
463                 popad
464                 jc .cb_error
465
466 .cb_done:
467                 pop bp
468                 pop eax
469                 pop edx
470                 ret
471
472 .cb_error:
473                 dec bp
474                 jnz .cb_retry
475 .cb_overflow:
476                 stc
477                 jmp .cb_done
478
479                 section .data
480                 align 4, db 0
481 ADVSec0         dd 0                    ; Not specified
482 ADVSec1         dd 0                    ; Not specified
483 ADVDrive        db -1                   ; No ADV defined
484
485                 section .bss
486                 alignb 4
487 ADVSecPerTrack  resw 1
488 ADVHeads        resw 1
489 ADVOp           resb 1