Add pxechain.com; new modules direction; reorganize installation
[syslinux.git] / modules / pxechain.asm
blobb11d0ea959a8013f172352f8c76dd17cb307418a
1 ; "$Id: pxechain.asm,v 1.2 2007/12/16 08:15:39 jhutz Exp $"
2 ; -*- fundamental -*- (asm-mode sucks) vim:noet:com=\:;
3 ; ****************************************************************************
5 ; pxechain.asm
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.
14 ; (*) This seems unnecessary at first, but it is very helpful when
15 ; selecting from among self-contained network boot images.
17 ; Copyright (c) 2007 Carnegie Mellon University
18 ; Copyright (C) 1994-2007 H. Peter Anvin
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.
26 ; ****************************************************************************
28 ;%define DEBUG
29 ;%define NO_RUN
31 absolute 0
32 pspInt20: resw 1
33 pspNextP: resw 1
34 resb 124
35 pspCmdLen: resb 1
36 pspCmdArg: resb 127
38 section .text
39 org 0x100
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
84 _start:
85 MARK 'INIT'
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.
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
103 .chomp: lodsb
104 cmp al,' '
105 loopz .chomp
106 inc cx
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'
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
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
152 mov cx,si
153 sub cx,bx
154 MARK 'LEN'
155 SHOWD cl ; assume it's <256 for debugging
156 dec si
158 .find_slash:
159 lodsb
160 cmp al,'/'
161 je .slash
162 loop .find_slash
163 .slash:
164 cmp cx,127
166 jna .copy_prefix
167 pop ds
168 jmp too_long
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
180 mov cl,[pspCmdLen]
181 mov si,pspCmdArg+1
182 jmp prefix_done
184 usage:
185 xor cx,cx
186 mov si,msg_usage
187 jmp fail
189 too_long:
190 xor cx,cx
191 mov si,msg_too_long
192 jmp fail
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
222 prefix_done:
223 SHOWD bl
224 MARK 'LEFT'
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
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
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
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
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
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
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
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
336 %endif
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
375 ; print_hex
377 ; Take a 16-bit integer in BX and print it as 2 hex digits.
378 ; Destroys AX and DL.
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
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
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
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
419 ; print_dec
421 ; Take an 8-bit integer in AL and print it in decimal.
422 ; Destroys AX and DL.
424 print_dec:
425 cmp al,10 ; < 10?
426 jb .lt10 ; If so, skip first 2 digits
428 cmp al,100 ; < 100
429 jb .lt100 ; If so, skip first digit
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
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
446 .lt10:
447 add al,'0'
448 mov dl,al
449 mov ah,0x02
450 int 0x21
454 ; print_dotquad
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
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:
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.
481 ; Return CF=1 on error.
483 ; No segment assumptions permitted.
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
514 jmp .done
515 .realerror:
517 .done:
518 dec si ; CF unchanged!
519 pop cx
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
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
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
553 PXECloseParms:
554 close_status: dw 0
556 PXEOpenParms:
557 open_status: dw 0
558 open_ciaddr: dd 0
560 section .bss
561 ipaddrbuf: resb 16