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