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 ; ****************************************************************************
25 ; Some semi-configurable constants... change on your own risk.
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.
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
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
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.
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
113 vk_append: resb max_cmd_len
+1 ; Command line
115 vk_end: equ $
; Should be <= vk_size
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
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
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
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
174 %if
(open_file_t_size
& (open_file_t_size
-1))
175 %error
"open_file_t is not a power of 2"
179 ; ---------------------------------------------------------------------------
181 ; ---------------------------------------------------------------------------
184 ; Memory below this point is reserved for the BIOS and the MBR
187 trackbufsize
equ 8192
188 trackbuf resb trackbufsize
; Track buffer goes here
189 getcbuf resb trackbufsize
192 ; Put some large buffers here, before RBFG_brainfuck,
193 ; where we can still carefully control the address
196 alignb open_file_t_size
197 Files resb MAX_OPEN
*open_file_t_size
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
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
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
249 ; BOOTP/DHCP packet buffer
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
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.
278 pushfd ; Paranoia... in case of return to PXE
279 pushad ; ... save as much state as possible
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)]
298 std ; Overlapping areas, copy backwards
302 jmp 0:_start1
; Canonicalize address
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.
312 %if USE_PXE_PROVIDED_STACK
313 ; Apparently some platforms go bonkers if we
314 ; set up our own stack...
322 sti ; Stack set up and ready
326 ; Initialize screen (if we're using one)
328 push es ; Save ES -> PXE entry structure
332 pop es ; Restore ES -> PXE entry structure
334 ; Tell the user we got this far
336 mov si,syslinux_banner
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'
360 ; Uh-oh, not there... try plan B
362 %if USE_PXE_PROVIDED_STACK
== 0
365 int 1Ah ; May trash regs
366 %if USE_PXE_PROVIDED_STACK
== 0
374 ; Okay, that gave us the PXENV+ structure, find !PXE
375 ; structure from that (if available)
376 cmp dword [es:bx], 'PXEN'
378 cmp word [es:bx+4], 'V+'
381 ; Nothing there either. Last-ditch: scan memory
382 call memory_scan_for_pxe_struct
; !PXE scan
384 call memory_scan_for_pxenv_struct
; PXENV+ scan
387 no_pxe: mov si,err_nopxe
405 cmp ax,0201h ; API version 2.1 or higher
409 les bx,[es:bx+28h] ; !PXE structure pointer
410 cmp dword [es:bx],'!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
418 ; Otherwise, no dice, use PXENV+ structure
422 old_api: ; Need to use a PXENV+ structure
423 mov si,using_pxenv_msg
426 mov eax,[es:bx+0Ah] ; PXE RM API
434 mov si,undi_data_len_msg
444 mov si,undi_code_len_msg
450 ; Compute base memory size from PXENV+ structure
452 movzx eax,word [es:bx+20h] ; UNDI data seg
453 cmp ax,[es:bx+24h] ; UNDI code seg
463 shr eax,10 ; Convert to kilobytes
466 mov si,pxenventry_msg
468 mov ax,[PXENVEntry
+2]
489 mov si,undi_data_len_msg
499 mov si,undi_code_len_msg
505 ; Compute base memory size from !PXE structure
532 pop es ; Restore CS == DS == ES
535 ; Network-specific initialization
538 mov [LocalDomain
],al ; No LocalDomain received
541 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
542 ; packet (query info 1).
546 call pxe_get_cached_info
549 ; We don't use flags from the request packet, so
550 ; this is a good time to initialize DHCPMagic...
551 mov byte [DHCPMagic
],0
554 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
555 ; address). This lives in the DHCPACK packet (query info 2).
559 call pxe_get_cached_info
560 call parse_dhcp
; Parse DHCP packet
562 ; Save away MAC address (assume this is in query info 2. If this
563 ; turns out to be problematic it might be better getting it from
564 ; the query info 1 packet.)
567 movzx cx,byte [trackbuf
+bootp.hardlen
]
570 xor cx,cx ; Bad hardware address length
573 mov al,[trackbuf
+bootp.hardware
]
575 mov si,trackbuf
+bootp.macaddr
579 ; Enable this if we really need to zero-pad this field...
580 ; mov cx,MAC+MAC_MAX+1
586 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
587 ; packet (query info 3).
590 call pxe_get_cached_info
591 call parse_dhcp
; Parse DHCP packet
594 ; Generate the bootif string, and the hardware-based config string.
599 mov cx,bootif_str_len
602 movzx cx,byte [MACLen
]
607 mov cl,1 ; CH == 0 already
613 mov [di-1],cl ; Null-terminate and strip final dash
615 ; Generate ip= option
625 call gendotquad
; This takes network byte order input
627 xchg ah,al ; Convert to host byte order
628 ror eax,16 ; (BSWAP doesn't work on 386)
645 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
646 ; if we didn't get the magic enable, do not recognize any other options.
649 test byte [DHCPMagic
], 1 ; If we didn't get the magic enable...
651 mov byte [DHCPMagic
], 0 ; If not, kill all other options
656 ; Initialize UDP stack
660 mov [pxe_udp_open_pkt.sip
],eax
661 mov di,pxe_udp_open_pkt
662 mov bx,PXENV_UDP_OPEN
665 cmp word [pxe_udp_open_pkt.status
], byte 0
667 .
failed: mov si,err_udpinit
673 ; Common initialization code
675 %include "cpuinit.inc"
678 ; Now we're all set to start with our *real* business. First load the
679 ; configuration file (if any) and parse it.
681 ; In previous versions I avoided using 32-bit registers because of a
682 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
683 ; random. I figure, though, that if there are any of those still left
684 ; they probably won't be trying to install Linux on them...
686 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
687 ; to take'm out. In fact, we may want to put them back if we're going
688 ; to boot ELKS at some point.
692 ; Store standard filename prefix
694 prefix: test byte [DHCPMagic
], 04h ; Did we get a path prefix option
703 lea si,[di-2] ; Skip final null!
706 cmp al,'.' ; Count . or - as alphanum
718 .
alnum: loop .find_alnum
720 .
notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
723 mov si,tftpprefix_msg
730 ; Load configuration file
735 ; Begin looking for configuration file
738 test byte [DHCPMagic
], 02h
741 ; We got a DHCP option, try it first
751 ; Have to guess config file name...
753 ; Try loading by UUID.
754 cmp byte [HaveUUID
],0
769 mov [di-1],cl ; Remove last dash and zero-terminate
775 ; Try loading by MAC address
783 ; Nope, try hexadecimal IP prefixes...
787 call uchexbytes
; Convert to hex string
789 mov cx,8 ; Up to 8 attempts
791 mov byte [di],0 ; Zero-terminate string
794 dec di ; Drop one character
797 ; Final attempt: "default" string
798 mov si,default_str
; "default" string
826 ; Linux kernel loading code is common. However, we need to define
827 ; a couple of helper macros...
830 ; Handle "ipappend" option
831 %define HAVE_SPECIAL_APPEND
832 %macro SPECIAL_APPEND
0
833 test byte [IPAppend
],01h ; ip=
841 test byte [IPAppend
],02h
845 mov byte [es:di-1],' ' ; Replace null with space
850 %define HAVE_UNLOAD_PREP
856 ; Now we have the config file open. Parse the config file and
857 ; run the user interface.
862 ; Boot to the local disk by returning the appropriate PXE magic.
863 ; AX contains the appropriate return code.
868 mov [LocalBootType
],ax
872 ; Restore the environment we were called with
879 mov ax,[cs:LocalBootType
]
884 ; kaboom: write a message and bail out. Wait for quite a while,
885 ; or a user keypress, then do a hard reboot.
888 RESET_STACK_AND_SEGS
AX
889 .
patch: mov si,bailmsg
890 call writestr
; Returns with AL = 0
891 .
drain: call pollchar
898 and al,09h ; Magic+Timeout
906 .
wait2: mov dx,[BIOS_timer
]
907 .
wait3: call pollchar
918 mov word [BIOS_magic
],0 ; Cold reboot
919 jmp 0F000h:0FFF0h
; Reset vector address
922 ; memory_scan_for_pxe_struct:
924 ; If none of the standard methods find the !PXE structure, look for it
925 ; by scanning memory.
928 ; CF = 0, ES:BX -> !PXE structure
929 ; Otherwise CF = 1, all registers saved
931 memory_scan_for_pxe_struct:
938 mov ax,[BIOS_fbm
] ; Starting segment
939 shl ax,(10-4) ; Kilobytes -> paragraphs
940 ; mov ax,01000h ; Start to look here
941 dec ax ; To skip inc ax
944 cmp ax,0A000h
; End of memory
953 movzx cx,byte [es:4] ; Length of structure
954 cmp cl,08h ; Minimum length
963 jnz .mismatch
; Checksum must == 0
966 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
974 .
not_found: mov si,notfound_msg
982 ; memory_scan_for_pxenv_struct:
984 ; If none of the standard methods find the PXENV+ structure, look for it
985 ; by scanning memory.
988 ; CF = 0, ES:BX -> PXENV+ structure
989 ; Otherwise CF = 1, all registers saved
991 memory_scan_for_pxenv_struct:
993 mov si,trymempxenv_msg
995 ; mov ax,[BIOS_fbm] ; Starting segment
996 ; shl ax,(10-4) ; Kilobytes -> paragraphs
997 mov ax,01000h ; Start to look here
998 dec ax ; To skip inc ax
1001 cmp ax,0A000h
; End of memory
1010 movzx cx,byte [es:8] ; Length of structure
1011 cmp cl,26h ; Minimum length
1019 jnz .mismatch
; Checksum must == 0
1021 mov [bp+8],bx ; Save BX into stack frame
1027 .
not_found: mov si,notfound_msg
1035 ; Deallocates a file structure (pointer in SI)
1038 ; XXX: We should check to see if this file is still open on the server
1039 ; side and send a courtesy ERROR packet to the server.
1044 mov word [si],0 ; Not in use
1050 ; Open a TFTP connection to the server
1053 ; DS:DI = mangled filename
1056 ; SI = socket pointer
1057 ; DX:AX = file length in bytes
1072 call allocate_socket
1075 mov ax,PKT_RETRY
; Retry counter
1076 mov word [PktTimeout
],PKT_TIMEOUT
; Initial timeout
1078 .
sendreq: push ax ; [bp-2] - Retry counter
1079 push si ; [bp-4] - File name
1082 mov [pxe_udp_write_pkt.buffer
],di
1084 mov ax,TFTP_RRQ
; TFTP opcode
1087 lodsd ; EAX <- server override (if any)
1089 jnz .noprefix
; No prefix, and we have the server
1091 push si ; Add common prefix
1097 mov eax,[ServerIP
] ; Get default server
1100 call strcpy
; Filename
1102 mov [bx+tftp_remoteip
],eax
1104 push bx ; [bp-6] - TFTP block
1106 push bx ; [bp-8] - TID (local port no)
1108 mov [pxe_udp_write_pkt.status
],byte 0
1109 mov [pxe_udp_write_pkt.sip
],eax
1110 ; Now figure out the gateway
1116 mov [pxe_udp_write_pkt.gip
],eax
1117 mov [pxe_udp_write_pkt.lport
],bx
1119 mov [pxe_udp_write_pkt.rport
],ax
1121 mov cx,tftp_tail_len
1123 sub di,packet_buf
; Get packet size
1124 mov [pxe_udp_write_pkt.buffersize
],di
1126 mov di,pxe_udp_write_pkt
1127 mov bx,PXENV_UDP_WRITE
1130 cmp word [pxe_udp_write_pkt.status
],byte 0
1134 ; Danger, Will Robinson! We need to support timeout
1135 ; and retry lest we just lost a packet...
1138 ; Packet transmitted OK, now we need to receive
1139 .
getpacket: push word [PktTimeout
] ; [bp-10]
1140 push word [BIOS_timer
] ; [bp-12]
1142 .
pkt_loop: mov bx,[bp-8] ; TID
1144 mov word [pxe_udp_read_pkt.status
],0
1145 mov [pxe_udp_read_pkt.buffer
],di
1146 mov [pxe_udp_read_pkt.buffer
+2],ds
1147 mov word [pxe_udp_read_pkt.buffersize
],packet_buf_size
1149 mov [pxe_udp_read_pkt.dip
],eax
1150 mov [pxe_udp_read_pkt.lport
],bx
1151 mov di,pxe_udp_read_pkt
1152 mov bx,PXENV_UDP_READ
1155 jz .got_packet
; Wait for packet
1161 dec word [bp-10] ; Timeout
1163 pop ax ; Adjust stack
1165 shl word [PktTimeout
],1 ; Exponential backoff
1169 mov si,[bp-6] ; TFTP pointer
1172 mov eax,[si+tftp_remoteip
]
1173 cmp [pxe_udp_read_pkt.sip
],eax ; This is technically not to the TFTP spec?
1176 ; Got packet - reset timeout
1177 mov word [PktTimeout
],PKT_TIMEOUT
1179 pop ax ; Adjust stack
1182 mov ax,[pxe_udp_read_pkt.rport
]
1183 mov [si+tftp_remoteport
],ax
1185 ; filesize <- -1 == unknown
1186 mov dword [si+tftp_filesize
], -1
1187 ; Default blksize unless blksize option negotiated
1188 mov word [si+tftp_blksize
], TFTP_BLOCKSIZE
1190 mov cx,[pxe_udp_read_pkt.buffersize
]
1191 sub cx,2 ; CX <- bytes after opcode
1192 jb .failure
; Garbled reply
1198 je .bailnow
; ERROR reply: don't try again
1203 ; Now we need to parse the OACK packet to get the transfer
1204 ; size. SI -> first byte of options; CX -> byte count
1206 jcxz .no_tsize
; No options acked
1210 .
opt_name_loop: lodsb
1213 or al,20h ; Convert to lowercase
1216 ; We ran out, and no final null
1218 .
got_opt_name: ; si -> option value
1219 dec cx ; bytes left in pkt
1220 jz .err_reply
; Option w/o value
1222 ; Parse option pointed to by bx; guaranteed to be
1226 mov si,bx ; -> option name
1227 mov bx,tftp_opt_table
1232 mov di,[bx] ; Option pointer
1233 mov cx,[bx+2] ; Option len
1237 je .get_value
; OK, known option
1243 jmp .err_reply
; Non-negotiated option returned
1245 .
get_value: pop si ; si -> option value
1246 pop cx ; cx -> bytes left in pkt
1247 mov bx,[bx+4] ; Pointer to data target
1248 add bx,[bp-6] ; TFTP socket pointer
1256 ja .err_reply
; Not a decimal digit
1261 ; Ran out before final null, accept anyway
1266 jnz .get_opt_name
; Not end of packet
1273 pop si ; We want the packet ptr in SI
1275 mov eax,[si+tftp_filesize
]
1279 shr edx,16 ; DX:AX == EAX
1281 and eax,eax ; Set ZF depending on file size
1283 pop bp ; Junk (retry counter)
1284 jz .error_si
; ZF = 1 need to free the socket
1293 .
err_reply: ; Option negotiation error. Send ERROR reply.
1294 ; ServerIP and gateway are already programmed in
1296 mov ax,[si+tftp_remoteport
]
1297 mov word [pxe_udp_write_pkt.rport
],ax
1298 mov word [pxe_udp_write_pkt.buffer
],tftp_opt_err
1299 mov word [pxe_udp_write_pkt.buffersize
],tftp_opt_err_len
1300 mov di,pxe_udp_write_pkt
1301 mov bx,PXENV_UDP_WRITE
1304 ; Write an error message and explode
1309 .
bailnow: mov word [bp-2],1 ; Immediate error - no retry
1311 .
failure: pop bx ; Junk
1315 dec ax ; Retry counter
1316 jnz .sendreq
; Try again
1318 .
error: mov si,bx ; Socket pointer
1319 .
error_si: ; Socket pointer already in SI
1320 call free_socket
; ZF <- 1, SI <- 0
1324 ; allocate_socket: Allocate a local UDP port structure
1328 ; BX = socket pointer
1336 .
check: cmp word [bx], byte 0
1338 add bx,open_file_t_size
1343 ; Allocate a socket number. Socket numbers are made
1344 ; guaranteed unique by including the socket slot number
1345 ; (inverted, because we use the loop counter cx); add a
1346 ; counter value to keep the numbers from being likely to
1347 ; get immediately reused.
1349 ; The NextSocket variable also contains the top two bits
1350 ; set. This generates a value in the range 49152 to
1357 and ax,((1 << (13-MAX_OPEN_LG2
))-1) |
0xC000
1359 shl cx,13-MAX_OPEN_LG2
1361 xchg ch,cl ; Convert to network byte order
1362 mov [bx],cx ; Socket in use
1368 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1376 mov cx,tftp_pktbuf
>> 1 ; tftp_pktbuf is not cleared
1385 ; Read a dot-quad pathname in DS:SI and output an IP
1386 ; address in EAX, with SI pointing to the first
1387 ; nonmatching character.
1389 ; Return CF=1 on error.
1403 aad ; AL += 10 * AH; AH = 0;
1418 loop .realerror
; If CX := 1 then we're done
1424 dec si ; CF unchanged!
1428 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1429 ; to by ES:DI; ends on encountering any whitespace.
1432 ; This verifies that a filename is < FILENAME_MAX characters
1433 ; and doesn't contain whitespace, and zero-pads the output buffer,
1434 ; so "repe cmpsb" can do a compare.
1436 ; The first four bytes of the manged name is the IP address of
1437 ; the download host.
1444 je .noip
; Null filename?!?!
1445 cmp word [si],'::' ; Leading ::?
1455 ; We have a :: prefix of some sort, it could be either
1456 ; a DNS name or a dot-quad IP address. Try the dot-quad
1480 pop cx ; Adjust stack
1481 inc si ; Skip double colon
1485 stosd ; Save IP address prefix
1486 mov cx,FILENAME_MAX
-5
1490 cmp al,' ' ; If control or space, end
1495 inc cx ; At least one null byte
1496 xor ax,ax ; Zero-fill name
1497 rep stosb ; Doesn't do anything if CX=0
1502 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1503 ; filename to the conventional representation. This is needed
1504 ; for the BOOT_IMAGE= parameter for the kernel.
1505 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1506 ; known to be shorter.
1508 ; DS:SI -> input mangled file name
1509 ; ES:DI -> output buffer
1511 ; On return, DI points to the first byte after the output name,
1512 ; which is set to a null byte.
1524 dec di ; Point to final null byte
1531 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1532 ; calling convention. This is a separate local routine so
1533 ; we can hook special things from it if necessary. In particular,
1534 ; some PXE stacks seem to not like being invoked from anything but
1535 ; the initial stack, so humour it.
1539 %if USE_PXE_PROVIDED_STACK
== 0
1540 mov [cs:PXEStack
],sp
1541 mov [cs:PXEStack
+2],ss
1542 lss sp,[cs:InitStack
]
1544 .
jump: call 0:pxe_thunk
; Default to calling the thunk
1545 %if USE_PXE_PROVIDED_STACK
== 0
1546 lss sp,[cs:PXEStack
]
1548 cld ; Make sure DF <- 0
1551 ; Must be after function def due to NASM bug
1552 PXENVEntry
equ pxenv.jump
+1
1557 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1558 ; calling convention (using the stack.)
1560 ; This is called as a far routine so that we can just stick it into
1561 ; the PXENVEntry variable.
1569 cmc ; Set CF unless ax == 0
1572 ; Must be after function def due to NASM bug
1573 PXEEntry
equ pxe_thunk.jump
+1
1576 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1578 ; In this case, get multiple blocks from a specific TCP connection.
1582 ; SI -> TFTP socket pointer
1583 ; CX -> 512-byte block count; 0FFFFh = until end of file
1585 ; SI -> TFTP socket pointer (or 0 on EOF)
1596 shl ecx,TFTP_BLOCKSIZE_LG2
; Convert to bytes
1597 jz .hit_eof
; Nothing to do?
1602 movzx eax,word [bx+tftp_bytesleft
]
1606 jcxz .need_packet
; No bytes available?
1609 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1610 mov si,[bx+tftp_dataptr
]
1611 sub [bx+tftp_bytesleft
],cx
1612 fs rep movsb ; Copy from packet buffer
1613 mov [bx+tftp_dataptr
],si
1624 ; Is there anything left of this?
1625 mov eax,[si+tftp_filesize
]
1626 sub eax,[si+tftp_filepos
]
1627 jnz .bytes_left
; CF <- 0
1629 cmp [si+tftp_bytesleft
],ax
1630 jnz .bytes_left
; CF <- 0
1632 ; The socket is closed and the buffer drained
1633 ; Close socket structure and re-init for next user
1640 ; No data in buffer, check to see if we can get a packet...
1644 mov eax,[bx+tftp_filesize
]
1645 cmp eax,[bx+tftp_filepos
]
1646 je .hit_eof
; Already EOF'd; socket already closed
1658 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1665 ; Start by ACKing the previous packet; this should cause the
1666 ; next packet to be sent.
1668 mov word [PktTimeout
],PKT_TIMEOUT
1670 .
send_ack: push cx ; <D> Retry count
1672 mov ax,[si+tftp_lastpkt
]
1673 call ack_packet
; Send ACK
1675 ; We used to test the error code here, but sometimes
1676 ; PXE would return negative status even though we really
1677 ; did send the ACK. Now, just treat a failed send as
1678 ; a normally lost packet, and let it time out in due
1681 .
send_ok: ; Now wait for packet.
1682 mov dx,[BIOS_timer
] ; Get current time
1685 .
wait_data: push cx ; <E> Timeout
1686 push dx ; <F> Old time
1688 mov bx,[si+tftp_pktbuf
]
1689 mov [pxe_udp_read_pkt.buffer
],bx
1690 mov [pxe_udp_read_pkt.buffer
+2],fs
1691 mov [pxe_udp_read_pkt.buffersize
],word PKTBUF_SIZE
1692 mov eax,[si+tftp_remoteip
]
1693 mov [pxe_udp_read_pkt.sip
],eax
1695 mov [pxe_udp_read_pkt.dip
],eax
1696 mov ax,[si+tftp_remoteport
]
1697 mov [pxe_udp_read_pkt.rport
],ax
1698 mov ax,[si+tftp_localport
]
1699 mov [pxe_udp_read_pkt.lport
],ax
1700 mov di,pxe_udp_read_pkt
1701 mov bx,PXENV_UDP_READ
1708 ; No packet, or receive failure
1710 pop ax ; <F> Old time
1711 pop cx ; <E> Timeout
1712 cmp ax,dx ; Same time -> don't advance timeout
1713 je .wait_data
; Same clock tick
1714 loop .wait_data
; Decrease timeout
1716 pop cx ; <D> Didn't get any, send another ACK
1717 shl word [PktTimeout
],1 ; Exponential backoff
1719 jmp kaboom
; Forget it...
1721 .
recv_ok: pop dx ; <F>
1724 cmp word [pxe_udp_read_pkt.buffersize
],byte 4
1725 jb .wait_data
; Bad size for a DATA packet
1727 mov bx,[si+tftp_pktbuf
]
1728 cmp word [fs:bx],TFTP_DATA
; Not a data packet?
1729 jne .wait_data
; Then wait for something else
1731 mov ax,[si+tftp_lastpkt
]
1732 xchg ah,al ; Host byte order
1733 inc ax ; Which packet are we waiting for?
1734 xchg ah,al ; Network byte order
1738 ; Wrong packet, ACK the packet and then try again
1739 ; This is presumably because the ACK got lost,
1740 ; so the server just resent the previous packet
1743 jmp .send_ok
; Reset timeout
1745 .
right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1747 pop cx ; <D> Don't need the retry count anymore
1749 mov [si+tftp_lastpkt
],ax ; Update last packet number
1751 movzx ecx,word [pxe_udp_read_pkt.buffersize
]
1752 sub cx,byte 4 ; Skip TFTP header
1754 ; If this is a zero-length block, don't mess with the pointers,
1755 ; since we may have just set up the previous block that way
1758 ; Set pointer to data block
1759 lea ax,[bx+4] ; Data past TFTP header
1760 mov [si+tftp_dataptr
],ax
1762 add [si+tftp_filepos
],ecx
1763 mov [si+tftp_bytesleft
],cx
1765 cmp cx,[si+tftp_blksize
] ; Is it a full block?
1766 jb .last_block
; If so, it's not EOF
1768 ; If we had the exact right number of bytes, always get
1769 ; one more packet to get the (zero-byte) EOF packet and
1771 mov eax,[si+tftp_filepos
]
1772 cmp [si+tftp_filesize
],eax
1778 .
last_block: ; Last block - ACK packet immediately
1782 ; Make sure we know we are at end of file
1783 mov eax,[si+tftp_filepos
]
1784 mov [si+tftp_filesize
],eax
1791 ; Send ACK packet. This is a common operation and so is worth canning.
1795 ; AX = Packet # to ack (network byte order)
1798 ; All registers preserved
1800 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1804 mov [ack_packet_buf
+2],ax ; Packet number to ack
1806 mov [pxe_udp_write_pkt.lport
],ax
1807 mov ax,[si+tftp_remoteport
]
1808 mov [pxe_udp_write_pkt.rport
],ax
1809 mov eax,[si+tftp_remoteip
]
1810 mov [pxe_udp_write_pkt.sip
],eax
1816 mov [pxe_udp_write_pkt.gip
],eax
1817 mov [pxe_udp_write_pkt.buffer
],word ack_packet_buf
1818 mov [pxe_udp_write_pkt.buffersize
], word 4
1819 mov di,pxe_udp_write_pkt
1820 mov bx,PXENV_UDP_WRITE
1822 cmp ax,byte 0 ; ZF = 1 if write OK
1829 ; This function unloads the PXE and UNDI stacks and unclaims
1833 test byte [KeepPXE
],01h ; Should we keep PXE around?
1843 mov si,new_api_unload
1844 cmp byte [APIVer
+1],2 ; Major API version >= 2?
1846 mov si,old_api_unload
1849 .
call_loop: xor ax,ax
1854 mov di,pxe_unload_stack_pkt
1857 mov cx,pxe_unload_stack_pkt_len
>> 1
1862 mov ax,word [pxe_unload_stack_pkt.status
]
1863 cmp ax,PXENV_STATUS_SUCCESS
1870 mov dx,[RealBaseMem
]
1871 cmp dx,[BIOS_fbm
] ; Sanity check
1875 ; Check that PXE actually unhooked the INT 1Ah chain
1876 movzx eax,word [4*0x1a]
1877 movzx ecx,word [4*0x1a+2]
1881 cmp ax,dx ; Not in range
1895 mov si,cant_free_msg
1911 ; We want to keep PXE around, but still we should reset
1912 ; it to the standard bootup configuration
1917 mov bx,PXENV_UDP_CLOSE
1918 mov di,pxe_udp_close_pkt
1926 ; Take an IP address (in network byte order) in EAX and
1927 ; output a dotted quad string to ES:DI.
1928 ; DI points to terminal null at end of string on exit.
1937 jb .lt10
; If so, skip first 2 digits
1940 jb .lt100
; If so, skip first digit
1943 ; Now AH = 100-digit; AL = remainder
1950 ; Now AH = 10-digit; AL = remainder
1961 ror eax,8 ; Move next char into LSB
1969 ; uchexbytes/lchexbytes
1971 ; Take a number of bytes in memory and convert to upper/lower-case
1975 ; DS:SI = input bytes
1976 ; ES:DI = output buffer
1977 ; CX = number of bytes
1979 ; DS:SI = first byte after
1980 ; ES:DI = first byte after
2012 ; pxe_get_cached_info
2014 ; Get a DHCP packet from the PXE stack into the trackbuf.
2021 ; Assumes CS == DS == ES.
2023 pxe_get_cached_info:
2025 mov di,pxe_bootp_query_pkt
2034 stosw ; Buffer offset
2036 stosw ; Buffer segment
2038 pop di ; DI -> parameter set
2039 mov bx,PXENV_GET_CACHED_INFO
2046 mov cx,[pxe_bootp_query_pkt.buffersize
]
2050 mov si,err_pxefailed
2056 ; Parse a DHCP packet. This includes dealing with "overloaded"
2057 ; option fields (see RFC 2132, section 9.3)
2059 ; This should fill in the following global variables, if the
2060 ; information is present:
2062 ; MyIP - client IP address
2063 ; ServerIP - boot server IP address
2064 ; Netmask - network mask
2065 ; Gateway - default gateway router IP
2066 ; BootFile - boot file name
2067 ; DNSServers - DNS server IPs
2068 ; LocalDomain - Local domain name
2069 ; MACLen, MAC - Client identifier, if MACLen == 0
2071 ; This assumes the DHCP packet is in "trackbuf" and the length
2072 ; of the packet in in CX on entry.
2076 mov byte [OverLoad
],0 ; Assume no overload
2077 mov eax, [trackbuf
+bootp.yip
]
2080 cmp al,224 ; Class D or higher -> bad
2084 mov eax, [trackbuf
+bootp.sip
]
2087 cmp al,224 ; Class D or higher -> bad
2091 sub cx, bootp.options
2093 mov si, trackbuf
+bootp.option_magic
2095 cmp eax, BOOTP_OPTION_MAGIC
2097 call parse_dhcp_options
2099 mov si, trackbuf
+bootp.bootfile
2100 test byte [OverLoad
],1
2103 call parse_dhcp_options
2104 jmp short .parsed_file
2107 jz .parsed_file
; No bootfile name
2112 stosb ; Null-terminate
2114 mov si, trackbuf
+bootp.sname
2115 test byte [OverLoad
],2
2118 call parse_dhcp_options
2123 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2124 ; size is CX -- some DHCP servers leave option fields unterminated
2125 ; in violation of the spec.
2127 ; For parse_some_dhcp_options, DH contains the minimum value for
2128 ; the option to recognize -- this is used to restrict parsing to
2129 ; PXELINUX-specific options only.
2134 parse_some_dhcp_options:
2141 jz .done
; Last byte; must be PAD, END or malformed
2142 cmp al, 0 ; PAD option
2144 cmp al,255 ; END option
2147 ; Anything else will have a length field
2148 mov dl,al ; DL <- option number
2150 lodsb ; AX <- option length
2152 sub cx,ax ; Decrement bytes left counter
2153 jb .done
; Malformed option: length > field size
2155 cmp dl,dh ; Is the option value valid?
2158 mov bx,dhcp_option_list
2160 cmp bx,dhcp_option_list_end
2172 ; Unknown option. Skip to the next one.
2192 ; Parse individual DHCP options. SI points to the option data and
2193 ; AX to the option length. DL contains the option number.
2194 ; All registers are saved around the routine.
2209 cmp cl,DNS_MAX_SERVERS
2211 mov cl,DNS_MAX_SERVERS
2215 mov [LastDNSServer
],di
2218 dopt
16, local_domain
2222 xchg [bx],al ; Zero-terminate option
2224 call dns_mangle
; Convert to DNS label set
2225 mov [bx],al ; Restore ending byte
2228 dopt
43, vendor_encaps
2229 mov dh,208 ; Only recognize PXELINUX options
2230 mov cx,ax ; Length of option = max bytes to parse
2231 call parse_some_dhcp_options
; Parse recursive structure
2234 dopt
52, option_overload
2241 cmp dword [ServerIP
],0
2242 jne .skip
; Already have a next server IP
2243 cmp al,224 ; Class D or higher
2248 dopt
61, client_identifier
2249 cmp ax,MAC_MAX
; Too long?
2251 cmp ax,2 ; Too short?
2253 cmp [MACLen
],ah ; Only do this if MACLen == 0
2256 lodsb ; Client identifier type
2259 jne .skip
; Client identifier is not a MAC
2266 dopt
67, bootfile_name
2270 dopt
97, uuid_client_identifier
2271 cmp ax,17 ; type byte + 16 bytes UUID
2273 mov dl,[si] ; Must have type 0 == UUID
2274 or dl,[HaveUUID
] ; Capture only the first instance
2276 mov byte [HaveUUID
],1 ; Got UUID
2281 dopt
208, pxelinux_magic
2282 cmp al,4 ; Must have length == 4
2284 cmp dword [si], htonl
(0xF100747E) ; Magic number
2286 or byte [DHCPMagic
],1 ; Found magic #
2289 dopt
209, pxelinux_configfile
2291 or byte [DHCPMagic
],2 ; Got config file
2294 dopt
210, pxelinux_pathprefix
2296 or byte [DHCPMagic
],4 ; Got path prefix
2299 dopt
211, pxelinux_reboottime
2303 xchg bl,bh ; Convert to host byte order
2306 mov [RebootTime
],ebx
2307 or byte [DHCPMagic
],8 ; Got RebootTime
2310 ; Common code for copying an option verbatim
2311 ; Copies the option into ES:DI and null-terminates it.
2312 ; Returns with AX=0 and SI past the option.
2314 xchg cx,ax ; CX <- option length
2316 xchg cx,ax ; AX <- 0
2317 stosb ; Null-terminate
2321 dhcp_option_list_end:
2326 uuid_dashes
db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2332 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2333 ; option into IPOption based on a DHCP packet in trackbuf.
2334 ; Assumes CS == DS == ES.
2355 call gendotquad
; Zero-terminates its output
2357 mov [IPOptionLen
],di
2362 ; Call the receive loop while idle. This is done mostly so we can respond to
2363 ; ARP messages, but perhaps in the future this can be used to do network
2366 ; hpa sez: people using automatic control on the serial port get very
2367 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2368 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2369 ; passed since the last poll, and reset this when a character is
2370 ; received (RESET_IDLE).
2376 mov ax,[cs:BIOS_timer
]
2377 mov [cs:IdleTimer
],ax
2383 mov ax,[cs:BIOS_timer
]
2384 sub ax,[cs:IdleTimer
]
2396 mov [pxe_udp_read_pkt.status
],al ; 0
2397 mov [pxe_udp_read_pkt.buffer
],di
2398 mov [pxe_udp_read_pkt.buffer
+2],ds
2399 mov word [pxe_udp_read_pkt.buffersize
],packet_buf_size
2401 mov [pxe_udp_read_pkt.dip
],eax
2402 mov word [pxe_udp_read_pkt.lport
],htons
(9) ; discard port
2403 mov di,pxe_udp_read_pkt
2404 mov bx,PXENV_UDP_READ
2415 ; -----------------------------------------------------------------------------
2417 ; -----------------------------------------------------------------------------
2419 %include "getc.inc" ; getc et al
2420 %include "conio.inc" ; Console I/O
2421 %include "writestr.inc" ; String output
2422 writestr
equ cwritestr
2423 %include "writehex.inc" ; Hexadecimal output
2424 %include "configinit.inc" ; Initialize configuration
2425 %include "parseconfig.inc" ; High-level config file handling
2426 %include "parsecmd.inc" ; Low-level config file handling
2427 %include "bcopy32.inc" ; 32-bit bcopy
2428 %include "loadhigh.inc" ; Load a file into high memory
2429 %include "font.inc" ; VGA font stuff
2430 %include "graphics.inc" ; VGA graphics
2431 %include "highmem.inc" ; High memory sizing
2432 %include "strcpy.inc" ; strcpy()
2433 %include "rawcon.inc" ; Console I/O w/o using the console functions
2434 %include "dnsresolv.inc" ; DNS resolver
2436 ; -----------------------------------------------------------------------------
2437 ; Begin data section
2438 ; -----------------------------------------------------------------------------
2442 copyright_str
db ' Copyright (C) 1994-', year
, ' H. Peter Anvin'
2444 err_bootfailed
db CR
, LF
, 'Boot failed: press a key to retry, or wait for reset...', CR
, LF
, 0
2445 bailmsg
equ err_bootfailed
2446 err_nopxe
db "No !PXE or PXENV+ API found; we're dead...", CR
, LF
, 0
2447 err_pxefailed
db 'PXE API call failed, error ', 0
2448 err_udpinit
db 'Failed to initialize UDP stack', CR
, LF
, 0
2449 err_noconfig
db 'Unable to locate configuration file', CR
, LF
, 0
2450 err_oldtftp
db 'TFTP server does not support the tsize option', CR
, LF
, 0
2451 found_pxenv
db 'Found PXENV+ structure', CR
, LF
, 0
2452 using_pxenv_msg
db 'Old PXE API detected, using PXENV+ structure', CR
, LF
, 0
2453 apiver_str
db 'PXE API version is ',0
2454 pxeentry_msg
db 'PXE entry point found (we hope) at ', 0
2455 pxenventry_msg
db 'PXENV entry point found (we hope) at ', 0
2456 trymempxe_msg
db 'Scanning memory for !PXE structure... ', 0
2457 trymempxenv_msg
db 'Scanning memory for PXENV+ structure... ', 0
2458 undi_data_msg
db 'UNDI data segment at: ',0
2459 undi_data_len_msg
db 'UNDI data segment size: ',0
2460 undi_code_msg
db 'UNDI code segment at: ',0
2461 undi_code_len_msg
db 'UNDI code segment size: ',0
2462 cant_free_msg
db 'Failed to free base memory, error ', 0
2463 notfound_msg
db 'not found', CR
, LF
, 0
2464 myipaddr_msg
db 'My IP address seems to be ',0
2465 tftpprefix_msg
db 'TFTP prefix: ', 0
2466 localboot_msg
db 'Booting from local disk...', CR
, LF
, 0
2467 trying_msg
db 'Trying to load: ', 0
2468 fourbs_msg
db BS
, BS
, BS
, BS
, 0
2469 default_str
db 'default', 0
2470 syslinux_banner
db CR
, LF
, 'PXELINUX ', version_str
, ' ', date
, ' ', 0
2471 cfgprefix
db 'pxelinux.cfg/' ; No final null!
2472 cfgprefix_len
equ ($
-cfgprefix
)
2475 ; Command line options we'd like to take a look at
2477 ; mem= and vga= are handled as normal 32-bit integer values
2478 initrd_cmd
db 'initrd='
2479 initrd_cmd_len
equ $
-initrd_cmd
2481 ; This one we make ourselves
2482 bootif_str
db 'BOOTIF='
2483 bootif_str_len
equ $
-bootif_str
2485 ; Config file keyword table
2487 %include "keywords.inc"
2490 ; Extensions to search for (in *forward* order).
2491 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2494 exten_table: db '.cbt' ; COMBOOT (specific)
2495 db '.0', 0, 0 ; PXE bootstrap program
2496 db '.com' ; COMBOOT (same as DOS)
2499 dd 0, 0 ; Need 8 null bytes here
2502 ; PXE unload sequences
2506 db PXENV_UNDI_SHUTDOWN
2507 db PXENV_UNLOAD_STACK
2512 db PXENV_UNDI_SHUTDOWN
2513 db PXENV_UNLOAD_STACK
2514 db PXENV_UNDI_CLEANUP
2518 ; PXE query packets partially filled in
2521 pxe_bootp_query_pkt:
2522 .
status: resw
1 ; Status
2523 .
packettype: resw
1 ; Boot server packet type
2524 .
buffersize: resw
1 ; Packet size
2525 .
buffer: resw
2 ; seg:off of buffer
2526 .
bufferlimit: resw
1 ; Unused
2530 .
status: dw 0 ; Status
2531 .
sip: dd 0 ; Source (our) IP
2534 .
status: dw 0 ; Status
2537 .
status: dw 0 ; Status
2538 .
sip: dd 0 ; Server IP
2539 .
gip: dd 0 ; Gateway IP
2540 .
lport: dw 0 ; Local port
2541 .
rport: dw 0 ; Remote port
2542 .
buffersize: dw 0 ; Size of packet
2543 .
buffer: dw 0, 0 ; seg:off of buffer
2546 .
status: dw 0 ; Status
2547 .
sip: dd 0 ; Source IP
2548 .
dip: dd 0 ; Destination (our) IP
2549 .
rport: dw 0 ; Remote port
2550 .
lport: dw 0 ; Local port
2551 .
buffersize: dw 0 ; Max packet size
2552 .
buffer: dw 0, 0 ; seg:off of buffer
2555 ; Misc initialized (data) variables
2558 BaseStack
dd StackBuf
; ESP of base stack
2559 dw 0 ; SS of base stack
2560 NextSocket
dw 49152 ; Counter for allocating socket numbers
2561 KeepPXE
db 0 ; Should PXE be kept around?
2566 tftp_tail
db 'octet', 0 ; Octet mode
2567 tsize_str
db 'tsize' ,0 ; Request size
2568 tsize_len
equ ($
-tsize_str
)
2570 blksize_str
db 'blksize', 0 ; Request large blocks
2571 blksize_len
equ ($
-blksize_str
)
2572 asciidec TFTP_LARGEBLK
2574 tftp_tail_len
equ ($
-tftp_tail
)
2578 ; Options negotiation parsing table (string pointer, string len, offset
2579 ; into socket structure)
2582 dw tsize_str
, tsize_len
, tftp_filesize
2583 dw blksize_str
, blksize_len
, tftp_blksize
2584 tftp_opts
equ ($
-tftp_opt_table
)/6
2587 ; Error packet to return on options negotiation error
2589 tftp_opt_err
dw TFTP_ERROR
; ERROR packet
2590 dw TFTP_EOPTNEG
; ERROR 8: bad options
2591 db 'tsize option required', 0 ; Error message
2592 tftp_opt_err_len
equ ($
-tftp_opt_err
)
2595 ack_packet_buf: dw TFTP_ACK
, 0 ; TFTP ACK packet
2598 ; IP information (initialized to "unknown" values)
2599 MyIP
dd 0 ; My IP address
2600 ServerIP
dd 0 ; IP address of boot server
2601 Netmask
dd 0 ; Netmask of this subnet
2602 Gateway
dd 0 ; Default router
2603 ServerPort
dw TFTP_PORT
; TFTP server port
2606 ; Variables that are uninitialized in SYSLINUX but initialized here
2609 BufSafe
dw trackbufsize
/TFTP_BLOCKSIZE
; Clusters we can load into trackbuf
2610 BufSafeBytes
dw trackbufsize
; = how many bytes?
2612 %if
( trackbufsize
% TFTP_BLOCKSIZE
) != 0
2613 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE