Add logo by Abi Rasheed
[profile/ivi/syslinux.git] / modules / pxechain.asm
1 ; "$Id: pxechain.asm,v 1.2 2007/12/16 08:15:39 jhutz Exp $"
2 ; -*- fundamental -*- (asm-mode sucks)  vim:noet:com=\:;
3 ; ****************************************************************************
4 ;
5 ;  pxechain.asm
6 ;
7 ;  A comboot program to chain from PXELINUX to another PXE network
8 ;  bootstrap program (NBP).  This improves on PXELINUX's built-in PXE
9 ;  chaining support by arranging for the server address and boot filename
10 ;  reported by the PXE stack to be those from which the new NBP was
11 ;  loaded, allowing PXELINUX to be used to select from multiple NBP's,
12 ;  such as gPXE, another PXELINUX(*), Windows RIS, and so on.
13 ;
14 ;  (*) This seems unnecessary at first, but it is very helpful when
15 ;  selecting from among self-contained network boot images.
16 ;
17 ;   Copyright (c) 2007 Carnegie Mellon University
18 ;   Copyright (C) 1994-2007  H. Peter Anvin
19 ;
20 ;  This program is free software; you can redistribute it and/or modify
21 ;  it under the terms of the GNU General Public License as published by
22 ;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
23 ;  Boston MA 02111-1307, USA; either version 2 of the License, or
24 ;  (at your option) any later version; incorporated herein by reference.
25 ;
26 ; ****************************************************************************
27
28 ;%define DEBUG
29 ;%define NO_RUN
30
31                 absolute 0
32 pspInt20:       resw 1
33 pspNextP:       resw 1
34                 resb 124
35 pspCmdLen:      resb 1
36 pspCmdArg:      resb 127
37
38                 section .text
39                 org     0x100
40
41 %ifdef DEBUG
42 %macro MARK 1.nolist
43                 mov ah,0x02
44                 mov dl,%1&0xff
45                 int 0x21
46 %if (%1 >> 8) & 0xff
47                 mov dl,(%1 >> 8) & 0xff
48                 int 0x21
49 %if (%1 >> 16) & 0xff
50                 mov dl,(%1 >> 16) & 0xff
51                 int 0x21
52 %if (%1 >> 24) & 0xff
53                 mov dl,(%1 >> 24) & 0xff
54                 int 0x21
55 %endif
56 %endif
57 %endif
58                 mov dl,' '
59                 int 0x21
60 %endmacro
61 %macro SHOWD 1.nolist
62                 mov al,%1
63                 call print_dec
64                 mov ah,0x02
65                 mov dl,' '
66                 int 0x21
67 %endmacro
68 %macro SHOWX 1.nolist
69                 mov bx,%1
70                 call print_hex
71                 mov ah,0x02
72                 mov dl,' '
73                 int 0x21
74 %endmacro
75 %else
76 %macro MARK 1.nolist
77 %endmacro
78 %macro SHOWD 1.nolist
79 %endmacro
80 %macro SHOWX 1.nolist
81 %endmacro
82 %endif
83
84 _start:
85                 MARK 'INIT'
86
87 ; There should be exactly one command-line argument, which is of the form
88 ; [[ipaddress]::]tftp_filename, just like filenames given to PXELINUX.
89 ; Too few or too many arguments is an error.
90 ;
91 ; This code is based on mangle_name in pxelinux.asm
92 parse_args:
93                 cld
94                 xor cx,cx
95                 mov cl,[pspCmdLen]
96                 dec cx
97                 mov si,pspCmdArg+1
98                 and cx,cx
99                 je near usage           ; no args is bad
100                 add si,cx
101                 dec si
102                 std
103 .chomp:         lodsb
104                 cmp al,' '
105                 loopz .chomp
106                 inc cx
107                 cld
108                 mov [pspCmdLen],cl
109                 mov si,pspCmdArg+1
110                 cmp word [si],'::'      ; Leading ::?
111                 je near gotprefix
112                 dec cx
113                 jz noip
114                 MARK 'SCAN'
115
116 .more:
117                 inc si
118                 cmp byte [si],' '
119                 je near usage
120                 cmp word [si],'::'
121                 je near parse_ip
122                 loop .more
123
124 noip:
125                 MARK 'NOIP'
126                 mov ax,0x0e             ; get config file name
127                 int 0x22
128                 mov si,bx
129 %ifdef DEBUG
130                 mov ah,0x02
131                 mov dl,'['
132                 int 0x21
133                 mov ax,0x02
134                 int 0x22
135                 mov ah,0x02
136                 mov dl,']'
137                 int 0x21
138                 mov dl,' '
139                 int 0x21
140 %endif
141                 push ds
142                 push es
143                 pop ds
144                 pop es
145                 push es
146 .find_prefix:
147                 lodsb
148                 and al,al
149                 jnz .find_prefix
150                 dec si
151
152                 mov cx,si
153                 sub cx,bx
154                 MARK 'LEN'
155                 SHOWD cl                ; assume it's <256 for debugging
156                 dec si
157                 std
158 .find_slash:
159                 lodsb
160                 cmp al,'/'
161                 je .slash
162                 loop .find_slash
163 .slash:
164                 cmp cx,127
165                 cld
166                 jna .copy_prefix
167                 pop ds
168                 jmp too_long
169
170 .copy_prefix:
171                 SHOWD cl
172                 MARK 'PFX'
173                 mov si,bx
174                 mov di,tftp_filename
175                 mov bx,128
176                 sub bx,cx
177                 rep movsb
178                 pop ds
179
180                 mov cl,[pspCmdLen]
181                 mov si,pspCmdArg+1
182                 jmp prefix_done
183
184 usage:
185                 xor cx,cx
186                 mov si,msg_usage
187                 jmp fail
188
189 too_long:
190                 xor cx,cx
191                 mov si,msg_too_long
192                 jmp fail
193
194 parse_ip:
195                 MARK 'PIP'
196                 mov di,si
197                 mov si,pspCmdArg+1
198                 call parse_dotquad
199                 jc .notdq
200                 cmp si,di               ; is it the same place?
201                 jne .notdq
202                 mov [tftp_siaddr],eax
203                 jmp gotprefix
204 .notdq:
205                 MARK 'NDQ'
206                 mov si,di
207                 mov bx,pspCmdArg+1
208                 mov ax,0x0010           ; DNS resolve
209                 int 0x22
210                 and eax,eax
211                 jz noip
212                 mov [tftp_siaddr],eax
213 gotprefix:
214                 MARK 'GOTP'
215                 dec cx                  ; skip the ::
216                 dec cx
217                 inc si
218                 inc si
219                 mov di,tftp_filename
220                 mov bx,128
221
222 prefix_done:
223                 SHOWD bl
224                 MARK 'LEFT'
225
226 ; SI points at the filename, plus remaining arguments,
227 ; CX contains their combined length.
228 ; DI points to where the filename should be stored
229 ; BX says how much space is left for the filename and NUL
230
231                 and cx,cx
232                 jz usage                ; no args is bad
233 .copy_filename:
234                 lodsb
235 %ifdef DEBUG
236                 mov dl,al
237                 mov ah,0x2
238                 int 0x21
239 %endif
240                 cmp al,' '
241                 je usage
242                 dec bx
243                 jz too_long
244                 stosb
245                 loop .copy_filename
246                 xor eax,eax
247                 stosb
248
249 ; get PXE cached data
250                 MARK 'GCI'
251                 mov ax,0x0009           ; call PXE stack
252                 mov bx,0x0071           ; PXENV_GET_CACHED_INFO
253                 mov di,PXECacheParms
254                 int 0x22
255                 and eax,eax
256                 jz .fix_siaddr
257                 mov cx,[gci_status]
258                 mov si,msg_get_cache
259                 jmp fail
260
261 .fix_siaddr:
262                 mov bx,[gci_bufferseg]
263                 mov es,bx
264                 mov bx,[gci_buffer]
265                 mov eax,[es:bx+12]      ; save our address (ciaddr)
266                 mov [open_ciaddr],eax   ; ... in case we have to do UDP open
267                 mov eax,[tftp_siaddr]
268                 and eax,eax
269                 jnz .replace_addr
270                 MARK 'ADDR'
271                 mov eax,[es:bx+20]      ; siaddr
272                 mov [tftp_siaddr],eax
273                 jmp .addr_done
274 .replace_addr:
275                 mov [es:bx+20],eax
276 .addr_done:
277                 mov si,tftp_filename    ; copy the new filename...
278                 lea di,[es:bx+108]      ; to the "cached DHCP response"
279                 mov cx,128
280                 rep movsb
281                 mov bx,ds               ; restore es before proceeding
282                 mov es,bx
283
284 ; print out what we are doing
285 %ifdef DEBUG
286                 mov ah,0x02             ; write character
287                 mov dl,0x0d             ; print a CRLF first
288                 int 0x21
289                 mov dl,0x0a
290                 int 0x21
291 %endif
292                 mov ax,0x0002           ; write string
293                 mov bx,msg_booting
294                 int 0x22
295                 mov ebx,[tftp_siaddr]
296                 call print_dotquad
297                 mov ah,0x02             ; write character
298                 mov dl,' '
299                 int 0x21
300                 mov ax,0x0002           ; write string
301                 mov bx,tftp_filename
302                 int 0x22
303                 mov ah,0x02             ; write character
304                 mov dl,0x0d
305                 int 0x21
306                 mov dl,0x0a
307                 int 0x21
308
309 %ifndef NO_RUN
310                 mov ax,0x0009           ; call PXE stack
311                 mov bx,0x0031           ; PXENV_UDP_CLOSE
312                 mov di,PXECloseParms
313                 int 0x22
314                 mov cx,[close_status]
315                 mov si,msg_udp_close
316                 and ax,ax
317                 jnz fail
318
319                 mov ax,0x0009           ; call PXE stack
320                 mov bx,0x0073           ; PXENV_RESTART_TFTP
321                 mov di,PXERestartTFTPParms
322                 int 0x22
323                 mov cx,[tftp_status]
324                 mov si,msg_rst_tftp
325                 call fail
326
327                 mov ax,0x0009           ; call PXE stack
328                 mov bx,0x0030           ; PXENV_UDP_OPEN
329                 mov di,PXEOpenParms
330                 int 0x22
331                 mov cx,[open_status]
332                 mov si,msg_udp_open
333                 and ax,ax
334                 jnz fail
335                 ret
336 %endif
337
338 fail:
339                 MARK 'FAIL'
340                 SHOWX cs
341                 SHOWX ds
342                 SHOWX es
343                 SHOWX si
344 %ifdef DEBUG
345                 mov ah,0x02             ; write character
346                 mov dl,0x0d             ; print a CRLF first
347                 int 0x21
348                 mov dl,0x0a
349                 int 0x21
350 %endif
351                 mov ax,0x0002           ; write string
352                 mov bx,msg_progname     ; print our name
353                 int 0x22
354                 mov bx,si               ; ... the error message
355                 int 0x22
356                 mov ah,0x02             ; write character
357                 jcxz .done
358                 mov dl,' '              ; ... and the error code, in []
359                 int 0x21
360                 mov dl,'['
361                 int 0x21
362                 mov bx,cx
363                 call print_hex
364                 mov ah,0x02             ; write character
365                 mov dl,']'
366                 int 0x21
367 .done:
368                 mov dl,0x0d             ; and finally a CRLF
369                 int 0x21
370                 mov dl,0x0a
371                 int 0x21
372                 ret
373
374
375 ; print_hex
376 ;
377 ; Take a 16-bit integer in BX and print it as 2 hex digits.
378 ; Destroys AX and DL.
379 ;
380 print_hex:
381                 mov al,bh
382                 aam 16
383                 cmp ah,10
384                 jb .lt_a000
385                 add ah,'A'-'0'-10
386 .lt_a000:       add ah,'0'
387                 mov dl,ah
388                 mov ah,0x02             ; write character
389                 int 0x21
390
391                 cmp al,10
392                 jb .lt_a00
393                 add al,'A'-'0'-10
394 .lt_a00:        add al,'0'
395                 mov dl,al
396                 mov ah,0x02             ; write character
397                 int 0x21
398
399                 mov al,bl
400                 aam 16
401                 cmp ah,10
402                 jb .lt_a0
403                 add ah,'A'-'0'-10
404 .lt_a0:         add ah,'0'
405                 mov dl,ah
406                 mov ah,0x02             ; write character
407                 int 0x21
408
409                 cmp al,10
410                 jb .lt_a
411                 add al,'A'-'0'-10
412 .lt_a:          add al,'0'
413                 mov dl,al
414                 mov ah,0x02             ; write character
415                 int 0x21
416                 ret
417
418
419 ; print_dec
420 ;
421 ; Take an 8-bit integer in AL and print it in decimal.
422 ; Destroys AX and DL.
423 ;
424 print_dec:
425                 cmp al,10               ; < 10?
426                 jb .lt10                ; If so, skip first 2 digits
427
428                 cmp al,100              ; < 100
429                 jb .lt100               ; If so, skip first digit
430
431                 aam 100
432                 ; Now AH = 100-digit; AL = remainder
433                 add ah,'0'
434                 mov dl,ah
435                 mov ah,0x02
436                 int 0x21
437
438 .lt100:
439                 aam 10
440                 ; Now AH = 10-digit; AL = remainder
441                 add ah,'0'
442                 mov dl,ah
443                 mov ah,0x02
444                 int 0x21
445
446 .lt10:
447                 add al,'0'
448                 mov dl,al
449                 mov ah,0x02
450                 int 0x21
451                 ret
452
453
454 ; print_dotquad
455 ;
456 ; Take an IP address (in network byte order) in EBX and print it
457 ; as a dotted quad.
458 ; Destroys EAX, EBX, ECX, EDX
459 ;
460 print_dotquad:
461                 mov cx,3
462 .octet:
463                 mov al,bl
464                 call print_dec
465                 jcxz .done
466                 mov ah,0x02
467                 mov dl,'.'
468                 int 0x21
469                 ror ebx,8       ; Move next char into LSB
470                 dec cx
471                 jmp .octet
472 .done:
473                 ret
474
475
476 ; parse_dotquad:
477 ;               Read a dot-quad pathname in DS:SI and output an IP
478 ;               address in EAX, with SI pointing to the first
479 ;               nonmatching character.
480 ;
481 ;               Return CF=1 on error.
482 ;
483 ;               No segment assumptions permitted.
484 ;
485 parse_dotquad:
486                 push cx
487                 mov cx,4
488                 xor eax,eax
489 .parseloop:
490                 mov ch,ah
491                 mov ah,al
492                 lodsb
493                 sub al,'0'
494                 jb .notnumeric
495                 cmp al,9
496                 ja .notnumeric
497                 aad                             ; AL += 10 * AH; AH = 0;
498                 xchg ah,ch
499                 jmp .parseloop
500 .notnumeric:
501                 cmp al,'.'-'0'
502                 pushf
503                 mov al,ah
504                 mov ah,ch
505                 xor ch,ch
506                 ror eax,8
507                 popf
508                 jne .error
509                 loop .parseloop
510                 jmp .done
511 .error:
512                 loop .realerror                 ; If CX := 1 then we're done
513                 clc
514                 jmp .done
515 .realerror:
516                 stc
517 .done:
518                 dec si                          ; CF unchanged!
519                 pop cx
520                 ret
521
522                 section .data
523 msg_booting:    db 'TFTP boot: ',0
524 msg_progname:   db 'pxechain: ',0
525 msg_usage:      db 'usage: pxechain.cbt [[ipaddress]::]filename',0dh,0ah,0
526 msg_too_long:   db 'pxechain: filename is too long (max 127)',0dh,0ah,0
527 msg_get_cache:  db 'PXENV_GET_CACHED_INFO',0
528 msg_rst_tftp:   db 'PXENV_RESTART_TFTP',0
529 msg_udp_close:  db 'PXENV_UDP_CLOSE',0
530 msg_udp_open:   db 'PXENV_UDP_OPEN',0
531
532 PXECacheParms:
533 gci_status:     dw 0
534 gci_packettype: dw 3                    ; PXENV_PACKET_TYPE_CACHED_REPLY
535 gci_buffersize: dw 0
536 gci_buffer:     dw 0
537 gci_bufferseg:  dw 0
538 gci_bufferlim:  dw 0
539
540 PXERestartTFTPParms:
541 tftp_status:    dw 0
542 tftp_filename:  times 128 db 0
543 tftp_bufsize:   dd 0x00090000           ; available memory for NBP
544 tftp_bufaddr:   dd 0x00007c00           ; PXE NBP load address
545 tftp_siaddr:    dd 0
546 tftp_giaddr:    dd 0
547 tftp_mcaddr:    dd 0
548 tftp_mcport:    dw 0
549 tftp_msport:    dw 0
550 tftp_timeout:   dw 0
551 tftp_reopendly: dw 0
552
553 PXECloseParms:
554 close_status:   dw 0
555
556 PXEOpenParms:
557 open_status:    dw 0
558 open_ciaddr:    dd 0