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