Adding upstream version 3.61.
[syslinux-debian/hramrach.git] / pxelinux.asm
blob02f38c02f307380ae62739fa4f246f8b52714491
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
4 ; pxelinux.asm
6 ; A program to boot Linux kernels off a TFTP server using the Intel PXE
7 ; network booting API. It is based on the SYSLINUX boot loader for
8 ; MS-DOS floppies.
10 ; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
12 ; This program is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
15 ; Boston MA 02111-1307, USA; either version 2 of the License, or
16 ; (at your option) any later version; incorporated herein by reference.
18 ; ****************************************************************************
20 %define IS_PXELINUX 1
21 %include "head.inc"
22 %include "pxe.inc"
25 ; Some semi-configurable constants... change on your own risk.
27 my_id equ pxelinux_id
28 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
29 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
30 NULLFILE equ 0 ; Zero byte == null file name
31 NULLOFFSET equ 4 ; Position in which to look
32 REBOOT_TIME equ 5*60 ; If failure, time until full reset
33 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
34 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
35 MAX_OPEN equ (1 << MAX_OPEN_LG2)
36 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
37 TFTP_PORT equ htons(69) ; Default TFTP port
38 PKT_RETRY equ 6 ; Packet transmit retry count
39 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
40 ; Desired TFTP block size
41 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
42 ; be a fair number of networks with "substandard" MTUs which break.
43 ; The code assumes TFTP_LARGEBLK <= 2K.
44 TFTP_MTU equ 1440
45 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
46 ; Standard TFTP block size
47 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
48 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
49 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
51 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
52 SECTOR_SIZE equ TFTP_BLOCKSIZE
55 ; This is what we need to do when idle
56 ; *** This is disabled because some PXE stacks wait for unacceptably
57 ; *** long if there are no packets receivable.
59 %define HAVE_IDLE 0 ; idle is not a noop
61 %if HAVE_IDLE
62 %macro RESET_IDLE 0
63 call reset_idle
64 %endmacro
65 %macro DO_IDLE 0
66 call check_for_arp
67 %endmacro
68 %else
69 %macro RESET_IDLE 0
70 ; Nothing
71 %endmacro
72 %macro DO_IDLE 0
73 ; Nothing
74 %endmacro
75 %endif
78 ; TFTP operation codes
80 TFTP_RRQ equ htons(1) ; Read request
81 TFTP_WRQ equ htons(2) ; Write request
82 TFTP_DATA equ htons(3) ; Data packet
83 TFTP_ACK equ htons(4) ; ACK packet
84 TFTP_ERROR equ htons(5) ; ERROR packet
85 TFTP_OACK equ htons(6) ; OACK packet
88 ; TFTP error codes
90 TFTP_EUNDEF equ htons(0) ; Unspecified error
91 TFTP_ENOTFOUND equ htons(1) ; File not found
92 TFTP_EACCESS equ htons(2) ; Access violation
93 TFTP_ENOSPACE equ htons(3) ; Disk full
94 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
95 TFTP_EBADID equ htons(5) ; Unknown transfer
96 TFTP_EEXISTS equ htons(6) ; File exists
97 TFTP_ENOUSER equ htons(7) ; No such user
98 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
101 ; The following structure is used for "virtual kernels"; i.e. LILO-style
102 ; option labels. The options we permit here are `kernel' and `append
103 ; Since there is no room in the bottom 64K for all of these, we
104 ; stick them at vk_seg:0000 and copy them down before we need them.
106 struc vkernel
107 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
108 vk_rname: resb FILENAME_MAX ; Real name
109 vk_ipappend: resb 1 ; "IPAPPEND" flag
110 vk_type: resb 1 ; Type of file
111 vk_appendlen: resw 1
112 alignb 4
113 vk_append: resb max_cmd_len+1 ; Command line
114 alignb 4
115 vk_end: equ $ ; Should be <= vk_size
116 endstruc
119 ; Segment assignments in the bottom 640K
120 ; 0000h - main code/data segment (and BIOS segment)
122 real_mode_seg equ 4000h
123 pktbuf_seg equ 3000h ; Packet buffers segments
124 vk_seg equ 2000h ; Virtual kernels
125 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
126 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
129 ; BOOTP/DHCP packet pattern
131 struc bootp_t
132 bootp:
133 .opcode resb 1 ; BOOTP/DHCP "opcode"
134 .hardware resb 1 ; ARP hardware type
135 .hardlen resb 1 ; Hardware address length
136 .gatehops resb 1 ; Used by forwarders
137 .ident resd 1 ; Transaction ID
138 .seconds resw 1 ; Seconds elapsed
139 .flags resw 1 ; Broadcast flags
140 .cip resd 1 ; Client IP
141 .yip resd 1 ; "Your" IP
142 .sip resd 1 ; Next server IP
143 .gip resd 1 ; Relay agent IP
144 .macaddr resb 16 ; Client MAC address
145 .sname resb 64 ; Server name (optional)
146 .bootfile resb 128 ; Boot file name
147 .option_magic resd 1 ; Vendor option magic cookie
148 .options resb 1260 ; Vendor options
149 endstruc
151 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
154 ; TFTP connection data structure. Each one of these corresponds to a local
155 ; UDP port. The size of this structure must be a power of 2.
156 ; HBO = host byte order; NBO = network byte order
157 ; (*) = written by options negotiation code, must be dword sized
159 struc open_file_t
160 tftp_localport resw 1 ; Local port number (0 = not in use)
161 tftp_remoteport resw 1 ; Remote port number
162 tftp_remoteip resd 1 ; Remote IP address
163 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
164 tftp_filesize resd 1 ; Total file size(*)
165 tftp_blksize resd 1 ; Block size for this connection(*)
166 tftp_bytesleft resw 1 ; Unclaimed data bytes
167 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
168 tftp_dataptr resw 1 ; Pointer to available data
169 resw 2 ; Currently unusued
170 ; At end since it should not be zeroed on socked close
171 tftp_pktbuf resw 1 ; Packet buffer offset
172 endstruc
173 %ifndef DEPEND
174 %if (open_file_t_size & (open_file_t_size-1))
175 %error "open_file_t is not a power of 2"
176 %endif
177 %endif
179 ; ---------------------------------------------------------------------------
180 ; BEGIN CODE
181 ; ---------------------------------------------------------------------------
184 ; Memory below this point is reserved for the BIOS and the MBR
186 section .earlybss
187 trackbufsize equ 8192
188 trackbuf resb trackbufsize ; Track buffer goes here
189 getcbuf resb trackbufsize
190 ; ends at 4800h
192 ; Put some large buffers here, before RBFG_brainfuck,
193 ; where we can still carefully control the address
194 ; assignments...
196 alignb open_file_t_size
197 Files resb MAX_OPEN*open_file_t_size
199 alignb FILENAME_MAX
200 BootFile resb 256 ; Boot file from DHCP packet
201 PathPrefix resb 256 ; Path prefix derived from boot file
202 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
203 IPOption resb 80 ; ip= option buffer
204 InitStack resd 1 ; Pointer to reset stack (SS:SP)
205 PXEStack resd 1 ; Saved stack during PXE call
207 ; Warning here: RBFG build 22 randomly overwrites memory location
208 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
209 ; screws up the pointer to its own internal packet buffer and starts
210 ; writing a received ARP packet into low memory.
211 RBFG_brainfuck resb 0E00h
213 section .bss
214 alignb 4
215 RebootTime resd 1 ; Reboot timeout, if set by option
216 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
217 APIVer resw 1 ; PXE API version found
218 IPOptionLen resw 1 ; Length of IPOption
219 IdleTimer resw 1 ; Time to check for ARP?
220 LocalBootType resw 1 ; Local boot return code
221 PktTimeout resw 1 ; Timeout for current packet
222 RealBaseMem resw 1 ; Amount of DOS memory after freeing
223 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
224 DHCPMagic resb 1 ; PXELINUX magic flags
226 ; The relative position of these fields matter!
227 MAC_MAX equ 32 ; Handle hardware addresses this long
228 MACLen resb 1 ; MAC address len
229 MACType resb 1 ; MAC address type
230 MAC resb MAC_MAX+1 ; Actual MAC address
231 BOOTIFStr resb 7 ; Space for "BOOTIF="
232 MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
234 ; The relative position of these fields matter!
235 UUIDType resb 1 ; Type byte from DHCP option
236 UUID resb 16 ; UUID, from the PXE stack
237 UUIDNull resb 1 ; dhcp_copyoption zero-terminates
240 ; PXE packets which don't need static initialization
242 alignb 4
243 pxe_unload_stack_pkt:
244 .status: resw 1 ; Status
245 .reserved: resw 10 ; Reserved
246 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
248 alignb 16
249 ; BOOTP/DHCP packet buffer
251 alignb 16
252 packet_buf resb 2048 ; Transfer packet
253 packet_buf_size equ $-packet_buf
256 ; Constants for the xfer_buf_seg
258 ; The xfer_buf_seg is also used to store message file buffers. We
259 ; need two trackbuffers (text and graphics), plus a work buffer
260 ; for the graphics decompressor.
262 xbs_textbuf equ 0 ; Also hard-coded, do not change
263 xbs_vgabuf equ trackbufsize
264 xbs_vgatmpbuf equ 2*trackbufsize
266 section .text
268 ; PXELINUX needs more BSS than the other derivatives;
269 ; therefore we relocate it from 7C00h on startup.
271 StackBuf equ $ ; Base of stack if we use our own
274 ; Primary entry point.
276 bootsec equ $
277 _start:
278 pushfd ; Paranoia... in case of return to PXE
279 pushad ; ... save as much state as possible
280 push ds
281 push es
282 push fs
283 push gs
285 xor ax,ax
286 mov ds,ax
287 mov es,ax
289 %ifndef DEPEND
290 %if TEXT_START != 0x7c00
291 ; This is uglier than it should be, but works around
292 ; some NASM 0.98.38 bugs.
293 mov di,section..bcopy32.start
294 add di,__bcopy_size-4
295 lea si,[di-(TEXT_START-7C00h)]
296 lea cx,[di-(TEXT_START-4)]
297 shr cx,2
298 std ; Overlapping areas, copy backwards
299 rep movsd
300 %endif
301 %endif
302 jmp 0:_start1 ; Canonicalize address
303 _start1:
304 mov bp,sp
305 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
307 ; That is all pushed onto the PXE stack. Save the pointer
308 ; to it and switch to an internal stack.
309 mov [InitStack],sp
310 mov [InitStack+2],ss
312 %if USE_PXE_PROVIDED_STACK
313 ; Apparently some platforms go bonkers if we
314 ; set up our own stack...
315 mov [BaseStack],sp
316 mov [BaseStack+4],ss
317 %endif
319 cli ; Paranoia
320 lss esp,[BaseStack]
322 sti ; Stack set up and ready
323 cld ; Copy upwards
326 ; Initialize screen (if we're using one)
328 push es ; Save ES -> PXE entry structure
329 push ds
330 pop es ; ES <- DS
331 %include "init.inc"
332 pop es ; Restore ES -> PXE entry structure
334 ; Tell the user we got this far
336 mov si,syslinux_banner
337 call writestr
339 mov si,copyright_str
340 call writestr
343 ; Assume API version 2.1, in case we find the !PXE structure without
344 ; finding the PXENV+ structure. This should really look at the Base
345 ; Code ROM ID structure in have_pxe, but this is adequate for now --
346 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
347 ; about higher versions than that.
349 mov word [APIVer],0201h
352 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
353 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
354 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
355 ; We should make that the second test, and not trash ES:BX...
357 cmp dword [es:bx], '!PXE'
358 je have_pxe
360 ; Uh-oh, not there... try plan B
361 mov ax, 5650h
362 %if USE_PXE_PROVIDED_STACK == 0
363 lss sp,[InitStack]
364 %endif
365 int 1Ah ; May trash regs
366 %if USE_PXE_PROVIDED_STACK == 0
367 lss esp,[BaseStack]
368 %endif
370 jc no_pxe
371 cmp ax,564Eh
372 jne no_pxe
374 ; Okay, that gave us the PXENV+ structure, find !PXE
375 ; structure from that (if available)
376 cmp dword [es:bx], 'PXEN'
377 jne no_pxe
378 cmp word [es:bx+4], 'V+'
379 je have_pxenv
381 ; Nothing there either. Last-ditch: scan memory
382 call memory_scan_for_pxe_struct ; !PXE scan
383 jnc have_pxe
384 call memory_scan_for_pxenv_struct ; PXENV+ scan
385 jnc have_pxenv
387 no_pxe: mov si,err_nopxe
388 call writestr
389 jmp kaboom
391 have_pxenv:
392 mov [StrucPtr],bx
393 mov [StrucPtr+2],es
395 mov si,found_pxenv
396 call writestr
398 mov si,apiver_str
399 call writestr
400 mov ax,[es:bx+6]
401 mov [APIVer],ax
402 call writehex4
403 call crlf
405 cmp ax,0201h ; API version 2.1 or higher
406 jb old_api
407 mov si,bx
408 mov ax,es
409 les bx,[es:bx+28h] ; !PXE structure pointer
410 cmp dword [es:bx],'!PXE'
411 je have_pxe
413 ; Nope, !PXE structure missing despite API 2.1+, or at least
414 ; the pointer is missing. Do a last-ditch attempt to find it.
415 call memory_scan_for_pxe_struct
416 jnc have_pxe
418 ; Otherwise, no dice, use PXENV+ structure
419 mov bx,si
420 mov es,ax
422 old_api: ; Need to use a PXENV+ structure
423 mov si,using_pxenv_msg
424 call writestr
426 mov eax,[es:bx+0Ah] ; PXE RM API
427 mov [PXENVEntry],eax
429 mov si,undi_data_msg
430 call writestr
431 mov ax,[es:bx+20h]
432 call writehex4
433 call crlf
434 mov si,undi_data_len_msg
435 call writestr
436 mov ax,[es:bx+22h]
437 call writehex4
438 call crlf
439 mov si,undi_code_msg
440 call writestr
441 mov ax,[es:bx+24h]
442 call writehex4
443 call crlf
444 mov si,undi_code_len_msg
445 call writestr
446 mov ax,[es:bx+26h]
447 call writehex4
448 call crlf
450 ; Compute base memory size from PXENV+ structure
451 xor esi,esi
452 movzx eax,word [es:bx+20h] ; UNDI data seg
453 cmp ax,[es:bx+24h] ; UNDI code seg
454 ja .use_data
455 mov ax,[es:bx+24h]
456 mov si,[es:bx+26h]
457 jmp short .combine
458 .use_data:
459 mov si,[es:bx+22h]
460 .combine:
461 shl eax,4
462 add eax,esi
463 shr eax,10 ; Convert to kilobytes
464 mov [RealBaseMem],ax
466 mov si,pxenventry_msg
467 call writestr
468 mov ax,[PXENVEntry+2]
469 call writehex4
470 mov al,':'
471 call writechr
472 mov ax,[PXENVEntry]
473 call writehex4
474 call crlf
475 jmp have_entrypoint
477 have_pxe:
478 mov [StrucPtr],bx
479 mov [StrucPtr+2],es
481 mov eax,[es:bx+10h]
482 mov [PXEEntry],eax
484 mov si,undi_data_msg
485 call writestr
486 mov eax,[es:bx+2Ah]
487 call writehex8
488 call crlf
489 mov si,undi_data_len_msg
490 call writestr
491 mov ax,[es:bx+2Eh]
492 call writehex4
493 call crlf
494 mov si,undi_code_msg
495 call writestr
496 mov ax,[es:bx+32h]
497 call writehex8
498 call crlf
499 mov si,undi_code_len_msg
500 call writestr
501 mov ax,[es:bx+36h]
502 call writehex4
503 call crlf
505 ; Compute base memory size from !PXE structure
506 xor esi,esi
507 mov eax,[es:bx+2Ah]
508 cmp eax,[es:bx+32h]
509 ja .use_data
510 mov eax,[es:bx+32h]
511 mov si,[es:bx+36h]
512 jmp short .combine
513 .use_data:
514 mov si,[es:bx+2Eh]
515 .combine:
516 add eax,esi
517 shr eax,10
518 mov [RealBaseMem],ax
520 mov si,pxeentry_msg
521 call writestr
522 mov ax,[PXEEntry+2]
523 call writehex4
524 mov al,':'
525 call writechr
526 mov ax,[PXEEntry]
527 call writehex4
528 call crlf
530 have_entrypoint:
531 push cs
532 pop es ; Restore CS == DS == ES
535 ; Network-specific initialization
537 xor ax,ax
538 mov [LocalDomain],al ; No LocalDomain received
541 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
542 ; packet (query info 1).
544 query_bootp_1:
545 mov dl,1
546 call pxe_get_cached_info
547 call parse_dhcp
549 ; We don't use flags from the request packet, so
550 ; this is a good time to initialize DHCPMagic...
551 ; Initialize it to 1 meaning we will accept options found;
552 ; in earlier versions of PXELINUX bit 0 was used to indicate
553 ; we have found option 208 with the appropriate magic number;
554 ; we no longer require that, but MAY want to re-introduce
555 ; it in the future for vendor encapsulated options.
556 mov byte [DHCPMagic],1
559 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
560 ; address). This lives in the DHCPACK packet (query info 2).
562 query_bootp_2:
563 mov dl,2
564 call pxe_get_cached_info
565 call parse_dhcp ; Parse DHCP packet
567 ; Save away MAC address (assume this is in query info 2. If this
568 ; turns out to be problematic it might be better getting it from
569 ; the query info 1 packet.)
571 .save_mac:
572 movzx cx,byte [trackbuf+bootp.hardlen]
573 cmp cx,16
574 jna .mac_ok
575 xor cx,cx ; Bad hardware address length
576 .mac_ok:
577 mov [MACLen],cl
578 mov al,[trackbuf+bootp.hardware]
579 mov [MACType],al
580 mov si,trackbuf+bootp.macaddr
581 mov di,MAC
582 rep movsb
584 ; Enable this if we really need to zero-pad this field...
585 ; mov cx,MAC+MAC_MAX+1
586 ; sub cx,di
587 ; xor ax,ax
588 ; rep stosb
591 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
592 ; packet (query info 3).
594 mov dl,3
595 call pxe_get_cached_info
596 call parse_dhcp ; Parse DHCP packet
599 ; Generate the bootif string, and the hardware-based config string.
601 make_bootif_string:
602 mov si,bootif_str
603 mov di,BOOTIFStr
604 mov cx,bootif_str_len
605 rep movsb
607 movzx cx,byte [MACLen]
608 mov si,MACType
609 inc cx
610 .hexify_mac:
611 push cx
612 mov cl,1 ; CH == 0 already
613 call lchexbytes
614 mov al,'-'
615 stosb
616 pop cx
617 loop .hexify_mac
618 mov [di-1],cl ; Null-terminate and strip final dash
620 ; Generate ip= option
622 call genipopt
625 ; Print IP address
627 mov eax,[MyIP]
628 mov di,DotQuadBuf
629 push di
630 call gendotquad ; This takes network byte order input
632 xchg ah,al ; Convert to host byte order
633 ror eax,16 ; (BSWAP doesn't work on 386)
634 xchg ah,al
636 mov si,myipaddr_msg
637 call writestr
638 call writehex8
639 mov al,' '
640 call writechr
641 pop si ; DotQuadBuf
642 call writestr
643 call crlf
645 mov si,IPOption
646 call writestr
647 call crlf
650 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
651 ; if we didn't get the magic enable, do not recognize any other options.
653 check_dhcp_magic:
654 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
655 jnz .got_magic
656 mov byte [DHCPMagic], 0 ; If not, kill all other options
657 .got_magic:
661 ; Initialize UDP stack
663 udp_init:
664 mov eax,[MyIP]
665 mov [pxe_udp_open_pkt.sip],eax
666 mov di,pxe_udp_open_pkt
667 mov bx,PXENV_UDP_OPEN
668 call pxenv
669 jc .failed
670 cmp word [pxe_udp_open_pkt.status], byte 0
671 je .success
672 .failed: mov si,err_udpinit
673 call writestr
674 jmp kaboom
675 .success:
678 ; Common initialization code
680 %include "cpuinit.inc"
683 ; Now we're all set to start with our *real* business. First load the
684 ; configuration file (if any) and parse it.
686 ; In previous versions I avoided using 32-bit registers because of a
687 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
688 ; random. I figure, though, that if there are any of those still left
689 ; they probably won't be trying to install Linux on them...
691 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
692 ; to take'm out. In fact, we may want to put them back if we're going
693 ; to boot ELKS at some point.
697 ; Store standard filename prefix
699 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
700 jnz .got_prefix
701 mov si,BootFile
702 mov di,PathPrefix
704 call strcpy
705 mov cx,di
706 sub cx,PathPrefix+1
708 lea si,[di-2] ; Skip final null!
709 .find_alnum: lodsb
710 or al,20h
711 cmp al,'.' ; Count . or - as alphanum
712 je .alnum
713 cmp al,'-'
714 je .alnum
715 cmp al,'0'
716 jb .notalnum
717 cmp al,'9'
718 jbe .alnum
719 cmp al,'a'
720 jb .notalnum
721 cmp al,'z'
722 ja .notalnum
723 .alnum: loop .find_alnum
724 dec si
725 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
727 .got_prefix:
728 mov si,tftpprefix_msg
729 call writestr
730 mov si,PathPrefix
731 call writestr
732 call crlf
735 ; Load configuration file
737 find_config:
740 ; Begin looking for configuration file
742 config_scan:
743 test byte [DHCPMagic], 02h
744 jz .no_option
746 ; We got a DHCP option, try it first
747 call .try
748 jnz .success
750 .no_option:
751 mov di,ConfigName
752 mov si,cfgprefix
753 mov cx,cfgprefix_len
754 rep movsb
756 ; Have to guess config file name...
758 ; Try loading by UUID.
759 cmp byte [HaveUUID],0
760 je .no_uuid
762 push di
763 mov bx,uuid_dashes
764 mov si,UUID
765 .gen_uuid:
766 movzx cx,byte [bx]
767 jcxz .done_uuid
768 inc bx
769 call lchexbytes
770 mov al,'-'
771 stosb
772 jmp .gen_uuid
773 .done_uuid:
774 mov [di-1],cl ; Remove last dash and zero-terminate
775 pop di
776 call .try
777 jnz .success
778 .no_uuid:
780 ; Try loading by MAC address
781 push di
782 mov si,MACStr
783 call strcpy
784 pop di
785 call .try
786 jnz .success
788 ; Nope, try hexadecimal IP prefixes...
789 .scan_ip:
790 mov cx,4
791 mov si,MyIP
792 call uchexbytes ; Convert to hex string
794 mov cx,8 ; Up to 8 attempts
795 .tryagain:
796 mov byte [di],0 ; Zero-terminate string
797 call .try
798 jnz .success
799 dec di ; Drop one character
800 loop .tryagain
802 ; Final attempt: "default" string
803 mov si,default_str ; "default" string
804 call strcpy
805 call .try
806 jnz .success
808 mov si,err_noconfig
809 call writestr
810 jmp kaboom
812 .try:
813 pusha
814 mov si,trying_msg
815 call writestr
816 mov di,ConfigName
817 mov si,di
818 call writestr
819 call crlf
820 mov si,di
821 mov di,getcbuf
822 call mangle_name
823 call open
824 popa
828 .success:
831 ; Linux kernel loading code is common. However, we need to define
832 ; a couple of helper macros...
835 ; Handle "ipappend" option
836 %define HAVE_SPECIAL_APPEND
837 %macro SPECIAL_APPEND 0
838 test byte [IPAppend],01h ; ip=
839 jz .noipappend1
840 mov si,IPOption
841 mov cx,[IPOptionLen]
842 rep movsb
843 mov al,' '
844 stosb
845 .noipappend1:
846 test byte [IPAppend],02h
847 jz .noipappend2
848 mov si,BOOTIFStr
849 call strcpy
850 mov byte [es:di-1],' ' ; Replace null with space
851 .noipappend2:
852 %endmacro
854 ; Unload PXE stack
855 %define HAVE_UNLOAD_PREP
856 %macro UNLOAD_PREP 0
857 call unload_pxe
858 %endmacro
861 ; Now we have the config file open. Parse the config file and
862 ; run the user interface.
864 %include "ui.inc"
867 ; Boot to the local disk by returning the appropriate PXE magic.
868 ; AX contains the appropriate return code.
870 local_boot:
871 push cs
872 pop ds
873 mov [LocalBootType],ax
874 call vgaclearmode
875 mov si,localboot_msg
876 call writestr
877 ; Restore the environment we were called with
878 lss sp,[InitStack]
879 pop gs
880 pop fs
881 pop es
882 pop ds
883 popad
884 mov ax,[cs:LocalBootType]
885 popfd
886 retf ; Return to PXE
889 ; kaboom: write a message and bail out. Wait for quite a while,
890 ; or a user keypress, then do a hard reboot.
892 kaboom:
893 RESET_STACK_AND_SEGS AX
894 .patch: mov si,bailmsg
895 call writestr ; Returns with AL = 0
896 .drain: call pollchar
897 jz .drained
898 call getchar
899 jmp short .drain
900 .drained:
901 mov edi,[RebootTime]
902 mov al,[DHCPMagic]
903 and al,09h ; Magic+Timeout
904 cmp al,09h
905 je .time_set
906 mov edi,REBOOT_TIME
907 .time_set:
908 mov cx,18
909 .wait1: push cx
910 mov ecx,edi
911 .wait2: mov dx,[BIOS_timer]
912 .wait3: call pollchar
913 jnz .keypress
914 cmp dx,[BIOS_timer]
915 je .wait3
916 loop .wait2,ecx
917 mov al,'.'
918 call writechr
919 pop cx
920 loop .wait1
921 .keypress:
922 call crlf
923 mov word [BIOS_magic],0 ; Cold reboot
924 jmp 0F000h:0FFF0h ; Reset vector address
927 ; memory_scan_for_pxe_struct:
929 ; If none of the standard methods find the !PXE structure, look for it
930 ; by scanning memory.
932 ; On exit, if found:
933 ; CF = 0, ES:BX -> !PXE structure
934 ; Otherwise CF = 1, all registers saved
936 memory_scan_for_pxe_struct:
937 push ds
938 pusha
939 mov ax,cs
940 mov ds,ax
941 mov si,trymempxe_msg
942 call writestr
943 mov ax,[BIOS_fbm] ; Starting segment
944 shl ax,(10-4) ; Kilobytes -> paragraphs
945 ; mov ax,01000h ; Start to look here
946 dec ax ; To skip inc ax
947 .mismatch:
948 inc ax
949 cmp ax,0A000h ; End of memory
950 jae .not_found
951 call writehex4
952 mov si,fourbs_msg
953 call writestr
954 mov es,ax
955 mov edx,[es:0]
956 cmp edx,'!PXE'
957 jne .mismatch
958 movzx cx,byte [es:4] ; Length of structure
959 cmp cl,08h ; Minimum length
960 jb .mismatch
961 push ax
962 xor ax,ax
963 xor si,si
964 .checksum: es lodsb
965 add ah,al
966 loop .checksum
967 pop ax
968 jnz .mismatch ; Checksum must == 0
969 .found: mov bp,sp
970 xor bx,bx
971 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
972 mov ax,es
973 call writehex4
974 call crlf
975 popa
976 pop ds
979 .not_found: mov si,notfound_msg
980 call writestr
981 popa
982 pop ds
987 ; memory_scan_for_pxenv_struct:
989 ; If none of the standard methods find the PXENV+ structure, look for it
990 ; by scanning memory.
992 ; On exit, if found:
993 ; CF = 0, ES:BX -> PXENV+ structure
994 ; Otherwise CF = 1, all registers saved
996 memory_scan_for_pxenv_struct:
997 pusha
998 mov si,trymempxenv_msg
999 call writestr
1000 ; mov ax,[BIOS_fbm] ; Starting segment
1001 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1002 mov ax,01000h ; Start to look here
1003 dec ax ; To skip inc ax
1004 .mismatch:
1005 inc ax
1006 cmp ax,0A000h ; End of memory
1007 jae .not_found
1008 mov es,ax
1009 mov edx,[es:0]
1010 cmp edx,'PXEN'
1011 jne .mismatch
1012 mov dx,[es:4]
1013 cmp dx,'V+'
1014 jne .mismatch
1015 movzx cx,byte [es:8] ; Length of structure
1016 cmp cl,26h ; Minimum length
1017 jb .mismatch
1018 xor ax,ax
1019 xor si,si
1020 .checksum: es lodsb
1021 add ah,al
1022 loop .checksum
1023 and ah,ah
1024 jnz .mismatch ; Checksum must == 0
1025 .found: mov bp,sp
1026 mov [bp+8],bx ; Save BX into stack frame
1027 mov ax,bx
1028 call writehex4
1029 call crlf
1032 .not_found: mov si,notfound_msg
1033 call writestr
1034 popad
1039 ; close_file:
1040 ; Deallocates a file structure (pointer in SI)
1041 ; Assumes CS == DS.
1043 ; XXX: We should check to see if this file is still open on the server
1044 ; side and send a courtesy ERROR packet to the server.
1046 close_file:
1047 and si,si
1048 jz .closed
1049 mov word [si],0 ; Not in use
1050 .closed: ret
1053 ; searchdir:
1055 ; Open a TFTP connection to the server
1057 ; On entry:
1058 ; DS:DI = mangled filename
1059 ; If successful:
1060 ; ZF clear
1061 ; SI = socket pointer
1062 ; DX:AX = file length in bytes
1063 ; If unsuccessful
1064 ; ZF set
1067 searchdir:
1068 push es
1069 push bx
1070 push cx
1071 mov ax,ds
1072 mov es,ax
1073 mov si,di
1074 push bp
1075 mov bp,sp
1077 call allocate_socket
1078 jz .ret
1080 mov ax,PKT_RETRY ; Retry counter
1081 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1083 .sendreq: push ax ; [bp-2] - Retry counter
1084 push si ; [bp-4] - File name
1086 mov di,packet_buf
1087 mov [pxe_udp_write_pkt.buffer],di
1089 mov ax,TFTP_RRQ ; TFTP opcode
1090 stosw
1092 lodsd ; EAX <- server override (if any)
1093 and eax,eax
1094 jnz .noprefix ; No prefix, and we have the server
1096 push si ; Add common prefix
1097 mov si,PathPrefix
1098 call strcpy
1099 dec di
1100 pop si
1102 mov eax,[ServerIP] ; Get default server
1104 .noprefix:
1105 call strcpy ; Filename
1107 mov [bx+tftp_remoteip],eax
1109 push bx ; [bp-6] - TFTP block
1110 mov bx,[bx]
1111 push bx ; [bp-8] - TID (local port no)
1113 mov [pxe_udp_write_pkt.status],byte 0
1114 mov [pxe_udp_write_pkt.sip],eax
1115 ; Now figure out the gateway
1116 xor eax,[MyIP]
1117 and eax,[Netmask]
1118 jz .nogwneeded
1119 mov eax,[Gateway]
1120 .nogwneeded:
1121 mov [pxe_udp_write_pkt.gip],eax
1122 mov [pxe_udp_write_pkt.lport],bx
1123 mov ax,[ServerPort]
1124 mov [pxe_udp_write_pkt.rport],ax
1125 mov si,tftp_tail
1126 mov cx,tftp_tail_len
1127 rep movsb
1128 sub di,packet_buf ; Get packet size
1129 mov [pxe_udp_write_pkt.buffersize],di
1131 mov di,pxe_udp_write_pkt
1132 mov bx,PXENV_UDP_WRITE
1133 call pxenv
1134 jc .failure
1135 cmp word [pxe_udp_write_pkt.status],byte 0
1136 jne .failure
1139 ; Danger, Will Robinson! We need to support timeout
1140 ; and retry lest we just lost a packet...
1143 ; Packet transmitted OK, now we need to receive
1144 .getpacket: push word [PktTimeout] ; [bp-10]
1145 push word [BIOS_timer] ; [bp-12]
1147 .pkt_loop: mov bx,[bp-8] ; TID
1148 mov di,packet_buf
1149 mov word [pxe_udp_read_pkt.status],0
1150 mov [pxe_udp_read_pkt.buffer],di
1151 mov [pxe_udp_read_pkt.buffer+2],ds
1152 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1153 mov eax,[MyIP]
1154 mov [pxe_udp_read_pkt.dip],eax
1155 mov [pxe_udp_read_pkt.lport],bx
1156 mov di,pxe_udp_read_pkt
1157 mov bx,PXENV_UDP_READ
1158 call pxenv
1159 and ax,ax
1160 jz .got_packet ; Wait for packet
1161 .no_packet:
1162 mov dx,[BIOS_timer]
1163 cmp dx,[bp-12]
1164 je .pkt_loop
1165 mov [bp-12],dx
1166 dec word [bp-10] ; Timeout
1167 jnz .pkt_loop
1168 pop ax ; Adjust stack
1169 pop ax
1170 shl word [PktTimeout],1 ; Exponential backoff
1171 jmp .failure
1173 .got_packet:
1174 mov si,[bp-6] ; TFTP pointer
1175 mov bx,[bp-8] ; TID
1177 mov eax,[si+tftp_remoteip]
1178 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1179 jne .no_packet
1181 ; Got packet - reset timeout
1182 mov word [PktTimeout],PKT_TIMEOUT
1184 pop ax ; Adjust stack
1185 pop ax
1187 mov ax,[pxe_udp_read_pkt.rport]
1188 mov [si+tftp_remoteport],ax
1190 ; filesize <- -1 == unknown
1191 mov dword [si+tftp_filesize], -1
1192 ; Default blksize unless blksize option negotiated
1193 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1195 mov cx,[pxe_udp_read_pkt.buffersize]
1196 sub cx,2 ; CX <- bytes after opcode
1197 jb .failure ; Garbled reply
1199 mov si,packet_buf
1200 lodsw
1202 cmp ax, TFTP_ERROR
1203 je .bailnow ; ERROR reply: don't try again
1205 cmp ax, TFTP_OACK
1206 jne .no_tsize
1208 ; Now we need to parse the OACK packet to get the transfer
1209 ; size. SI -> first byte of options; CX -> byte count
1210 .parse_oack:
1211 jcxz .no_tsize ; No options acked
1212 .get_opt_name:
1213 mov di,si
1214 mov bx,si
1215 .opt_name_loop: lodsb
1216 and al,al
1217 jz .got_opt_name
1218 or al,20h ; Convert to lowercase
1219 stosb
1220 loop .opt_name_loop
1221 ; We ran out, and no final null
1222 jmp .err_reply
1223 .got_opt_name: ; si -> option value
1224 dec cx ; bytes left in pkt
1225 jz .err_reply ; Option w/o value
1227 ; Parse option pointed to by bx; guaranteed to be
1228 ; null-terminated.
1229 push cx
1230 push si
1231 mov si,bx ; -> option name
1232 mov bx,tftp_opt_table
1233 mov cx,tftp_opts
1234 .opt_loop:
1235 push cx
1236 push si
1237 mov di,[bx] ; Option pointer
1238 mov cx,[bx+2] ; Option len
1239 repe cmpsb
1240 pop si
1241 pop cx
1242 je .get_value ; OK, known option
1243 add bx,6
1244 loop .opt_loop
1246 pop si
1247 pop cx
1248 jmp .err_reply ; Non-negotiated option returned
1250 .get_value: pop si ; si -> option value
1251 pop cx ; cx -> bytes left in pkt
1252 mov bx,[bx+4] ; Pointer to data target
1253 add bx,[bp-6] ; TFTP socket pointer
1254 xor eax,eax
1255 xor edx,edx
1256 .value_loop: lodsb
1257 and al,al
1258 jz .got_value
1259 sub al,'0'
1260 cmp al, 9
1261 ja .err_reply ; Not a decimal digit
1262 imul edx,10
1263 add edx,eax
1264 mov [bx],edx
1265 loop .value_loop
1266 ; Ran out before final null, accept anyway
1267 jmp short .done_pkt
1269 .got_value:
1270 dec cx
1271 jnz .get_opt_name ; Not end of packet
1273 ; ZF == 1
1275 ; Success, done!
1276 .done_pkt:
1277 pop si ; Junk
1278 pop si ; We want the packet ptr in SI
1280 mov eax,[si+tftp_filesize]
1281 cmp eax,-1
1282 jz .no_tsize
1283 mov edx,eax
1284 shr edx,16 ; DX:AX == EAX
1286 and eax,eax ; Set ZF depending on file size
1287 pop bp ; Junk
1288 pop bp ; Junk (retry counter)
1289 jz .error_si ; ZF = 1 need to free the socket
1290 .ret:
1291 pop bp
1292 pop cx
1293 pop bx
1294 pop es
1297 .no_tsize:
1298 .err_reply: ; Option negotiation error. Send ERROR reply.
1299 ; ServerIP and gateway are already programmed in
1300 mov si,[bp-6]
1301 mov ax,[si+tftp_remoteport]
1302 mov word [pxe_udp_write_pkt.rport],ax
1303 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1304 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1305 mov di,pxe_udp_write_pkt
1306 mov bx,PXENV_UDP_WRITE
1307 call pxenv
1309 ; Write an error message and explode
1310 mov si,err_oldtftp
1311 call writestr
1312 jmp kaboom
1314 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1316 .failure: pop bx ; Junk
1317 pop bx
1318 pop si
1319 pop ax
1320 dec ax ; Retry counter
1321 jnz .sendreq ; Try again
1323 .error: mov si,bx ; Socket pointer
1324 .error_si: ; Socket pointer already in SI
1325 call free_socket ; ZF <- 1, SI <- 0
1326 jmp .ret
1329 ; allocate_socket: Allocate a local UDP port structure
1331 ; If successful:
1332 ; ZF set
1333 ; BX = socket pointer
1334 ; If unsuccessful:
1335 ; ZF clear
1337 allocate_socket:
1338 push cx
1339 mov bx,Files
1340 mov cx,MAX_OPEN
1341 .check: cmp word [bx], byte 0
1342 je .found
1343 add bx,open_file_t_size
1344 loop .check
1345 xor cx,cx ; ZF = 1
1346 pop cx
1348 ; Allocate a socket number. Socket numbers are made
1349 ; guaranteed unique by including the socket slot number
1350 ; (inverted, because we use the loop counter cx); add a
1351 ; counter value to keep the numbers from being likely to
1352 ; get immediately reused.
1354 ; The NextSocket variable also contains the top two bits
1355 ; set. This generates a value in the range 49152 to
1356 ; 57343.
1357 .found:
1358 dec cx
1359 push ax
1360 mov ax,[NextSocket]
1361 inc ax
1362 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1363 mov [NextSocket],ax
1364 shl cx,13-MAX_OPEN_LG2
1365 add cx,ax ; ZF = 0
1366 xchg ch,cl ; Convert to network byte order
1367 mov [bx],cx ; Socket in use
1368 pop ax
1369 pop cx
1373 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1375 free_socket:
1376 push es
1377 pusha
1378 xor ax,ax
1379 mov es,ax
1380 mov di,si
1381 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1382 rep stosw
1383 popa
1384 pop es
1385 xor si,si
1389 ; parse_dotquad:
1390 ; Read a dot-quad pathname in DS:SI and output an IP
1391 ; address in EAX, with SI pointing to the first
1392 ; nonmatching character.
1394 ; Return CF=1 on error.
1396 ; No segment assumptions permitted.
1398 parse_dotquad:
1399 push cx
1400 mov cx,4
1401 xor eax,eax
1402 .parseloop:
1403 mov ch,ah
1404 mov ah,al
1405 lodsb
1406 sub al,'0'
1407 jb .notnumeric
1408 cmp al,9
1409 ja .notnumeric
1410 aad ; AL += 10 * AH; AH = 0;
1411 xchg ah,ch
1412 jmp .parseloop
1413 .notnumeric:
1414 cmp al,'.'-'0'
1415 pushf
1416 mov al,ah
1417 mov ah,ch
1418 xor ch,ch
1419 ror eax,8
1420 popf
1421 jne .error
1422 loop .parseloop
1423 jmp .done
1424 .error:
1425 loop .realerror ; If CX := 1 then we're done
1427 jmp .done
1428 .realerror:
1430 .done:
1431 dec si ; CF unchanged!
1432 pop cx
1435 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1436 ; to by ES:DI; ends on encountering any whitespace.
1437 ; DI is preserved.
1439 ; This verifies that a filename is < FILENAME_MAX characters
1440 ; and doesn't contain whitespace, and zero-pads the output buffer,
1441 ; so "repe cmpsb" can do a compare.
1443 ; The first four bytes of the manged name is the IP address of
1444 ; the download host.
1446 ; No segment assumptions permitted.
1448 mangle_name:
1449 push di
1450 push si
1451 mov eax,[cs:ServerIP]
1452 cmp byte [si],0
1453 je .noip ; Null filename?!?!
1454 cmp word [si],'::' ; Leading ::?
1455 je .gotprefix
1457 .more:
1458 inc si
1459 cmp byte [si],0
1460 je .noip
1461 cmp word [si],'::'
1462 jne .more
1464 ; We have a :: prefix of some sort, it could be either
1465 ; a DNS name or a dot-quad IP address. Try the dot-quad
1466 ; first...
1467 .here:
1468 pop si
1469 push si
1470 call parse_dotquad
1471 jc .notdq
1472 cmp word [si],'::'
1473 je .gotprefix
1474 .notdq:
1475 pop si
1476 push si
1477 call dns_resolv
1478 cmp word [si],'::'
1479 jne .noip
1480 and eax,eax
1481 jnz .gotprefix
1483 .noip:
1484 pop si
1485 xor eax,eax
1486 jmp .prefix_done
1488 .gotprefix:
1489 pop cx ; Adjust stack
1490 inc si ; Skip double colon
1491 inc si
1493 .prefix_done:
1494 stosd ; Save IP address prefix
1495 mov cx,FILENAME_MAX-5
1497 .mn_loop:
1498 lodsb
1499 cmp al,' ' ; If control or space, end
1500 jna .mn_end
1501 stosb
1502 loop .mn_loop
1503 .mn_end:
1504 inc cx ; At least one null byte
1505 xor ax,ax ; Zero-fill name
1506 rep stosb ; Doesn't do anything if CX=0
1507 pop di
1508 ret ; Done
1511 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1512 ; filename to the conventional representation. This is needed
1513 ; for the BOOT_IMAGE= parameter for the kernel.
1514 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1515 ; known to be shorter.
1517 ; DS:SI -> input mangled file name
1518 ; ES:DI -> output buffer
1520 ; On return, DI points to the first byte after the output name,
1521 ; which is set to a null byte.
1523 unmangle_name:
1524 push eax
1525 lodsd
1526 and eax,eax
1527 jz .noip
1528 call gendotquad
1529 mov ax,'::'
1530 stosw
1531 .noip:
1532 call strcpy
1533 dec di ; Point to final null byte
1534 pop eax
1538 ; pxenv
1540 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1541 ; calling convention. This is a separate local routine so
1542 ; we can hook special things from it if necessary. In particular,
1543 ; some PXE stacks seem to not like being invoked from anything but
1544 ; the initial stack, so humour it.
1547 pxenv:
1548 %if USE_PXE_PROVIDED_STACK == 0
1549 mov [cs:PXEStack],sp
1550 mov [cs:PXEStack+2],ss
1551 lss sp,[cs:InitStack]
1552 %endif
1553 .jump: call 0:pxe_thunk ; Default to calling the thunk
1554 %if USE_PXE_PROVIDED_STACK == 0
1555 lss sp,[cs:PXEStack]
1556 %endif
1557 cld ; Make sure DF <- 0
1560 ; Must be after function def due to NASM bug
1561 PXENVEntry equ pxenv.jump+1
1564 ; pxe_thunk
1566 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1567 ; calling convention (using the stack.)
1569 ; This is called as a far routine so that we can just stick it into
1570 ; the PXENVEntry variable.
1572 pxe_thunk: push es
1573 push di
1574 push bx
1575 .jump: call 0:0
1576 add sp,byte 6
1577 cmp ax,byte 1
1578 cmc ; Set CF unless ax == 0
1579 retf
1581 ; Must be after function def due to NASM bug
1582 PXEEntry equ pxe_thunk.jump+1
1585 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1587 ; In this case, get multiple blocks from a specific TCP connection.
1589 ; On entry:
1590 ; ES:BX -> Buffer
1591 ; SI -> TFTP socket pointer
1592 ; CX -> 512-byte block count; 0FFFFh = until end of file
1593 ; On exit:
1594 ; SI -> TFTP socket pointer (or 0 on EOF)
1595 ; CF = 1 -> Hit EOF
1597 getfssec: push si
1598 push fs
1599 mov di,bx
1600 mov bx,si
1601 mov ax,pktbuf_seg
1602 mov fs,ax
1604 movzx ecx,cx
1605 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1606 jz .hit_eof ; Nothing to do?
1608 .need_more:
1609 push ecx
1611 movzx eax,word [bx+tftp_bytesleft]
1612 cmp ecx,eax
1613 jna .ok_size
1614 mov ecx,eax
1615 jcxz .need_packet ; No bytes available?
1616 .ok_size:
1618 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1619 mov si,[bx+tftp_dataptr]
1620 sub [bx+tftp_bytesleft],cx
1621 fs rep movsb ; Copy from packet buffer
1622 mov [bx+tftp_dataptr],si
1624 pop ecx
1625 sub ecx,eax
1626 jnz .need_more
1629 .hit_eof:
1630 pop fs
1631 pop si
1633 ; Is there anything left of this?
1634 mov eax,[si+tftp_filesize]
1635 sub eax,[si+tftp_filepos]
1636 jnz .bytes_left ; CF <- 0
1638 cmp [si+tftp_bytesleft],ax
1639 jnz .bytes_left ; CF <- 0
1641 ; The socket is closed and the buffer drained
1642 ; Close socket structure and re-init for next user
1643 call free_socket
1645 .bytes_left:
1649 ; No data in buffer, check to see if we can get a packet...
1651 .need_packet:
1652 pop ecx
1653 mov eax,[bx+tftp_filesize]
1654 cmp eax,[bx+tftp_filepos]
1655 je .hit_eof ; Already EOF'd; socket already closed
1657 pushad
1658 push es
1659 mov si,bx
1660 call get_packet
1661 pop es
1662 popad
1664 jmp .need_more
1667 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1669 get_packet:
1670 mov ax,ds
1671 mov es,ax
1673 .packet_loop:
1674 ; Start by ACKing the previous packet; this should cause the
1675 ; next packet to be sent.
1676 mov cx,PKT_RETRY
1677 mov word [PktTimeout],PKT_TIMEOUT
1679 .send_ack: push cx ; <D> Retry count
1681 mov ax,[si+tftp_lastpkt]
1682 call ack_packet ; Send ACK
1684 ; We used to test the error code here, but sometimes
1685 ; PXE would return negative status even though we really
1686 ; did send the ACK. Now, just treat a failed send as
1687 ; a normally lost packet, and let it time out in due
1688 ; course of events.
1690 .send_ok: ; Now wait for packet.
1691 mov dx,[BIOS_timer] ; Get current time
1693 mov cx,[PktTimeout]
1694 .wait_data: push cx ; <E> Timeout
1695 push dx ; <F> Old time
1697 mov bx,[si+tftp_pktbuf]
1698 mov [pxe_udp_read_pkt.buffer],bx
1699 mov [pxe_udp_read_pkt.buffer+2],fs
1700 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1701 mov eax,[si+tftp_remoteip]
1702 mov [pxe_udp_read_pkt.sip],eax
1703 mov eax,[MyIP]
1704 mov [pxe_udp_read_pkt.dip],eax
1705 mov ax,[si+tftp_remoteport]
1706 mov [pxe_udp_read_pkt.rport],ax
1707 mov ax,[si+tftp_localport]
1708 mov [pxe_udp_read_pkt.lport],ax
1709 mov di,pxe_udp_read_pkt
1710 mov bx,PXENV_UDP_READ
1711 push si ; <G>
1712 call pxenv
1713 pop si ; <G>
1714 and ax,ax
1715 jz .recv_ok
1717 ; No packet, or receive failure
1718 mov dx,[BIOS_timer]
1719 pop ax ; <F> Old time
1720 pop cx ; <E> Timeout
1721 cmp ax,dx ; Same time -> don't advance timeout
1722 je .wait_data ; Same clock tick
1723 loop .wait_data ; Decrease timeout
1725 pop cx ; <D> Didn't get any, send another ACK
1726 shl word [PktTimeout],1 ; Exponential backoff
1727 loop .send_ack
1728 jmp kaboom ; Forget it...
1730 .recv_ok: pop dx ; <F>
1731 pop cx ; <E>
1733 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1734 jb .wait_data ; Bad size for a DATA packet
1736 mov bx,[si+tftp_pktbuf]
1737 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1738 jne .wait_data ; Then wait for something else
1740 mov ax,[si+tftp_lastpkt]
1741 xchg ah,al ; Host byte order
1742 inc ax ; Which packet are we waiting for?
1743 xchg ah,al ; Network byte order
1744 cmp [fs:bx+2],ax
1745 je .right_packet
1747 ; Wrong packet, ACK the packet and then try again
1748 ; This is presumably because the ACK got lost,
1749 ; so the server just resent the previous packet
1750 mov ax,[fs:bx+2]
1751 call ack_packet
1752 jmp .send_ok ; Reset timeout
1754 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1756 pop cx ; <D> Don't need the retry count anymore
1758 mov [si+tftp_lastpkt],ax ; Update last packet number
1760 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1761 sub cx,byte 4 ; Skip TFTP header
1763 ; If this is a zero-length block, don't mess with the pointers,
1764 ; since we may have just set up the previous block that way
1765 jz .last_block
1767 ; Set pointer to data block
1768 lea ax,[bx+4] ; Data past TFTP header
1769 mov [si+tftp_dataptr],ax
1771 add [si+tftp_filepos],ecx
1772 mov [si+tftp_bytesleft],cx
1774 cmp cx,[si+tftp_blksize] ; Is it a full block?
1775 jb .last_block ; If so, it's not EOF
1777 ; If we had the exact right number of bytes, always get
1778 ; one more packet to get the (zero-byte) EOF packet and
1779 ; close the socket.
1780 mov eax,[si+tftp_filepos]
1781 cmp [si+tftp_filesize],eax
1782 je .packet_loop
1787 .last_block: ; Last block - ACK packet immediately
1788 mov ax,[fs:bx+2]
1789 call ack_packet
1791 ; Make sure we know we are at end of file
1792 mov eax,[si+tftp_filepos]
1793 mov [si+tftp_filesize],eax
1798 ; ack_packet:
1800 ; Send ACK packet. This is a common operation and so is worth canning.
1802 ; Entry:
1803 ; SI = TFTP block
1804 ; AX = Packet # to ack (network byte order)
1805 ; Exit:
1806 ; ZF = 0 -> Error
1807 ; All registers preserved
1809 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1811 ack_packet:
1812 pushad
1813 mov [ack_packet_buf+2],ax ; Packet number to ack
1814 mov ax,[si]
1815 mov [pxe_udp_write_pkt.lport],ax
1816 mov ax,[si+tftp_remoteport]
1817 mov [pxe_udp_write_pkt.rport],ax
1818 mov eax,[si+tftp_remoteip]
1819 mov [pxe_udp_write_pkt.sip],eax
1820 xor eax,[MyIP]
1821 and eax,[Netmask]
1822 jz .nogw
1823 mov eax,[Gateway]
1824 .nogw:
1825 mov [pxe_udp_write_pkt.gip],eax
1826 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1827 mov [pxe_udp_write_pkt.buffersize], word 4
1828 mov di,pxe_udp_write_pkt
1829 mov bx,PXENV_UDP_WRITE
1830 call pxenv
1831 cmp ax,byte 0 ; ZF = 1 if write OK
1832 popad
1836 ; unload_pxe:
1838 ; This function unloads the PXE and UNDI stacks and unclaims
1839 ; the memory.
1841 unload_pxe:
1842 test byte [KeepPXE],01h ; Should we keep PXE around?
1843 jnz reset_pxe
1845 push ds
1846 push es
1848 mov ax,cs
1849 mov ds,ax
1850 mov es,ax
1852 mov si,new_api_unload
1853 cmp byte [APIVer+1],2 ; Major API version >= 2?
1854 jae .new_api
1855 mov si,old_api_unload
1856 .new_api:
1858 .call_loop: xor ax,ax
1859 lodsb
1860 and ax,ax
1861 jz .call_done
1862 xchg bx,ax
1863 mov di,pxe_unload_stack_pkt
1864 push di
1865 xor ax,ax
1866 mov cx,pxe_unload_stack_pkt_len >> 1
1867 rep stosw
1868 pop di
1869 call pxenv
1870 jc .cant_free
1871 mov ax,word [pxe_unload_stack_pkt.status]
1872 cmp ax,PXENV_STATUS_SUCCESS
1873 jne .cant_free
1874 jmp .call_loop
1876 .call_done:
1877 mov bx,0FF00h
1879 mov dx,[RealBaseMem]
1880 cmp dx,[BIOS_fbm] ; Sanity check
1881 jna .cant_free
1882 inc bx
1884 ; Check that PXE actually unhooked the INT 1Ah chain
1885 movzx eax,word [4*0x1a]
1886 movzx ecx,word [4*0x1a+2]
1887 shl ecx,4
1888 add eax,ecx
1889 shr eax,10
1890 cmp ax,dx ; Not in range
1891 jae .ok
1892 cmp ax,[BIOS_fbm]
1893 jae .cant_free
1894 ; inc bx
1896 .ok:
1897 mov [BIOS_fbm],dx
1898 .pop_ret:
1899 pop es
1900 pop ds
1903 .cant_free:
1904 mov si,cant_free_msg
1905 call writestr
1906 push ax
1907 xchg bx,ax
1908 call writehex4
1909 mov al,'-'
1910 call writechr
1911 pop ax
1912 call writehex4
1913 mov al,'-'
1914 call writechr
1915 mov eax,[4*0x1a]
1916 call writehex8
1917 call crlf
1918 jmp .pop_ret
1920 ; We want to keep PXE around, but still we should reset
1921 ; it to the standard bootup configuration
1922 reset_pxe:
1923 push es
1924 push cs
1925 pop es
1926 mov bx,PXENV_UDP_CLOSE
1927 mov di,pxe_udp_close_pkt
1928 call pxenv
1929 pop es
1933 ; gendotquad
1935 ; Take an IP address (in network byte order) in EAX and
1936 ; output a dotted quad string to ES:DI.
1937 ; DI points to terminal null at end of string on exit.
1939 gendotquad:
1940 push eax
1941 push cx
1942 mov cx,4
1943 .genchar:
1944 push eax
1945 cmp al,10 ; < 10?
1946 jb .lt10 ; If so, skip first 2 digits
1948 cmp al,100 ; < 100
1949 jb .lt100 ; If so, skip first digit
1951 aam 100
1952 ; Now AH = 100-digit; AL = remainder
1953 add ah,'0'
1954 mov [es:di],ah
1955 inc di
1957 .lt100:
1958 aam 10
1959 ; Now AH = 10-digit; AL = remainder
1960 add ah,'0'
1961 mov [es:di],ah
1962 inc di
1964 .lt10:
1965 add al,'0'
1966 stosb
1967 mov al,'.'
1968 stosb
1969 pop eax
1970 ror eax,8 ; Move next char into LSB
1971 loop .genchar
1972 dec di
1973 mov [es:di], byte 0
1974 pop cx
1975 pop eax
1978 ; uchexbytes/lchexbytes
1980 ; Take a number of bytes in memory and convert to upper/lower-case
1981 ; hexadecimal
1983 ; Input:
1984 ; DS:SI = input bytes
1985 ; ES:DI = output buffer
1986 ; CX = number of bytes
1987 ; Output:
1988 ; DS:SI = first byte after
1989 ; ES:DI = first byte after
1990 ; CX = 0
1992 ; Trashes AX, DX
1995 lchexbytes:
1996 mov dl,'a'-'9'-1
1997 jmp xchexbytes
1998 uchexbytes:
1999 mov dl,'A'-'9'-1
2000 xchexbytes:
2001 .loop:
2002 lodsb
2003 mov ah,al
2004 shr al,4
2005 call .outchar
2006 mov al,ah
2007 call .outchar
2008 loop .loop
2010 .outchar:
2011 and al,0Fh
2012 add al,'0'
2013 cmp al,'9'
2014 jna .done
2015 add al,dl
2016 .done:
2017 stosb
2021 ; pxe_get_cached_info
2023 ; Get a DHCP packet from the PXE stack into the trackbuf.
2025 ; Input:
2026 ; DL = packet type
2027 ; Output:
2028 ; CX = buffer size
2030 ; Assumes CS == DS == ES.
2032 pxe_get_cached_info:
2033 pushad
2034 mov di,pxe_bootp_query_pkt
2035 push di
2036 xor ax,ax
2037 stosw ; Status
2038 movzx ax,dl
2039 stosw ; Packet type
2040 mov ax,trackbufsize
2041 stosw ; Buffer size
2042 mov ax,trackbuf
2043 stosw ; Buffer offset
2044 xor ax,ax
2045 stosw ; Buffer segment
2047 pop di ; DI -> parameter set
2048 mov bx,PXENV_GET_CACHED_INFO
2049 call pxenv
2050 jc .err
2051 and ax,ax
2052 jnz .err
2054 popad
2055 mov cx,[pxe_bootp_query_pkt.buffersize]
2058 .err:
2059 mov si,err_pxefailed
2060 jmp kaboom
2063 ; parse_dhcp
2065 ; Parse a DHCP packet. This includes dealing with "overloaded"
2066 ; option fields (see RFC 2132, section 9.3)
2068 ; This should fill in the following global variables, if the
2069 ; information is present:
2071 ; MyIP - client IP address
2072 ; ServerIP - boot server IP address
2073 ; Netmask - network mask
2074 ; Gateway - default gateway router IP
2075 ; BootFile - boot file name
2076 ; DNSServers - DNS server IPs
2077 ; LocalDomain - Local domain name
2078 ; MACLen, MAC - Client identifier, if MACLen == 0
2080 ; This assumes the DHCP packet is in "trackbuf" and the length
2081 ; of the packet in in CX on entry.
2084 parse_dhcp:
2085 mov byte [OverLoad],0 ; Assume no overload
2086 mov eax, [trackbuf+bootp.yip]
2087 and eax, eax
2088 jz .noyip
2089 cmp al,224 ; Class D or higher -> bad
2090 jae .noyip
2091 mov [MyIP], eax
2092 .noyip:
2093 mov eax, [trackbuf+bootp.sip]
2094 and eax, eax
2095 jz .nosip
2096 cmp al,224 ; Class D or higher -> bad
2097 jae .nosip
2098 mov [ServerIP], eax
2099 .nosip:
2100 sub cx, bootp.options
2101 jbe .nooptions
2102 mov si, trackbuf+bootp.option_magic
2103 lodsd
2104 cmp eax, BOOTP_OPTION_MAGIC
2105 jne .nooptions
2106 call parse_dhcp_options
2107 .nooptions:
2108 mov si, trackbuf+bootp.bootfile
2109 test byte [OverLoad],1
2110 jz .nofileoverload
2111 mov cx,128
2112 call parse_dhcp_options
2113 jmp short .parsed_file
2114 .nofileoverload:
2115 cmp byte [si], 0
2116 jz .parsed_file ; No bootfile name
2117 mov di,BootFile
2118 mov cx,32
2119 rep movsd
2120 xor al,al
2121 stosb ; Null-terminate
2122 .parsed_file:
2123 mov si, trackbuf+bootp.sname
2124 test byte [OverLoad],2
2125 jz .nosnameoverload
2126 mov cx,64
2127 call parse_dhcp_options
2128 .nosnameoverload:
2132 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2133 ; size is CX -- some DHCP servers leave option fields unterminated
2134 ; in violation of the spec.
2136 ; For parse_some_dhcp_options, DH contains the minimum value for
2137 ; the option to recognize -- this is used to restrict parsing to
2138 ; PXELINUX-specific options only.
2140 parse_dhcp_options:
2141 xor dx,dx
2143 parse_some_dhcp_options:
2144 .loop:
2145 and cx,cx
2146 jz .done
2148 lodsb
2149 dec cx
2150 jz .done ; Last byte; must be PAD, END or malformed
2151 cmp al, 0 ; PAD option
2152 je .loop
2153 cmp al,255 ; END option
2154 je .done
2156 ; Anything else will have a length field
2157 mov dl,al ; DL <- option number
2158 xor ax,ax
2159 lodsb ; AX <- option length
2160 dec cx
2161 sub cx,ax ; Decrement bytes left counter
2162 jb .done ; Malformed option: length > field size
2164 cmp dl,dh ; Is the option value valid?
2165 jb .opt_done
2167 mov bx,dhcp_option_list
2168 .find_option:
2169 cmp bx,dhcp_option_list_end
2170 jae .opt_done
2171 cmp dl,[bx]
2172 je .found_option
2173 add bx,3
2174 jmp .find_option
2175 .found_option:
2176 pushad
2177 call [bx+1]
2178 popad
2180 ; Fall through
2181 ; Unknown option. Skip to the next one.
2182 .opt_done:
2183 add si,ax
2184 jmp .loop
2185 .done:
2188 section .data
2189 dhcp_option_list:
2190 section .text
2192 %macro dopt 2
2193 section .data
2194 db %1
2195 dw dopt_%2
2196 section .text
2197 dopt_%2:
2198 %endmacro
2201 ; Parse individual DHCP options. SI points to the option data and
2202 ; AX to the option length. DL contains the option number.
2203 ; All registers are saved around the routine.
2205 dopt 1, subnet_mask
2206 mov ebx,[si]
2207 mov [Netmask],ebx
2210 dopt 3, router
2211 mov ebx,[si]
2212 mov [Gateway],ebx
2215 dopt 6, dns_servers
2216 mov cx,ax
2217 shr cx,2
2218 cmp cl,DNS_MAX_SERVERS
2219 jna .oklen
2220 mov cl,DNS_MAX_SERVERS
2221 .oklen:
2222 mov di,DNSServers
2223 rep movsd
2224 mov [LastDNSServer],di
2227 dopt 16, local_domain
2228 mov bx,si
2229 add bx,ax
2230 xor ax,ax
2231 xchg [bx],al ; Zero-terminate option
2232 mov di,LocalDomain
2233 call dns_mangle ; Convert to DNS label set
2234 mov [bx],al ; Restore ending byte
2237 dopt 43, vendor_encaps
2238 mov dh,208 ; Only recognize PXELINUX options
2239 mov cx,ax ; Length of option = max bytes to parse
2240 call parse_some_dhcp_options ; Parse recursive structure
2243 dopt 52, option_overload
2244 mov bl,[si]
2245 mov [OverLoad],bl
2248 dopt 54, server
2249 mov eax,[si]
2250 cmp dword [ServerIP],0
2251 jne .skip ; Already have a next server IP
2252 cmp al,224 ; Class D or higher
2253 jae .skip
2254 mov [ServerIP],eax
2255 .skip: ret
2257 dopt 61, client_identifier
2258 cmp ax,MAC_MAX ; Too long?
2259 ja .skip
2260 cmp ax,2 ; Too short?
2261 jb .skip
2262 cmp [MACLen],ah ; Only do this if MACLen == 0
2263 jne .skip
2264 push ax
2265 lodsb ; Client identifier type
2266 cmp al,[MACType]
2267 pop ax
2268 jne .skip ; Client identifier is not a MAC
2269 dec ax
2270 mov [MACLen],al
2271 mov di,MAC
2272 jmp dhcp_copyoption
2273 .skip: ret
2275 dopt 67, bootfile_name
2276 mov di,BootFile
2277 jmp dhcp_copyoption
2279 dopt 97, uuid_client_identifier
2280 cmp ax,17 ; type byte + 16 bytes UUID
2281 jne .skip
2282 mov dl,[si] ; Must have type 0 == UUID
2283 or dl,[HaveUUID] ; Capture only the first instance
2284 jnz .skip
2285 mov byte [HaveUUID],1 ; Got UUID
2286 mov di,UUIDType
2287 jmp dhcp_copyoption
2288 .skip: ret
2290 dopt 209, pxelinux_configfile
2291 mov di,ConfigName
2292 or byte [DHCPMagic],2 ; Got config file
2293 jmp dhcp_copyoption
2295 dopt 210, pxelinux_pathprefix
2296 mov di,PathPrefix
2297 or byte [DHCPMagic],4 ; Got path prefix
2298 jmp dhcp_copyoption
2300 dopt 211, pxelinux_reboottime
2301 cmp al,4
2302 jne .done
2303 mov ebx,[si]
2304 xchg bl,bh ; Convert to host byte order
2305 rol ebx,16
2306 xchg bl,bh
2307 mov [RebootTime],ebx
2308 or byte [DHCPMagic],8 ; Got RebootTime
2309 .done: ret
2311 ; Common code for copying an option verbatim
2312 ; Copies the option into ES:DI and null-terminates it.
2313 ; Returns with AX=0 and SI past the option.
2314 dhcp_copyoption:
2315 xchg cx,ax ; CX <- option length
2316 rep movsb
2317 xchg cx,ax ; AX <- 0
2318 stosb ; Null-terminate
2321 section .data
2322 dhcp_option_list_end:
2323 section .text
2325 section .data
2326 HaveUUID db 0
2327 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2328 section .text
2331 ; genipopt
2333 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2334 ; option into IPOption based on a DHCP packet in trackbuf.
2335 ; Assumes CS == DS == ES.
2337 genipopt:
2338 pushad
2339 mov di,IPOption
2340 mov eax,'ip='
2341 stosd
2342 dec di
2343 mov eax,[MyIP]
2344 call gendotquad
2345 mov al,':'
2346 stosb
2347 mov eax,[ServerIP]
2348 call gendotquad
2349 mov al,':'
2350 stosb
2351 mov eax,[Gateway]
2352 call gendotquad
2353 mov al,':'
2354 stosb
2355 mov eax,[Netmask]
2356 call gendotquad ; Zero-terminates its output
2357 sub di,IPOption
2358 mov [IPOptionLen],di
2359 popad
2363 ; Call the receive loop while idle. This is done mostly so we can respond to
2364 ; ARP messages, but perhaps in the future this can be used to do network
2365 ; console.
2367 ; hpa sez: people using automatic control on the serial port get very
2368 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2369 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2370 ; passed since the last poll, and reset this when a character is
2371 ; received (RESET_IDLE).
2373 %if HAVE_IDLE
2375 reset_idle:
2376 push ax
2377 mov ax,[cs:BIOS_timer]
2378 mov [cs:IdleTimer],ax
2379 pop ax
2382 check_for_arp:
2383 push ax
2384 mov ax,[cs:BIOS_timer]
2385 sub ax,[cs:IdleTimer]
2386 cmp ax,4
2387 pop ax
2388 jae .need_poll
2390 .need_poll: pushad
2391 push ds
2392 push es
2393 mov ax,cs
2394 mov ds,ax
2395 mov es,ax
2396 mov di,packet_buf
2397 mov [pxe_udp_read_pkt.status],al ; 0
2398 mov [pxe_udp_read_pkt.buffer],di
2399 mov [pxe_udp_read_pkt.buffer+2],ds
2400 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2401 mov eax,[MyIP]
2402 mov [pxe_udp_read_pkt.dip],eax
2403 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2404 mov di,pxe_udp_read_pkt
2405 mov bx,PXENV_UDP_READ
2406 call pxenv
2407 ; Ignore result...
2408 pop es
2409 pop ds
2410 popad
2411 RESET_IDLE
2414 %endif ; HAVE_IDLE
2416 ; -----------------------------------------------------------------------------
2417 ; Common modules
2418 ; -----------------------------------------------------------------------------
2420 %include "getc.inc" ; getc et al
2421 %include "conio.inc" ; Console I/O
2422 %include "writestr.inc" ; String output
2423 writestr equ cwritestr
2424 %include "writehex.inc" ; Hexadecimal output
2425 %include "configinit.inc" ; Initialize configuration
2426 %include "parseconfig.inc" ; High-level config file handling
2427 %include "parsecmd.inc" ; Low-level config file handling
2428 %include "bcopy32.inc" ; 32-bit bcopy
2429 %include "loadhigh.inc" ; Load a file into high memory
2430 %include "font.inc" ; VGA font stuff
2431 %include "graphics.inc" ; VGA graphics
2432 %include "highmem.inc" ; High memory sizing
2433 %include "strcpy.inc" ; strcpy()
2434 %include "rawcon.inc" ; Console I/O w/o using the console functions
2435 %include "dnsresolv.inc" ; DNS resolver
2436 %include "adv.inc" ; Auxillary Data Vector
2438 ; -----------------------------------------------------------------------------
2439 ; Begin data section
2440 ; -----------------------------------------------------------------------------
2442 section .data
2444 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2445 db CR, LF, 0
2446 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2447 bailmsg equ err_bootfailed
2448 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2449 err_pxefailed db 'PXE API call failed, error ', 0
2450 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2451 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2452 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2453 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2454 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2455 apiver_str db 'PXE API version is ',0
2456 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2457 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2458 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2459 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2460 undi_data_msg db 'UNDI data segment at: ',0
2461 undi_data_len_msg db 'UNDI data segment size: ',0
2462 undi_code_msg db 'UNDI code segment at: ',0
2463 undi_code_len_msg db 'UNDI code segment size: ',0
2464 cant_free_msg db 'Failed to free base memory, error ', 0
2465 notfound_msg db 'not found', CR, LF, 0
2466 myipaddr_msg db 'My IP address seems to be ',0
2467 tftpprefix_msg db 'TFTP prefix: ', 0
2468 localboot_msg db 'Booting from local disk...', CR, LF, 0
2469 trying_msg db 'Trying to load: ', 0
2470 fourbs_msg db BS, BS, BS, BS, 0
2471 default_str db 'default', 0
2472 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2473 cfgprefix db 'pxelinux.cfg/' ; No final null!
2474 cfgprefix_len equ ($-cfgprefix)
2477 ; Command line options we'd like to take a look at
2479 ; mem= and vga= are handled as normal 32-bit integer values
2480 initrd_cmd db 'initrd='
2481 initrd_cmd_len equ $-initrd_cmd
2483 ; This one we make ourselves
2484 bootif_str db 'BOOTIF='
2485 bootif_str_len equ $-bootif_str
2487 ; Config file keyword table
2489 %include "keywords.inc"
2492 ; Extensions to search for (in *forward* order).
2493 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2495 align 4, db 0
2496 exten_table: db '.cbt' ; COMBOOT (specific)
2497 db '.0', 0, 0 ; PXE bootstrap program
2498 db '.com' ; COMBOOT (same as DOS)
2499 db '.c32' ; COM32
2500 exten_table_end:
2501 dd 0, 0 ; Need 8 null bytes here
2504 ; PXE unload sequences
2506 new_api_unload:
2507 db PXENV_UDP_CLOSE
2508 db PXENV_UNDI_SHUTDOWN
2509 db PXENV_UNLOAD_STACK
2510 db PXENV_STOP_UNDI
2511 db 0
2512 old_api_unload:
2513 db PXENV_UDP_CLOSE
2514 db PXENV_UNDI_SHUTDOWN
2515 db PXENV_UNLOAD_STACK
2516 db PXENV_UNDI_CLEANUP
2517 db 0
2520 ; PXE query packets partially filled in
2522 section .bss
2523 pxe_bootp_query_pkt:
2524 .status: resw 1 ; Status
2525 .packettype: resw 1 ; Boot server packet type
2526 .buffersize: resw 1 ; Packet size
2527 .buffer: resw 2 ; seg:off of buffer
2528 .bufferlimit: resw 1 ; Unused
2530 section .data
2531 pxe_udp_open_pkt:
2532 .status: dw 0 ; Status
2533 .sip: dd 0 ; Source (our) IP
2535 pxe_udp_close_pkt:
2536 .status: dw 0 ; Status
2538 pxe_udp_write_pkt:
2539 .status: dw 0 ; Status
2540 .sip: dd 0 ; Server IP
2541 .gip: dd 0 ; Gateway IP
2542 .lport: dw 0 ; Local port
2543 .rport: dw 0 ; Remote port
2544 .buffersize: dw 0 ; Size of packet
2545 .buffer: dw 0, 0 ; seg:off of buffer
2547 pxe_udp_read_pkt:
2548 .status: dw 0 ; Status
2549 .sip: dd 0 ; Source IP
2550 .dip: dd 0 ; Destination (our) IP
2551 .rport: dw 0 ; Remote port
2552 .lport: dw 0 ; Local port
2553 .buffersize: dw 0 ; Max packet size
2554 .buffer: dw 0, 0 ; seg:off of buffer
2557 ; Misc initialized (data) variables
2559 alignb 4, db 0
2560 BaseStack dd StackBuf ; ESP of base stack
2561 dw 0 ; SS of base stack
2562 NextSocket dw 49152 ; Counter for allocating socket numbers
2563 KeepPXE db 0 ; Should PXE be kept around?
2566 ; TFTP commands
2568 tftp_tail db 'octet', 0 ; Octet mode
2569 tsize_str db 'tsize' ,0 ; Request size
2570 tsize_len equ ($-tsize_str)
2571 db '0', 0
2572 blksize_str db 'blksize', 0 ; Request large blocks
2573 blksize_len equ ($-blksize_str)
2574 asciidec TFTP_LARGEBLK
2575 db 0
2576 tftp_tail_len equ ($-tftp_tail)
2578 alignb 2, db 0
2580 ; Options negotiation parsing table (string pointer, string len, offset
2581 ; into socket structure)
2583 tftp_opt_table:
2584 dw tsize_str, tsize_len, tftp_filesize
2585 dw blksize_str, blksize_len, tftp_blksize
2586 tftp_opts equ ($-tftp_opt_table)/6
2589 ; Error packet to return on options negotiation error
2591 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2592 dw TFTP_EOPTNEG ; ERROR 8: bad options
2593 db 'tsize option required', 0 ; Error message
2594 tftp_opt_err_len equ ($-tftp_opt_err)
2596 alignb 4, db 0
2597 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2600 ; IP information (initialized to "unknown" values)
2601 MyIP dd 0 ; My IP address
2602 ServerIP dd 0 ; IP address of boot server
2603 Netmask dd 0 ; Netmask of this subnet
2604 Gateway dd 0 ; Default router
2605 ServerPort dw TFTP_PORT ; TFTP server port
2608 ; Variables that are uninitialized in SYSLINUX but initialized here
2610 alignb 4, db 0
2611 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2612 BufSafeBytes dw trackbufsize ; = how many bytes?
2613 %ifndef DEPEND
2614 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2615 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2616 %endif
2617 %endif