1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
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
10 ; Copyright (C) 1994-2007 H. Peter Anvin
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 ; ****************************************************************************
24 ; gPXE extensions support
28 ; Some semi-configurable constants... change on your own risk.
31 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
32 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
33 NULLFILE equ 0 ; Zero byte == null file name
34 NULLOFFSET equ 4 ; Position in which to look
35 REBOOT_TIME equ 5*60 ; If failure, time until full reset
36 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
37 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
38 MAX_OPEN equ (1 << MAX_OPEN_LG2)
39 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
40 TFTP_PORT equ htons(69) ; Default TFTP port
41 PKT_RETRY equ 6 ; Packet transmit retry count
42 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
43 ; Desired TFTP block size
44 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
45 ; be a fair number of networks with "substandard" MTUs which break.
46 ; The code assumes TFTP_LARGEBLK <= 2K.
48 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
49 ; Standard TFTP block size
50 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
51 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
52 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
54 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
55 SECTOR_SIZE equ TFTP_BLOCKSIZE
58 ; This is what we need to do when idle
59 ; *** This is disabled because some PXE stacks wait for unacceptably
60 ; *** long if there are no packets receivable.
62 %define HAVE_IDLE 0 ; idle is not a noop
81 ; TFTP operation codes
83 TFTP_RRQ equ htons(1) ; Read request
84 TFTP_WRQ equ htons(2) ; Write request
85 TFTP_DATA equ htons(3) ; Data packet
86 TFTP_ACK equ htons(4) ; ACK packet
87 TFTP_ERROR equ htons(5) ; ERROR packet
88 TFTP_OACK equ htons(6) ; OACK packet
93 TFTP_EUNDEF equ htons(0) ; Unspecified error
94 TFTP_ENOTFOUND equ htons(1) ; File not found
95 TFTP_EACCESS equ htons(2) ; Access violation
96 TFTP_ENOSPACE equ htons(3) ; Disk full
97 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
98 TFTP_EBADID equ htons(5) ; Unknown transfer
99 TFTP_EEXISTS equ htons(6) ; File exists
100 TFTP_ENOUSER equ htons(7) ; No such user
101 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
104 ; The following structure is used for "virtual kernels"; i.e. LILO-style
105 ; option labels. The options we permit here are `kernel' and `append
106 ; Since there is no room in the bottom 64K for all of these, we
107 ; stick them at vk_seg:0000 and copy them down before we need them.
110 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
111 vk_rname: resb FILENAME_MAX ; Real name
112 vk_ipappend: resb 1 ; "IPAPPEND" flag
113 vk_type: resb 1 ; Type of file
116 vk_append: resb max_cmd_len+1 ; Command line
118 vk_end: equ $ ; Should be <= vk_size
122 ; Segment assignments in the bottom 640K
123 ; 0000h - main code/data segment (and BIOS segment)
125 real_mode_seg equ 4000h
126 pktbuf_seg equ 3000h ; Packet buffers segments
127 vk_seg equ 2000h ; Virtual kernels
128 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
129 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
132 ; BOOTP/DHCP packet pattern
136 .opcode resb 1 ; BOOTP/DHCP "opcode"
137 .hardware resb 1 ; ARP hardware type
138 .hardlen resb 1 ; Hardware address length
139 .gatehops resb 1 ; Used by forwarders
140 .ident resd 1 ; Transaction ID
141 .seconds resw 1 ; Seconds elapsed
142 .flags resw 1 ; Broadcast flags
143 .cip resd 1 ; Client IP
144 .yip resd 1 ; "Your" IP
145 .sip resd 1 ; Next server IP
146 .gip resd 1 ; Relay agent IP
147 .macaddr resb 16 ; Client MAC address
148 .sname resb 64 ; Server name (optional)
149 .bootfile resb 128 ; Boot file name
150 .option_magic resd 1 ; Vendor option magic cookie
151 .options resb 1260 ; Vendor options
154 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
157 ; TFTP connection data structure. Each one of these corresponds to a local
158 ; UDP port. The size of this structure must be a power of 2.
159 ; HBO = host byte order; NBO = network byte order
160 ; (*) = written by options negotiation code, must be dword sized
162 ; For a gPXE connection, we set the local port number to -1 and the
163 ; remote port number contains the gPXE file handle.
166 tftp_localport resw 1 ; Local port number (0 = not in use)
167 tftp_remoteport resw 1 ; Remote port number
168 tftp_remoteip resd 1 ; Remote IP address
169 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
170 tftp_filesize resd 1 ; Total file size(*)
171 tftp_blksize resd 1 ; Block size for this connection(*)
172 tftp_bytesleft resw 1 ; Unclaimed data bytes
173 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
174 tftp_dataptr resw 1 ; Pointer to available data
175 resw 2 ; Currently unusued
176 ; At end since it should not be zeroed on socked close
177 tftp_pktbuf resw 1 ; Packet buffer offset
180 %if (open_file_t_size & (open_file_t_size-1))
181 %error "open_file_t is not a power of 2"
185 ; ---------------------------------------------------------------------------
187 ; ---------------------------------------------------------------------------
190 ; Memory below this point is reserved for the BIOS and the MBR
193 trackbufsize equ 8192
194 trackbuf resb trackbufsize ; Track buffer goes here
195 getcbuf resb trackbufsize
198 ; Put some large buffers here, before RBFG_brainfuck,
199 ; where we can still carefully control the address
202 alignb open_file_t_size
203 Files resb MAX_OPEN*open_file_t_size
206 BootFile resb 256 ; Boot file from DHCP packet
207 PathPrefix resb 256 ; Path prefix derived from boot file
208 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
209 IPOption resb 80 ; ip= option buffer
210 InitStack resd 1 ; Pointer to reset stack (SS:SP)
211 PXEStack resd 1 ; Saved stack during PXE call
213 ; Warning here: RBFG build 22 randomly overwrites memory location
214 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
215 ; screws up the pointer to its own internal packet buffer and starts
216 ; writing a received ARP packet into low memory.
217 RBFG_brainfuck resb 0E00h
221 RebootTime resd 1 ; Reboot timeout, if set by option
222 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
223 APIVer resw 1 ; PXE API version found
224 IPOptionLen resw 1 ; Length of IPOption
225 IdleTimer resw 1 ; Time to check for ARP?
226 LocalBootType resw 1 ; Local boot return code
227 PktTimeout resw 1 ; Timeout for current packet
228 RealBaseMem resw 1 ; Amount of DOS memory after freeing
229 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
230 DHCPMagic resb 1 ; PXELINUX magic flags
232 ; The relative position of these fields matter!
233 MAC_MAX equ 32 ; Handle hardware addresses this long
234 MACLen resb 1 ; MAC address len
235 MACType resb 1 ; MAC address type
236 MAC resb MAC_MAX+1 ; Actual MAC address
237 BOOTIFStr resb 7 ; Space for "BOOTIF="
238 MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
240 ; The relative position of these fields matter!
241 UUIDType resb 1 ; Type byte from DHCP option
242 UUID resb 16 ; UUID, from the PXE stack
243 UUIDNull resb 1 ; dhcp_copyoption zero-terminates
246 ; PXE packets which don't need static initialization
249 pxe_unload_stack_pkt:
250 .status: resw 1 ; Status
251 .reserved: resw 10 ; Reserved
252 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
255 ; BOOTP/DHCP packet buffer
258 packet_buf resb 2048 ; Transfer packet
259 packet_buf_size equ $-packet_buf
262 ; Constants for the xfer_buf_seg
264 ; The xfer_buf_seg is also used to store message file buffers. We
265 ; need two trackbuffers (text and graphics), plus a work buffer
266 ; for the graphics decompressor.
268 xbs_textbuf equ 0 ; Also hard-coded, do not change
269 xbs_vgabuf equ trackbufsize
270 xbs_vgatmpbuf equ 2*trackbufsize
274 ; PXELINUX needs more BSS than the other derivatives;
275 ; therefore we relocate it from 7C00h on startup.
277 StackBuf equ $ ; Base of stack if we use our own
280 ; Primary entry point.
284 pushfd ; Paranoia... in case of return to PXE
285 pushad ; ... save as much state as possible
296 %if TEXT_START != 0x7c00
297 ; This is uglier than it should be, but works around
298 ; some NASM 0.98.38 bugs.
299 mov di,section..bcopy32.start
300 add di,__bcopy_size-4
301 lea si,[di-(TEXT_START-7C00h)]
302 lea cx,[di-(TEXT_START-4)]
304 std ; Overlapping areas, copy backwards
308 jmp 0:_start1 ; Canonicalize address
311 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
313 ; That is all pushed onto the PXE stack. Save the pointer
314 ; to it and switch to an internal stack.
318 %if USE_PXE_PROVIDED_STACK
319 ; Apparently some platforms go bonkers if we
320 ; set up our own stack...
328 sti ; Stack set up and ready
332 ; Initialize screen (if we're using one)
334 push es ; Save ES -> PXE entry structure
338 pop es ; Restore ES -> PXE entry structure
340 ; Tell the user we got this far
342 mov si,syslinux_banner
349 ; Assume API version 2.1, in case we find the !PXE structure without
350 ; finding the PXENV+ structure. This should really look at the Base
351 ; Code ROM ID structure in have_pxe, but this is adequate for now --
352 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
353 ; about higher versions than that.
355 mov word [APIVer],0201h
358 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
359 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
360 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
361 ; We should make that the second test, and not trash ES:BX...
363 cmp dword [es:bx], '!PXE'
366 ; Uh-oh, not there... try plan B
368 %if USE_PXE_PROVIDED_STACK == 0
371 int 1Ah ; May trash regs
372 %if USE_PXE_PROVIDED_STACK == 0
380 ; Okay, that gave us the PXENV+ structure, find !PXE
381 ; structure from that (if available)
382 cmp dword [es:bx], 'PXEN'
384 cmp word [es:bx+4], 'V+'
387 ; Nothing there either. Last-ditch: scan memory
388 call memory_scan_for_pxe_struct ; !PXE scan
390 call memory_scan_for_pxenv_struct ; PXENV+ scan
393 no_pxe: mov si,err_nopxe
411 cmp ax,0201h ; API version 2.1 or higher
415 les bx,[es:bx+28h] ; !PXE structure pointer
416 cmp dword [es:bx],'!PXE'
419 ; Nope, !PXE structure missing despite API 2.1+, or at least
420 ; the pointer is missing. Do a last-ditch attempt to find it.
421 call memory_scan_for_pxe_struct
424 ; Otherwise, no dice, use PXENV+ structure
428 old_api: ; Need to use a PXENV+ structure
429 mov si,using_pxenv_msg
432 mov eax,[es:bx+0Ah] ; PXE RM API
440 mov si,undi_data_len_msg
450 mov si,undi_code_len_msg
456 ; Compute base memory size from PXENV+ structure
458 movzx eax,word [es:bx+20h] ; UNDI data seg
459 cmp ax,[es:bx+24h] ; UNDI code seg
469 shr eax,10 ; Convert to kilobytes
472 mov si,pxenventry_msg
474 mov ax,[PXENVEntry+2]
495 mov si,undi_data_len_msg
505 mov si,undi_code_len_msg
511 ; Compute base memory size from !PXE structure
538 pop es ; Restore CS == DS == ES
541 ; Network-specific initialization
544 mov [LocalDomain],al ; No LocalDomain received
547 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
548 ; packet (query info 1).
552 call pxe_get_cached_info
555 ; We don't use flags from the request packet, so
556 ; this is a good time to initialize DHCPMagic...
557 ; Initialize it to 1 meaning we will accept options found;
558 ; in earlier versions of PXELINUX bit 0 was used to indicate
559 ; we have found option 208 with the appropriate magic number;
560 ; we no longer require that, but MAY want to re-introduce
561 ; it in the future for vendor encapsulated options.
562 mov byte [DHCPMagic],1
565 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
566 ; address). This lives in the DHCPACK packet (query info 2).
570 call pxe_get_cached_info
571 call parse_dhcp ; Parse DHCP packet
573 ; Save away MAC address (assume this is in query info 2. If this
574 ; turns out to be problematic it might be better getting it from
575 ; the query info 1 packet.)
578 movzx cx,byte [trackbuf+bootp.hardlen]
581 xor cx,cx ; Bad hardware address length
584 mov al,[trackbuf+bootp.hardware]
586 mov si,trackbuf+bootp.macaddr
590 ; Enable this if we really need to zero-pad this field...
591 ; mov cx,MAC+MAC_MAX+1
597 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
598 ; packet (query info 3).
601 call pxe_get_cached_info
602 call parse_dhcp ; Parse DHCP packet
605 ; Generate the bootif string, and the hardware-based config string.
610 mov cx,bootif_str_len
613 movzx cx,byte [MACLen]
618 mov cl,1 ; CH == 0 already
624 mov [di-1],cl ; Null-terminate and strip final dash
626 ; Generate ip= option
636 call gendotquad ; This takes network byte order input
638 xchg ah,al ; Convert to host byte order
639 ror eax,16 ; (BSWAP doesn't work on 386)
656 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
657 ; if we didn't get the magic enable, do not recognize any other options.
660 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
662 mov byte [DHCPMagic], 0 ; If not, kill all other options
667 ; Initialize UDP stack
671 mov [pxe_udp_open_pkt.sip],eax
672 mov di,pxe_udp_open_pkt
673 mov bx,PXENV_UDP_OPEN
676 cmp word [pxe_udp_open_pkt.status], byte 0
678 .failed: mov si,err_udpinit
684 ; Common initialization code
686 %include "cpuinit.inc"
689 ; Now we're all set to start with our *real* business. First load the
690 ; configuration file (if any) and parse it.
692 ; In previous versions I avoided using 32-bit registers because of a
693 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
694 ; random. I figure, though, that if there are any of those still left
695 ; they probably won't be trying to install Linux on them...
697 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
698 ; to take'm out. In fact, we may want to put them back if we're going
699 ; to boot ELKS at some point.
703 ; Store standard filename prefix
705 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
714 lea si,[di-2] ; Skip final null!
717 cmp al,'.' ; Count . or - as alphanum
729 .alnum: loop .find_alnum
731 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
734 mov si,tftpprefix_msg
741 ; Load configuration file
746 ; Begin looking for configuration file
749 test byte [DHCPMagic], 02h
752 ; We got a DHCP option, try it first
762 ; Have to guess config file name...
764 ; Try loading by UUID.
765 cmp byte [HaveUUID],0
780 mov [di-1],cl ; Remove last dash and zero-terminate
786 ; Try loading by MAC address
794 ; Nope, try hexadecimal IP prefixes...
798 call uchexbytes ; Convert to hex string
800 mov cx,8 ; Up to 8 attempts
802 mov byte [di],0 ; Zero-terminate string
805 dec di ; Drop one character
808 ; Final attempt: "default" string
809 mov si,default_str ; "default" string
837 ; Linux kernel loading code is common. However, we need to define
838 ; a couple of helper macros...
841 ; Handle "ipappend" option
842 %define HAVE_SPECIAL_APPEND
843 %macro SPECIAL_APPEND 0
844 test byte [IPAppend],01h ; ip=
852 test byte [IPAppend],02h
856 mov byte [es:di-1],' ' ; Replace null with space
861 %define HAVE_UNLOAD_PREP
867 ; Now we have the config file open. Parse the config file and
868 ; run the user interface.
873 ; Boot to the local disk by returning the appropriate PXE magic.
874 ; AX contains the appropriate return code.
879 mov [LocalBootType],ax
883 ; Restore the environment we were called with
890 mov ax,[cs:LocalBootType]
895 ; kaboom: write a message and bail out. Wait for quite a while,
896 ; or a user keypress, then do a hard reboot.
899 RESET_STACK_AND_SEGS AX
900 .patch: mov si,bailmsg
901 call writestr ; Returns with AL = 0
902 .drain: call pollchar
909 and al,09h ; Magic+Timeout
917 .wait2: mov dx,[BIOS_timer]
918 .wait3: call pollchar
929 mov word [BIOS_magic],0 ; Cold reboot
930 jmp 0F000h:0FFF0h ; Reset vector address
933 ; memory_scan_for_pxe_struct:
935 ; If none of the standard methods find the !PXE structure, look for it
936 ; by scanning memory.
939 ; CF = 0, ES:BX -> !PXE structure
940 ; Otherwise CF = 1, all registers saved
942 memory_scan_for_pxe_struct:
949 mov ax,[BIOS_fbm] ; Starting segment
950 shl ax,(10-4) ; Kilobytes -> paragraphs
951 ; mov ax,01000h ; Start to look here
952 dec ax ; To skip inc ax
955 cmp ax,0A000h ; End of memory
964 movzx cx,byte [es:4] ; Length of structure
965 cmp cl,08h ; Minimum length
974 jnz .mismatch ; Checksum must == 0
977 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
985 .not_found: mov si,notfound_msg
993 ; memory_scan_for_pxenv_struct:
995 ; If none of the standard methods find the PXENV+ structure, look for it
996 ; by scanning memory.
999 ; CF = 0, ES:BX -> PXENV+ structure
1000 ; Otherwise CF = 1, all registers saved
1002 memory_scan_for_pxenv_struct:
1004 mov si,trymempxenv_msg
1006 ; mov ax,[BIOS_fbm] ; Starting segment
1007 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1008 mov ax,01000h ; Start to look here
1009 dec ax ; To skip inc ax
1012 cmp ax,0A000h ; End of memory
1021 movzx cx,byte [es:8] ; Length of structure
1022 cmp cl,26h ; Minimum length
1030 jnz .mismatch ; Checksum must == 0
1032 mov [bp+8],bx ; Save BX into stack frame
1038 .not_found: mov si,notfound_msg
1046 ; Deallocates a file structure (pointer in SI)
1049 ; XXX: We should check to see if this file is still open on the server
1050 ; side and send a courtesy ERROR packet to the server.
1055 mov word [si],0 ; Not in use
1061 ; Open a TFTP connection to the server
1064 ; DS:DI = mangled filename
1067 ; SI = socket pointer
1068 ; DX:AX = file length in bytes
1083 call allocate_socket
1086 mov ax,PKT_RETRY ; Retry counter
1087 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1089 .sendreq: push ax ; [bp-2] - Retry counter
1090 push si ; [bp-4] - File name
1093 mov [pxe_udp_write_pkt.buffer],di
1095 mov ax,TFTP_RRQ ; TFTP opcode
1098 lodsd ; EAX <- server override (if any)
1100 jnz .noprefix ; No prefix, and we have the server
1102 push si ; Add common prefix
1108 mov eax,[ServerIP] ; Get default server
1111 call strcpy ; Filename
1118 mov [bx+tftp_remoteip],eax
1120 push bx ; [bp-6] - TFTP block
1122 push bx ; [bp-8] - TID (local port no)
1124 mov [pxe_udp_write_pkt.status],byte 0
1125 mov [pxe_udp_write_pkt.sip],eax
1126 ; Now figure out the gateway
1132 mov [pxe_udp_write_pkt.gip],eax
1133 mov [pxe_udp_write_pkt.lport],bx
1135 mov [pxe_udp_write_pkt.rport],ax
1137 mov cx,tftp_tail_len
1139 sub di,packet_buf ; Get packet size
1140 mov [pxe_udp_write_pkt.buffersize],di
1142 mov di,pxe_udp_write_pkt
1143 mov bx,PXENV_UDP_WRITE
1146 cmp word [pxe_udp_write_pkt.status],byte 0
1150 ; Danger, Will Robinson! We need to support timeout
1151 ; and retry lest we just lost a packet...
1154 ; Packet transmitted OK, now we need to receive
1155 .getpacket: push word [PktTimeout] ; [bp-10]
1156 push word [BIOS_timer] ; [bp-12]
1158 .pkt_loop: mov bx,[bp-8] ; TID
1160 mov word [pxe_udp_read_pkt.status],0
1161 mov [pxe_udp_read_pkt.buffer],di
1162 mov [pxe_udp_read_pkt.buffer+2],ds
1163 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1165 mov [pxe_udp_read_pkt.dip],eax
1166 mov [pxe_udp_read_pkt.lport],bx
1167 mov di,pxe_udp_read_pkt
1168 mov bx,PXENV_UDP_READ
1171 jz .got_packet ; Wait for packet
1177 dec word [bp-10] ; Timeout
1179 pop ax ; Adjust stack
1181 shl word [PktTimeout],1 ; Exponential backoff
1185 mov si,[bp-6] ; TFTP pointer
1188 mov eax,[si+tftp_remoteip]
1189 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1192 ; Got packet - reset timeout
1193 mov word [PktTimeout],PKT_TIMEOUT
1195 pop ax ; Adjust stack
1198 mov ax,[pxe_udp_read_pkt.rport]
1199 mov [si+tftp_remoteport],ax
1201 ; filesize <- -1 == unknown
1202 mov dword [si+tftp_filesize], -1
1203 ; Default blksize unless blksize option negotiated
1204 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1206 mov cx,[pxe_udp_read_pkt.buffersize]
1207 sub cx,2 ; CX <- bytes after opcode
1208 jb .failure ; Garbled reply
1214 je .bailnow ; ERROR reply: don't try again
1219 ; Now we need to parse the OACK packet to get the transfer
1220 ; size. SI -> first byte of options; CX -> byte count
1222 jcxz .no_tsize ; No options acked
1226 .opt_name_loop: lodsb
1229 or al,20h ; Convert to lowercase
1232 ; We ran out, and no final null
1234 .got_opt_name: ; si -> option value
1235 dec cx ; bytes left in pkt
1236 jz .err_reply ; Option w/o value
1238 ; Parse option pointed to by bx; guaranteed to be
1242 mov si,bx ; -> option name
1243 mov bx,tftp_opt_table
1248 mov di,[bx] ; Option pointer
1249 mov cx,[bx+2] ; Option len
1253 je .get_value ; OK, known option
1259 jmp .err_reply ; Non-negotiated option returned
1261 .get_value: pop si ; si -> option value
1262 pop cx ; cx -> bytes left in pkt
1263 mov bx,[bx+4] ; Pointer to data target
1264 add bx,[bp-6] ; TFTP socket pointer
1272 ja .err_reply ; Not a decimal digit
1277 ; Ran out before final null, accept anyway
1282 jnz .get_opt_name ; Not end of packet
1289 pop si ; We want the packet ptr in SI
1291 mov eax,[si+tftp_filesize]
1295 pop bp ; Junk (retry counter)
1297 .got_file: ; SI->socket structure, EAX = size
1299 shr edx,16 ; DX:AX == EAX
1301 and eax,eax ; Set ZF depending on file size
1302 jz .error_si ; ZF = 1 need to free the socket
1311 .err_reply: ; Option negotiation error. Send ERROR reply.
1312 ; ServerIP and gateway are already programmed in
1314 mov ax,[si+tftp_remoteport]
1315 mov word [pxe_udp_write_pkt.rport],ax
1316 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1317 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1318 mov di,pxe_udp_write_pkt
1319 mov bx,PXENV_UDP_WRITE
1322 ; Write an error message and explode
1327 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1329 .failure: pop bx ; Junk
1333 dec ax ; Retry counter
1334 jnz .sendreq ; Try again
1336 .error: mov si,bx ; Socket pointer
1337 .error_si: ; Socket pointer already in SI
1338 call free_socket ; ZF <- 1, SI <- 0
1348 mov si,packet_buf+2 ; Completed URL
1349 mov di,gpxe_file_open
1352 mov bx,PXENV_FILE_OPEN
1354 pop si ; Packet pointer in SI
1358 mov word [si+tftp_localport],-1 ; gPXE URL
1359 mov [si+tftp_remoteport],ax
1360 mov di,gpxe_get_file_size
1363 mov bx,PXENV_GET_FILE_SIZE
1368 mov [si+tftp_filesize],eax
1373 ; allocate_socket: Allocate a local UDP port structure
1377 ; BX = socket pointer
1385 .check: cmp word [bx], byte 0
1387 add bx,open_file_t_size
1392 ; Allocate a socket number. Socket numbers are made
1393 ; guaranteed unique by including the socket slot number
1394 ; (inverted, because we use the loop counter cx); add a
1395 ; counter value to keep the numbers from being likely to
1396 ; get immediately reused.
1398 ; The NextSocket variable also contains the top two bits
1399 ; set. This generates a value in the range 49152 to
1406 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1408 shl cx,13-MAX_OPEN_LG2
1410 xchg ch,cl ; Convert to network byte order
1411 mov [bx],cx ; Socket in use
1417 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1425 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1434 ; Read a dot-quad pathname in DS:SI and output an IP
1435 ; address in EAX, with SI pointing to the first
1436 ; nonmatching character.
1438 ; Return CF=1 on error.
1440 ; No segment assumptions permitted.
1454 aad ; AL += 10 * AH; AH = 0;
1469 loop .realerror ; If CX := 1 then we're done
1475 dec si ; CF unchanged!
1480 ; is_url: Return CF=0 if and only if the buffer pointed to by
1481 ; DS:SI is a URL (contains ://). No registers modified.
1506 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1507 ; to by ES:DI; ends on encountering any whitespace.
1510 ; This verifies that a filename is < FILENAME_MAX characters
1511 ; and doesn't contain whitespace, and zero-pads the output buffer,
1512 ; so "repe cmpsb" can do a compare.
1514 ; The first four bytes of the manged name is the IP address of
1515 ; the download host, 0 for no host, or -1 for a gPXE URL.
1517 ; No segment assumptions permitted.
1524 or eax,-1 ; It's a URL
1529 mov eax,[cs:ServerIP]
1531 je .noip ; Null filename?!?!
1532 cmp word [si],'::' ; Leading ::?
1542 ; We have a :: prefix of some sort, it could be either
1543 ; a DNS name or a dot-quad IP address. Try the dot-quad
1567 pop cx ; Adjust stack
1568 inc si ; Skip double colon
1572 stosd ; Save IP address prefix
1573 mov cx,FILENAME_MAX-5
1577 cmp al,' ' ; If control or space, end
1582 inc cx ; At least one null byte
1583 xor ax,ax ; Zero-fill name
1584 rep stosb ; Doesn't do anything if CX=0
1589 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1590 ; filename to the conventional representation. This is needed
1591 ; for the BOOT_IMAGE= parameter for the kernel.
1592 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1593 ; known to be shorter.
1595 ; DS:SI -> input mangled file name
1596 ; ES:DI -> output buffer
1598 ; On return, DI points to the first byte after the output name,
1599 ; which is set to a null byte.
1613 dec di ; Point to final null byte
1620 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1621 ; calling convention. This is a separate local routine so
1622 ; we can hook special things from it if necessary. In particular,
1623 ; some PXE stacks seem to not like being invoked from anything but
1624 ; the initial stack, so humour it.
1628 %if USE_PXE_PROVIDED_STACK == 0
1629 mov [cs:PXEStack],sp
1630 mov [cs:PXEStack+2],ss
1631 lss sp,[cs:InitStack]
1633 .jump: call 0:pxe_thunk ; Default to calling the thunk
1634 %if USE_PXE_PROVIDED_STACK == 0
1635 lss sp,[cs:PXEStack]
1637 cld ; Make sure DF <- 0
1640 ; Must be after function def due to NASM bug
1641 PXENVEntry equ pxenv.jump+1
1646 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1647 ; calling convention (using the stack.)
1649 ; This is called as a far routine so that we can just stick it into
1650 ; the PXENVEntry variable.
1658 cmc ; Set CF unless ax == 0
1661 ; Must be after function def due to NASM bug
1662 PXEEntry equ pxe_thunk.jump+1
1665 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1667 ; In this case, get multiple blocks from a specific TCP connection.
1671 ; SI -> TFTP socket pointer
1672 ; CX -> 512-byte block count; 0FFFFh = until end of file
1674 ; SI -> TFTP socket pointer (or 0 on EOF)
1685 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1686 jz .hit_eof ; Nothing to do?
1691 movzx eax,word [bx+tftp_bytesleft]
1695 jcxz .need_packet ; No bytes available?
1698 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1699 mov si,[bx+tftp_dataptr]
1700 sub [bx+tftp_bytesleft],cx
1701 fs rep movsb ; Copy from packet buffer
1702 mov [bx+tftp_dataptr],si
1713 ; Is there anything left of this?
1714 mov eax,[si+tftp_filesize]
1715 sub eax,[si+tftp_filepos]
1716 jnz .bytes_left ; CF <- 0
1718 cmp [si+tftp_bytesleft],ax
1719 jnz .bytes_left ; CF <- 0
1721 ; The socket is closed and the buffer drained
1722 ; Close socket structure and re-init for next user
1729 ; No data in buffer, check to see if we can get a packet...
1733 mov eax,[bx+tftp_filesize]
1734 cmp eax,[bx+tftp_filepos]
1735 je .hit_eof ; Already EOF'd; socket already closed
1744 cmp word [si+tftp_localport], -1
1745 jne .get_packet_tftp
1746 call get_packet_gpxe
1759 ; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
1760 ; and ds:si -> socket structure
1762 ; Assumes CS == DS == ES.
1765 mov di,gpxe_file_read
1766 mov ax,[si+tftp_remoteport] ; gPXE filehandle
1768 mov word [di+4],PKTBUF_SIZE
1769 mov ax,[si+tftp_pktbuf]
1771 mov [si+tftp_dataptr],ax
1775 mov bx,PXENV_FILE_READ
1777 ; XXX: FIX THIS: Need to be able to distinguish
1778 ; error, EOF, and no data
1781 movzx eax,word [di+4] ; Bytes read
1782 mov [si+tftp_bytesleft],ax ; Bytes in buffer
1783 add [si+tftp_filepos],eax ; Position in file
1788 ; We got EOF here, make sure the upper layers know
1789 mov eax,[si+tftp_filepos]
1790 mov [si+tftp_filesize],eax
1793 ; If we're done here, close the file
1794 mov eax,[si+tftp_filepos]
1795 cmp [si+tftp_filesize],eax
1798 ; Reuse the previous [es:di] structure since the
1799 ; relevant fields are all the same
1800 mov bx,PXENV_FILE_CLOSE
1808 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1810 ; Assumes CS == DS == ES.
1814 ; Start by ACKing the previous packet; this should cause the
1815 ; next packet to be sent.
1817 mov word [PktTimeout],PKT_TIMEOUT
1819 .send_ack: push cx ; <D> Retry count
1821 mov ax,[si+tftp_lastpkt]
1822 call ack_packet ; Send ACK
1824 ; We used to test the error code here, but sometimes
1825 ; PXE would return negative status even though we really
1826 ; did send the ACK. Now, just treat a failed send as
1827 ; a normally lost packet, and let it time out in due
1830 .send_ok: ; Now wait for packet.
1831 mov dx,[BIOS_timer] ; Get current time
1834 .wait_data: push cx ; <E> Timeout
1835 push dx ; <F> Old time
1837 mov bx,[si+tftp_pktbuf]
1838 mov [pxe_udp_read_pkt.buffer],bx
1839 mov [pxe_udp_read_pkt.buffer+2],fs
1840 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1841 mov eax,[si+tftp_remoteip]
1842 mov [pxe_udp_read_pkt.sip],eax
1844 mov [pxe_udp_read_pkt.dip],eax
1845 mov ax,[si+tftp_remoteport]
1846 mov [pxe_udp_read_pkt.rport],ax
1847 mov ax,[si+tftp_localport]
1848 mov [pxe_udp_read_pkt.lport],ax
1849 mov di,pxe_udp_read_pkt
1850 mov bx,PXENV_UDP_READ
1857 ; No packet, or receive failure
1859 pop ax ; <F> Old time
1860 pop cx ; <E> Timeout
1861 cmp ax,dx ; Same time -> don't advance timeout
1862 je .wait_data ; Same clock tick
1863 loop .wait_data ; Decrease timeout
1865 pop cx ; <D> Didn't get any, send another ACK
1866 shl word [PktTimeout],1 ; Exponential backoff
1868 jmp kaboom ; Forget it...
1870 .recv_ok: pop dx ; <F>
1873 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1874 jb .wait_data ; Bad size for a DATA packet
1876 mov bx,[si+tftp_pktbuf]
1877 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1878 jne .wait_data ; Then wait for something else
1880 mov ax,[si+tftp_lastpkt]
1881 xchg ah,al ; Host byte order
1882 inc ax ; Which packet are we waiting for?
1883 xchg ah,al ; Network byte order
1887 ; Wrong packet, ACK the packet and then try again
1888 ; This is presumably because the ACK got lost,
1889 ; so the server just resent the previous packet
1892 jmp .send_ok ; Reset timeout
1894 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1896 pop cx ; <D> Don't need the retry count anymore
1898 mov [si+tftp_lastpkt],ax ; Update last packet number
1900 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1901 sub cx,byte 4 ; Skip TFTP header
1903 ; If this is a zero-length block, don't mess with the pointers,
1904 ; since we may have just set up the previous block that way
1907 ; Set pointer to data block
1908 lea ax,[bx+4] ; Data past TFTP header
1909 mov [si+tftp_dataptr],ax
1911 add [si+tftp_filepos],ecx
1912 mov [si+tftp_bytesleft],cx
1914 cmp cx,[si+tftp_blksize] ; Is it a full block?
1915 jb .last_block ; If so, it's not EOF
1917 ; If we had the exact right number of bytes, always get
1918 ; one more packet to get the (zero-byte) EOF packet and
1920 mov eax,[si+tftp_filepos]
1921 cmp [si+tftp_filesize],eax
1927 .last_block: ; Last block - ACK packet immediately
1931 ; Make sure we know we are at end of file
1932 mov eax,[si+tftp_filepos]
1933 mov [si+tftp_filesize],eax
1940 ; Send ACK packet. This is a common operation and so is worth canning.
1944 ; AX = Packet # to ack (network byte order)
1947 ; All registers preserved
1949 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1953 mov [ack_packet_buf+2],ax ; Packet number to ack
1955 mov [pxe_udp_write_pkt.lport],ax
1956 mov ax,[si+tftp_remoteport]
1957 mov [pxe_udp_write_pkt.rport],ax
1958 mov eax,[si+tftp_remoteip]
1959 mov [pxe_udp_write_pkt.sip],eax
1965 mov [pxe_udp_write_pkt.gip],eax
1966 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1967 mov [pxe_udp_write_pkt.buffersize], word 4
1968 mov di,pxe_udp_write_pkt
1969 mov bx,PXENV_UDP_WRITE
1971 cmp ax,byte 0 ; ZF = 1 if write OK
1978 ; This function unloads the PXE and UNDI stacks and unclaims
1982 test byte [KeepPXE],01h ; Should we keep PXE around?
1992 mov si,new_api_unload
1993 cmp byte [APIVer+1],2 ; Major API version >= 2?
1995 mov si,old_api_unload
1998 .call_loop: xor ax,ax
2003 mov di,pxe_unload_stack_pkt
2006 mov cx,pxe_unload_stack_pkt_len >> 1
2011 mov ax,word [pxe_unload_stack_pkt.status]
2012 cmp ax,PXENV_STATUS_SUCCESS
2019 mov dx,[RealBaseMem]
2020 cmp dx,[BIOS_fbm] ; Sanity check
2024 ; Check that PXE actually unhooked the INT 1Ah chain
2025 movzx eax,word [4*0x1a]
2026 movzx ecx,word [4*0x1a+2]
2030 cmp ax,dx ; Not in range
2044 mov si,cant_free_msg
2060 ; We want to keep PXE around, but still we should reset
2061 ; it to the standard bootup configuration
2066 mov bx,PXENV_UDP_CLOSE
2067 mov di,pxe_udp_close_pkt
2075 ; Take an IP address (in network byte order) in EAX and
2076 ; output a dotted quad string to ES:DI.
2077 ; DI points to terminal null at end of string on exit.
2086 jb .lt10 ; If so, skip first 2 digits
2089 jb .lt100 ; If so, skip first digit
2092 ; Now AH = 100-digit; AL = remainder
2099 ; Now AH = 10-digit; AL = remainder
2110 ror eax,8 ; Move next char into LSB
2118 ; uchexbytes/lchexbytes
2120 ; Take a number of bytes in memory and convert to upper/lower-case
2124 ; DS:SI = input bytes
2125 ; ES:DI = output buffer
2126 ; CX = number of bytes
2128 ; DS:SI = first byte after
2129 ; ES:DI = first byte after
2161 ; pxe_get_cached_info
2163 ; Get a DHCP packet from the PXE stack into the trackbuf.
2170 ; Assumes CS == DS == ES.
2172 pxe_get_cached_info:
2174 mov di,pxe_bootp_query_pkt
2183 stosw ; Buffer offset
2185 stosw ; Buffer segment
2187 pop di ; DI -> parameter set
2188 mov bx,PXENV_GET_CACHED_INFO
2195 mov cx,[pxe_bootp_query_pkt.buffersize]
2199 mov si,err_pxefailed
2205 ; Parse a DHCP packet. This includes dealing with "overloaded"
2206 ; option fields (see RFC 2132, section 9.3)
2208 ; This should fill in the following global variables, if the
2209 ; information is present:
2211 ; MyIP - client IP address
2212 ; ServerIP - boot server IP address
2213 ; Netmask - network mask
2214 ; Gateway - default gateway router IP
2215 ; BootFile - boot file name
2216 ; DNSServers - DNS server IPs
2217 ; LocalDomain - Local domain name
2218 ; MACLen, MAC - Client identifier, if MACLen == 0
2220 ; This assumes the DHCP packet is in "trackbuf" and the length
2221 ; of the packet in in CX on entry.
2225 mov byte [OverLoad],0 ; Assume no overload
2226 mov eax, [trackbuf+bootp.yip]
2229 cmp al,224 ; Class D or higher -> bad
2233 mov eax, [trackbuf+bootp.sip]
2236 cmp al,224 ; Class D or higher -> bad
2240 sub cx, bootp.options
2242 mov si, trackbuf+bootp.option_magic
2244 cmp eax, BOOTP_OPTION_MAGIC
2246 call parse_dhcp_options
2248 mov si, trackbuf+bootp.bootfile
2249 test byte [OverLoad],1
2252 call parse_dhcp_options
2253 jmp short .parsed_file
2256 jz .parsed_file ; No bootfile name
2261 stosb ; Null-terminate
2263 mov si, trackbuf+bootp.sname
2264 test byte [OverLoad],2
2267 call parse_dhcp_options
2272 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2273 ; size is CX -- some DHCP servers leave option fields unterminated
2274 ; in violation of the spec.
2276 ; For parse_some_dhcp_options, DH contains the minimum value for
2277 ; the option to recognize -- this is used to restrict parsing to
2278 ; PXELINUX-specific options only.
2283 parse_some_dhcp_options:
2290 jz .done ; Last byte; must be PAD, END or malformed
2291 cmp al, 0 ; PAD option
2293 cmp al,255 ; END option
2296 ; Anything else will have a length field
2297 mov dl,al ; DL <- option number
2299 lodsb ; AX <- option length
2301 sub cx,ax ; Decrement bytes left counter
2302 jb .done ; Malformed option: length > field size
2304 cmp dl,dh ; Is the option value valid?
2307 mov bx,dhcp_option_list
2309 cmp bx,dhcp_option_list_end
2321 ; Unknown option. Skip to the next one.
2341 ; Parse individual DHCP options. SI points to the option data and
2342 ; AX to the option length. DL contains the option number.
2343 ; All registers are saved around the routine.
2358 cmp cl,DNS_MAX_SERVERS
2360 mov cl,DNS_MAX_SERVERS
2364 mov [LastDNSServer],di
2367 dopt 16, local_domain
2371 xchg [bx],al ; Zero-terminate option
2373 call dns_mangle ; Convert to DNS label set
2374 mov [bx],al ; Restore ending byte
2377 dopt 43, vendor_encaps
2378 mov dh,208 ; Only recognize PXELINUX options
2379 mov cx,ax ; Length of option = max bytes to parse
2380 call parse_some_dhcp_options ; Parse recursive structure
2383 dopt 52, option_overload
2390 cmp dword [ServerIP],0
2391 jne .skip ; Already have a next server IP
2392 cmp al,224 ; Class D or higher
2397 dopt 61, client_identifier
2398 cmp ax,MAC_MAX ; Too long?
2400 cmp ax,2 ; Too short?
2402 cmp [MACLen],ah ; Only do this if MACLen == 0
2405 lodsb ; Client identifier type
2408 jne .skip ; Client identifier is not a MAC
2415 dopt 67, bootfile_name
2419 dopt 97, uuid_client_identifier
2420 cmp ax,17 ; type byte + 16 bytes UUID
2422 mov dl,[si] ; Must have type 0 == UUID
2423 or dl,[HaveUUID] ; Capture only the first instance
2425 mov byte [HaveUUID],1 ; Got UUID
2430 dopt 209, pxelinux_configfile
2432 or byte [DHCPMagic],2 ; Got config file
2435 dopt 210, pxelinux_pathprefix
2437 or byte [DHCPMagic],4 ; Got path prefix
2440 dopt 211, pxelinux_reboottime
2444 xchg bl,bh ; Convert to host byte order
2447 mov [RebootTime],ebx
2448 or byte [DHCPMagic],8 ; Got RebootTime
2451 ; Common code for copying an option verbatim
2452 ; Copies the option into ES:DI and null-terminates it.
2453 ; Returns with AX=0 and SI past the option.
2455 xchg cx,ax ; CX <- option length
2457 xchg cx,ax ; AX <- 0
2458 stosb ; Null-terminate
2462 dhcp_option_list_end:
2467 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2473 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2474 ; option into IPOption based on a DHCP packet in trackbuf.
2475 ; Assumes CS == DS == ES.
2496 call gendotquad ; Zero-terminates its output
2498 mov [IPOptionLen],di
2503 ; Call the receive loop while idle. This is done mostly so we can respond to
2504 ; ARP messages, but perhaps in the future this can be used to do network
2507 ; hpa sez: people using automatic control on the serial port get very
2508 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2509 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2510 ; passed since the last poll, and reset this when a character is
2511 ; received (RESET_IDLE).
2517 mov ax,[cs:BIOS_timer]
2518 mov [cs:IdleTimer],ax
2524 mov ax,[cs:BIOS_timer]
2525 sub ax,[cs:IdleTimer]
2537 mov [pxe_udp_read_pkt.status],al ; 0
2538 mov [pxe_udp_read_pkt.buffer],di
2539 mov [pxe_udp_read_pkt.buffer+2],ds
2540 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2542 mov [pxe_udp_read_pkt.dip],eax
2543 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2544 mov di,pxe_udp_read_pkt
2545 mov bx,PXENV_UDP_READ
2556 ; -----------------------------------------------------------------------------
2558 ; -----------------------------------------------------------------------------
2560 %include "getc.inc" ; getc et al
2561 %include "conio.inc" ; Console I/O
2562 %include "writestr.inc" ; String output
2563 writestr equ cwritestr
2564 %include "writehex.inc" ; Hexadecimal output
2565 %include "configinit.inc" ; Initialize configuration
2566 %include "parseconfig.inc" ; High-level config file handling
2567 %include "parsecmd.inc" ; Low-level config file handling
2568 %include "bcopy32.inc" ; 32-bit bcopy
2569 %include "loadhigh.inc" ; Load a file into high memory
2570 %include "font.inc" ; VGA font stuff
2571 %include "graphics.inc" ; VGA graphics
2572 %include "highmem.inc" ; High memory sizing
2573 %include "strcpy.inc" ; strcpy()
2574 %include "rawcon.inc" ; Console I/O w/o using the console functions
2575 %include "dnsresolv.inc" ; DNS resolver
2576 %include "adv.inc" ; Auxillary Data Vector
2578 ; -----------------------------------------------------------------------------
2579 ; Begin data section
2580 ; -----------------------------------------------------------------------------
2584 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2586 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2587 bailmsg equ err_bootfailed
2588 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2589 err_pxefailed db 'PXE API call failed, error ', 0
2590 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2591 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2592 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2593 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2594 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2595 apiver_str db 'PXE API version is ',0
2596 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2597 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2598 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2599 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2600 undi_data_msg db 'UNDI data segment at: ',0
2601 undi_data_len_msg db 'UNDI data segment size: ',0
2602 undi_code_msg db 'UNDI code segment at: ',0
2603 undi_code_len_msg db 'UNDI code segment size: ',0
2604 cant_free_msg db 'Failed to free base memory, error ', 0
2605 notfound_msg db 'not found', CR, LF, 0
2606 myipaddr_msg db 'My IP address seems to be ',0
2607 tftpprefix_msg db 'TFTP prefix: ', 0
2608 localboot_msg db 'Booting from local disk...', CR, LF, 0
2609 trying_msg db 'Trying to load: ', 0
2610 fourbs_msg db BS, BS, BS, BS, 0
2611 default_str db 'default', 0
2612 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2613 cfgprefix db 'pxelinux.cfg/' ; No final null!
2614 cfgprefix_len equ ($-cfgprefix)
2617 ; Command line options we'd like to take a look at
2619 ; mem= and vga= are handled as normal 32-bit integer values
2620 initrd_cmd db 'initrd='
2621 initrd_cmd_len equ $-initrd_cmd
2623 ; This one we make ourselves
2624 bootif_str db 'BOOTIF='
2625 bootif_str_len equ $-bootif_str
2627 ; Config file keyword table
2629 %include "keywords.inc"
2632 ; Extensions to search for (in *forward* order).
2633 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2636 exten_table: db '.cbt' ; COMBOOT (specific)
2637 db '.0', 0, 0 ; PXE bootstrap program
2638 db '.com' ; COMBOOT (same as DOS)
2641 dd 0, 0 ; Need 8 null bytes here
2644 ; PXE unload sequences
2648 db PXENV_UNDI_SHUTDOWN
2649 db PXENV_UNLOAD_STACK
2654 db PXENV_UNDI_SHUTDOWN
2655 db PXENV_UNLOAD_STACK
2656 db PXENV_UNDI_CLEANUP
2660 ; PXE query packets partially filled in
2663 pxe_bootp_query_pkt:
2664 .status: resw 1 ; Status
2665 .packettype: resw 1 ; Boot server packet type
2666 .buffersize: resw 1 ; Packet size
2667 .buffer: resw 2 ; seg:off of buffer
2668 .bufferlimit: resw 1 ; Unused
2672 .status: dw 0 ; Status
2673 .sip: dd 0 ; Source (our) IP
2676 .status: dw 0 ; Status
2679 .status: dw 0 ; Status
2680 .sip: dd 0 ; Server IP
2681 .gip: dd 0 ; Gateway IP
2682 .lport: dw 0 ; Local port
2683 .rport: dw 0 ; Remote port
2684 .buffersize: dw 0 ; Size of packet
2685 .buffer: dw 0, 0 ; seg:off of buffer
2688 .status: dw 0 ; Status
2689 .sip: dd 0 ; Source IP
2690 .dip: dd 0 ; Destination (our) IP
2691 .rport: dw 0 ; Remote port
2692 .lport: dw 0 ; Local port
2693 .buffersize: dw 0 ; Max packet size
2694 .buffer: dw 0, 0 ; seg:off of buffer
2699 .status: dw 0 ; Status
2700 .filehandle: dw 0 ; FileHandle
2701 .filename: dd 0 ; seg:off of FileName
2705 .status: dw 0 ; Status
2706 .filehandle: dw 0 ; FileHandle
2707 .filesize: dd 0 ; FileSize
2710 .status: dw 0 ; Status
2711 .filehandle: dw 0 ; FileHandle
2712 .buffersize: dw 0 ; BufferSize
2713 .buffer: dd 0 ; seg:off of buffer
2718 ; Misc initialized (data) variables
2721 BaseStack dd StackBuf ; ESP of base stack
2722 dw 0 ; SS of base stack
2723 NextSocket dw 49152 ; Counter for allocating socket numbers
2724 KeepPXE db 0 ; Should PXE be kept around?
2729 tftp_tail db 'octet', 0 ; Octet mode
2730 tsize_str db 'tsize' ,0 ; Request size
2731 tsize_len equ ($-tsize_str)
2733 blksize_str db 'blksize', 0 ; Request large blocks
2734 blksize_len equ ($-blksize_str)
2735 asciidec TFTP_LARGEBLK
2737 tftp_tail_len equ ($-tftp_tail)
2741 ; Options negotiation parsing table (string pointer, string len, offset
2742 ; into socket structure)
2745 dw tsize_str, tsize_len, tftp_filesize
2746 dw blksize_str, blksize_len, tftp_blksize
2747 tftp_opts equ ($-tftp_opt_table)/6
2750 ; Error packet to return on options negotiation error
2752 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2753 dw TFTP_EOPTNEG ; ERROR 8: bad options
2754 db 'tsize option required', 0 ; Error message
2755 tftp_opt_err_len equ ($-tftp_opt_err)
2758 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2761 ; IP information (initialized to "unknown" values)
2762 MyIP dd 0 ; My IP address
2763 ServerIP dd 0 ; IP address of boot server
2764 Netmask dd 0 ; Netmask of this subnet
2765 Gateway dd 0 ; Default router
2766 ServerPort dw TFTP_PORT ; TFTP server port
2769 ; Variables that are uninitialized in SYSLINUX but initialized here
2772 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2773 BufSafeBytes dw trackbufsize ; = how many bytes?
2775 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2776 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE