1 ; -*- fundamental -*- (asm-mode sucks)
3 ; ****************************************************************************
7 ; A program to boot Linux kernels off a TFTP server using the Intel PXE
8 ; network booting API. It is based on the SYSLINUX boot loader for
11 ; Copyright (C) 1994-2004 H. Peter Anvin
13 ; This program is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16 ; Boston MA 02111-1307, USA; either version 2 of the License, or
17 ; (at your option) any later version; incorporated herein by reference.
19 ; ****************************************************************************
26 %include "tracers.inc"
30 ; Some semi-configurable constants... change on your own risk.
33 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
34 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
35 NULLFILE equ 0 ; Zero byte == null file name
36 REBOOT_TIME equ 5*60 ; If failure, time until full reset
37 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
38 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
39 MAX_OPEN equ (1 << MAX_OPEN_LG2)
40 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
41 TFTP_PORT equ htons(69) ; Default TFTP port
42 PKT_RETRY equ 6 ; Packet transmit retry count
43 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
44 ; Desired TFTP block size
45 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
46 ; be a fair number of networks with "substandard" MTUs which break.
47 ; The code assumes TFTP_LARGEBLK <= 2K.
49 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
50 ; Standard TFTP block size
51 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
52 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
53 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
55 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
56 SECTOR_SIZE equ TFTP_BLOCKSIZE
59 ; This is what we need to do when idle
69 ; TFTP operation codes
71 TFTP_RRQ equ htons(1) ; Read request
72 TFTP_WRQ equ htons(2) ; Write request
73 TFTP_DATA equ htons(3) ; Data packet
74 TFTP_ACK equ htons(4) ; ACK packet
75 TFTP_ERROR equ htons(5) ; ERROR packet
76 TFTP_OACK equ htons(6) ; OACK packet
81 TFTP_EUNDEF equ htons(0) ; Unspecified error
82 TFTP_ENOTFOUND equ htons(1) ; File not found
83 TFTP_EACCESS equ htons(2) ; Access violation
84 TFTP_ENOSPACE equ htons(3) ; Disk full
85 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
86 TFTP_EBADID equ htons(5) ; Unknown transfer
87 TFTP_EEXISTS equ htons(6) ; File exists
88 TFTP_ENOUSER equ htons(7) ; No such user
89 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
92 ; The following structure is used for "virtual kernels"; i.e. LILO-style
93 ; option labels. The options we permit here are `kernel' and `append
94 ; Since there is no room in the bottom 64K for all of these, we
95 ; stick them at vk_seg:0000 and copy them down before we need them.
98 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
99 vk_rname: resb FILENAME_MAX ; Real name
100 vk_ipappend: resb 1 ; "IPAPPEND" flag
104 vk_append: resb max_cmd_len+1 ; Command line
106 vk_end: equ $ ; Should be <= vk_size
110 ; Segment assignments in the bottom 640K
111 ; 0000h - main code/data segment (and BIOS segment)
113 real_mode_seg equ 4000h
114 vk_seg equ 3000h ; Virtual kernels
115 xfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mem
116 pktbuf_seg equ 1000h ; Packet buffers segments
117 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
120 ; BOOTP/DHCP packet pattern
124 .opcode resb 1 ; BOOTP/DHCP "opcode"
125 .hardware resb 1 ; ARP hardware type
126 .hardlen resb 1 ; Hardware address length
127 .gatehops resb 1 ; Used by forwarders
128 .ident resd 1 ; Transaction ID
129 .seconds resw 1 ; Seconds elapsed
130 .flags resw 1 ; Broadcast flags
131 .cip resd 1 ; Client IP
132 .yip resd 1 ; "Your" IP
133 .sip resd 1 ; Next server IP
134 .gip resd 1 ; Relay agent IP
135 .macaddr resb 16 ; Client MAC address
136 .sname resb 64 ; Server name (optional)
137 .bootfile resb 128 ; Boot file name
138 .option_magic resd 1 ; Vendor option magic cookie
139 .options resb 1260 ; Vendor options
142 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
145 ; TFTP connection data structure. Each one of these corresponds to a local
146 ; UDP port. The size of this structure must be a power of 2.
147 ; HBO = host byte order; NBO = network byte order
148 ; (*) = written by options negotiation code, must be dword sized
151 tftp_localport resw 1 ; Local port number (0 = not in use)
152 tftp_remoteport resw 1 ; Remote port number
153 tftp_remoteip resd 1 ; Remote IP address
154 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
155 tftp_filesize resd 1 ; Total file size(*)
156 tftp_blksize resd 1 ; Block size for this connection(*)
157 tftp_bytesleft resw 1 ; Unclaimed data bytes
158 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
159 tftp_dataptr resw 1 ; Pointer to available data
160 resw 2 ; Currently unusued
161 ; At end since it should not be zeroed on socked close
162 tftp_pktbuf resw 1 ; Packet buffer offset
165 %if (open_file_t_size & (open_file_t_size-1))
166 %error "open_file_t is not a power of 2"
170 ; ---------------------------------------------------------------------------
172 ; ---------------------------------------------------------------------------
175 ; Memory below this point is reserved for the BIOS and the MBR
178 section .bss start=BSS_START
179 trackbufsize equ 8192
180 trackbuf resb trackbufsize ; Track buffer goes here
181 getcbuf resb trackbufsize
184 ; Warning here: RBFG build 22 randomly overwrites memory location
185 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
186 ; screws up the pointer to its own internal packet buffer and starts
187 ; writing a received ARP packet into low memory.
188 RBFB_brainfuck resb 800h
191 BootFile resb 256 ; Boot file from DHCP packet
192 ConfigName resb 256 ; Configuration file from DHCP option
193 PathPrefix resb 256 ; Path prefix derived from boot file
194 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
195 IPOption resb 80 ; ip= option buffer
197 InitStack resd 1 ; Pointer to reset stack
198 RebootTime resd 1 ; Reboot timeout, if set by option
199 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
200 APIVer resw 1 ; PXE API version found
201 IPOptionLen resw 1 ; Length of IPOption
202 IdleTimer resw 1 ; Time to check for ARP?
203 LocalBootType resw 1 ; Local boot return code
204 PktTimeout resw 1 ; Timeout for current packet
205 RealBaseMem resw 1 ; Amount of DOS memory after freeing
206 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
208 ; The relative position of these fields matter!
209 MACLen resb 1 ; MAC address len
210 MACType resb 1 ; MAC address type
211 MAC resb 16 ; Actual MAC address
212 MACStr resb 3*17 ; MAC address as a string
215 ; PXE packets which don't need static initialization
218 pxe_unload_stack_pkt:
219 .status: resw 1 ; Status
220 .reserved: resw 10 ; Reserved
221 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
223 alignb open_file_t_size
224 Files resb MAX_OPEN*open_file_t_size
227 ; BOOTP/DHCP packet buffer
230 packet_buf resb 2048 ; Transfer packet
231 packet_buf_size equ $-packet_buf
234 ; Constants for the xfer_buf_seg
236 ; The xfer_buf_seg is also used to store message file buffers. We
237 ; need two trackbuffers (text and graphics), plus a work buffer
238 ; for the graphics decompressor.
240 xbs_textbuf equ 0 ; Also hard-coded, do not change
241 xbs_vgabuf equ trackbufsize
242 xbs_vgatmpbuf equ 2*trackbufsize
246 StackBuf equ $-44 ; Base of stack if we use our own
249 ; Primary entry point.
253 jmp 0:_start1 ; Canonicalize address
255 pushfd ; Paranoia... in case of return to PXE
256 pushad ; ... save as much state as possible
263 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
268 ; That is all pushed onto the PXE stack. Save the pointer
269 ; to it and switch to an internal stack.
273 %if USE_PXE_PROVIDED_STACK
274 ; Apparently some platforms go bonkers if we
275 ; set up our own stack...
283 sti ; Stack set up and ready
287 ; Initialize screen (if we're using one)
289 ; Now set up screen parameters
292 ; Wipe the F-key area
295 mov cx,10*(1 << FILENAME_MAX_LG2)
296 push es ; Save ES -> PXE structure
303 ; Tell the user we got this far
305 mov si,syslinux_banner
312 ; Assume API version 2.1, in case we find the !PXE structure without
313 ; finding the PXENV+ structure. This should really look at the Base
314 ; Code ROM ID structure in have_pxe, but this is adequate for now --
315 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
316 ; about higher versions than that.
318 mov word [APIVer],0201h
321 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
322 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
323 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
324 ; We should make that the second test, and not trash ES:BX...
326 cmp dword [es:bx], '!PXE'
329 ; Uh-oh, not there... try plan B
331 int 1Ah ; May trash regs
336 ; Okay, that gave us the PXENV+ structure, find !PXE
337 ; structure from that (if available)
338 cmp dword [es:bx], 'PXEN'
340 cmp word [es:bx+4], 'V+'
343 ; Nothing there either. Last-ditch: scan memory
344 call memory_scan_for_pxe_struct ; !PXE scan
346 call memory_scan_for_pxenv_struct ; PXENV+ scan
349 no_pxe: mov si,err_nopxe
367 cmp ax,0201h ; API version 2.1 or higher
371 les bx,[es:bx+28h] ; !PXE structure pointer
372 cmp dword [es:bx],'!PXE'
375 ; Nope, !PXE structure missing despite API 2.1+, or at least
376 ; the pointer is missing. Do a last-ditch attempt to find it.
377 call memory_scan_for_pxe_struct
380 ; Otherwise, no dice, use PXENV+ structure
384 old_api: ; Need to use a PXENV+ structure
385 mov si,using_pxenv_msg
388 mov eax,[es:bx+0Ah] ; PXE RM API
391 mov si,undi_data_msg ; ***
396 mov si,undi_data_len_msg
406 mov si,undi_code_len_msg
412 ; Compute base memory size from PXENV+ structure
414 movzx eax,word [es:bx+20h] ; UNDI data seg
415 cmp ax,[es:bx+24h] ; UNDI code seg
425 shr eax,10 ; Convert to kilobytes
428 mov si,pxenventry_msg
430 mov ax,[PXENVEntry+2]
446 mov si,undi_data_msg ; ***
451 mov si,undi_data_len_msg
461 mov si,undi_code_len_msg
467 ; Compute base memory size from !PXE structure
494 pop es ; Restore CS == DS == ES
497 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
498 ; address). This lives in the DHCPACK packet (query info 2).
501 mov di,pxe_bootp_query_pkt_2
502 mov bx,PXENV_GET_CACHED_INFO
505 push word [pxe_bootp_query_pkt_2.status]
510 mov di,pxe_bootp_size_query_pkt
511 mov bx,PXENV_GET_CACHED_INFO
516 mov ax,[pxe_bootp_size_query_pkt.buffersize]
529 jmp kaboom ; We're dead
532 pop cx ; Forget status
533 mov cx,[pxe_bootp_query_pkt_2.buffersize]
534 call parse_dhcp ; Parse DHCP packet
536 ; Save away MAC address (assume this is in query info 2. If this
537 ; turns out to be problematic it might be better getting it from
538 ; the query info 1 packet.)
541 movzx cx,byte [trackbuf+bootp.hardlen]
543 mov al,[trackbuf+bootp.hardware]
545 mov si,trackbuf+bootp.macaddr
572 mov [di-1],byte 0 ; Null-terminate and strip final colon
575 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
576 ; packet (query info 3).
578 mov [pxe_bootp_size_query_pkt.packettype], byte 3
580 mov di,pxe_bootp_query_pkt_3
581 mov bx,PXENV_GET_CACHED_INFO
584 push word [pxe_bootp_query_pkt_3.status]
589 ; Packet loaded OK...
590 pop cx ; Forget status
591 mov cx,[pxe_bootp_query_pkt_3.buffersize]
592 call parse_dhcp ; Parse DHCP packet
594 ; Generate ip= option
604 call gendotquad ; This takes network byte order input
606 xchg ah,al ; Convert to host byte order
607 ror eax,16 ; (BSWAP doesn't work on 386)
624 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
625 ; if we didn't get the magic enable, do not recognize any other options.
628 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
630 mov byte [DHCPMagic], 0 ; If not, kill all other options
635 ; Initialize UDP stack
639 mov [pxe_udp_open_pkt.sip],eax
640 mov di,pxe_udp_open_pkt
641 mov bx,PXENV_UDP_OPEN
644 cmp word [pxe_udp_open_pkt.status], byte 0
646 .failed: mov si,err_udpinit
652 ; Common initialization code
655 %include "cpuinit.inc"
658 ; Now we're all set to start with our *real* business. First load the
659 ; configuration file (if any) and parse it.
661 ; In previous versions I avoided using 32-bit registers because of a
662 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
663 ; random. I figure, though, that if there are any of those still left
664 ; they probably won't be trying to install Linux on them...
666 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
667 ; to take'm out. In fact, we may want to put them back if we're going
668 ; to boot ELKS at some point.
672 ; Store standard filename prefix
674 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
683 lea si,[di-2] ; Skip final null!
686 cmp al,'.' ; Count . or - as alphanum
698 .alnum: loop .find_alnum
700 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
703 mov si,tftpprefix_msg
710 ; Load configuration file
715 ; Begin looking for configuration file
718 test byte [DHCPMagic], 02h
721 ; We got a DHCP option, try it first
737 ; Try loading by MAC address
738 ; Have to guess config file name
756 xchg ah,al ; Convert to host byte order
759 .hexify_loop: rol eax,4
771 mov cx,9 ; Up to 9 attempts
773 .tryagain: mov byte [di],0
779 rep movsb ; Copy "default" string
799 ; Now we have the config file open. Parse the config file and
800 ; run the user interface.
805 ; Linux kernel loading code is common. However, we need to define
806 ; a couple of helper macros...
809 ; Handle "ipappend" option
810 %define HAVE_SPECIAL_APPEND
811 %macro SPECIAL_APPEND 0
812 test byte [IPAppend],01h ; ip=
820 test byte [IPAppend],02h
823 mov cx,bootif_str_len
827 mov byte [es:di-1],' ' ; Replace null with space
832 %define HAVE_UNLOAD_PREP
837 %include "runkernel.inc"
840 ; COMBOOT-loading code
842 %include "comboot.inc"
844 %include "cmdline.inc"
847 ; Boot sector loading code
849 %include "bootsect.inc"
852 ; Boot to the local disk by returning the appropriate PXE magic.
853 ; AX contains the appropriate return code.
857 mov ds,si ; Restore DI
859 mov [LocalBootType],ax
863 ; Restore the environment we were called with
870 mov ax,[cs:LocalBootType]
875 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
886 ac_kill: mov si,aborted_msg
889 ; abort_load: Called by various routines which wants to print a fatal
890 ; error message and return to the command prompt. Since this
891 ; may happen at just about any stage of the boot process, assume
892 ; our state is messed up, and just reset the segment registers
893 ; and the stack forcibly.
895 ; SI = offset (in _text) of error message to print
898 mov ax,cs ; Restore CS = DS = ES
903 call cwritestr ; Expects SI -> error msg
904 al_ok: jmp enter_command ; Return to command prompt
913 ; kaboom: write a message and bail out. Wait for quite a while,
914 ; or a user keypress, then do a hard reboot.
922 .patch: mov si,bailmsg
923 call writestr ; Returns with AL = 0
924 .drain: call pollchar
931 and al,09h ; Magic+Timeout
939 .wait2: mov dx,[BIOS_timer]
940 .wait3: call pollchar
951 mov word [BIOS_magic],0 ; Cold reboot
952 jmp 0F000h:0FFF0h ; Reset vector address
955 ; memory_scan_for_pxe_struct:
957 ; If none of the standard methods find the !PXE structure, look for it
958 ; by scanning memory.
961 ; CF = 0, ES:BX -> !PXE structure
962 ; Otherwise CF = 1, all registers saved
964 memory_scan_for_pxe_struct:
971 mov ax,[BIOS_fbm] ; Starting segment
972 shl ax,(10-4) ; Kilobytes -> paragraphs
973 ; mov ax,01000h ; Start to look here
974 dec ax ; To skip inc ax
977 cmp ax,0A000h ; End of memory
986 movzx cx,byte [es:4] ; Length of structure
987 cmp cl,08h ; Minimum length
996 jnz .mismatch ; Checksum must == 0
999 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1007 .not_found: mov si,notfound_msg
1015 ; memory_scan_for_pxenv_struct:
1017 ; If none of the standard methods find the PXENV+ structure, look for it
1018 ; by scanning memory.
1020 ; On exit, if found:
1021 ; CF = 0, ES:BX -> PXENV+ structure
1022 ; Otherwise CF = 1, all registers saved
1024 memory_scan_for_pxenv_struct:
1026 mov si,trymempxenv_msg
1028 ; mov ax,[BIOS_fbm] ; Starting segment
1029 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1030 mov ax,01000h ; Start to look here
1031 dec ax ; To skip inc ax
1034 cmp ax,0A000h ; End of memory
1043 movzx cx,byte [es:8] ; Length of structure
1044 cmp cl,26h ; Minimum length
1052 jnz .mismatch ; Checksum must == 0
1054 mov [bp+8],bx ; Save BX into stack frame
1060 .not_found: mov si,notfound_msg
1069 ; Open a TFTP connection to the server
1075 ; SI = socket pointer
1076 ; DX:AX = file length in bytes
1089 call allocate_socket
1092 mov ax,PKT_RETRY ; Retry counter
1093 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1095 .sendreq: push ax ; [bp-2] - Retry counter
1096 push si ; [bp-4] - File name
1097 push bx ; [bp-6] - TFTP block
1099 push bx ; [bp-8] - TID (local port no)
1101 mov eax,[ServerIP] ; Server IP
1102 mov [pxe_udp_write_pkt.status],byte 0
1103 mov [pxe_udp_write_pkt.sip],eax
1105 mov [pxe_udp_write_pkt.gip],eax
1106 mov [pxe_udp_write_pkt.lport],bx
1108 mov [pxe_udp_write_pkt.rport],ax
1110 mov [pxe_udp_write_pkt.buffer],di
1111 mov ax,TFTP_RRQ ; TFTP opcode
1113 push si ; Add common prefix
1118 call strcpy ; Filename
1120 mov cx,tftp_tail_len
1122 sub di,packet_buf ; Get packet size
1123 mov [pxe_udp_write_pkt.buffersize],di
1125 mov di,pxe_udp_write_pkt
1126 mov bx,PXENV_UDP_WRITE
1129 cmp word [pxe_udp_write_pkt.status],byte 0
1133 ; Danger, Will Robinson! We need to support timeout
1134 ; and retry lest we just lost a packet...
1137 ; Packet transmitted OK, now we need to receive
1138 .getpacket: push word [PktTimeout] ; [bp-10]
1139 push word [BIOS_timer] ; [bp-12]
1141 .pkt_loop: mov bx,[bp-8] ; TID
1143 mov word [pxe_udp_read_pkt.status],0
1144 mov [pxe_udp_read_pkt.buffer],di
1145 mov [pxe_udp_read_pkt.buffer+2],ds
1146 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1148 mov [pxe_udp_read_pkt.dip],eax
1149 mov [pxe_udp_read_pkt.lport],bx
1150 mov di,pxe_udp_read_pkt
1151 mov bx,PXENV_UDP_READ
1154 jz .got_packet ; Wait for packet
1160 dec word [bp-10] ; Timeout
1162 pop ax ; Adjust stack
1164 shl word [PktTimeout],1 ; Exponential backoff
1168 mov si,[bp-6] ; TFTP pointer
1172 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1174 mov [si+tftp_remoteip],eax
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
1290 .err_reply: ; Option negotiation error. Send ERROR reply.
1291 ; ServerIP and gateway are already programmed in
1293 mov ax,[si+tftp_remoteport]
1294 mov word [pxe_udp_write_pkt.rport],ax
1295 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1296 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1297 mov di,pxe_udp_write_pkt
1298 mov bx,PXENV_UDP_WRITE
1301 ; Write an error message and explode
1306 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1308 .failure: pop bx ; Junk
1312 dec ax ; Retry counter
1313 jnz .sendreq ; Try again
1315 .error: mov si,bx ; Socket pointer
1316 .error_si: ; Socket pointer already in SI
1317 call free_socket ; ZF <- 1, SI <- 0
1323 ; allocate_socket: Allocate a local UDP port structure
1327 ; BX = socket pointer
1335 .check: cmp word [bx], byte 0
1337 add bx,open_file_t_size
1342 ; Allocate a socket number. Socket numbers are made
1343 ; guaranteed unique by including the socket slot number
1344 ; (inverted, because we use the loop counter cx); add a
1345 ; counter value to keep the numbers from being likely to
1346 ; get immediately reused.
1348 ; The NextSocket variable also contains the top two bits
1349 ; set. This generates a value in the range 49152 to
1356 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1358 shl cx,13-MAX_OPEN_LG2
1360 xchg ch,cl ; Convert to network byte order
1361 mov [bx],cx ; Socket in use
1367 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1375 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1383 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1384 ; to by ES:DI; ends on encountering any whitespace.
1386 ; This verifies that a filename is < FILENAME_MAX characters
1387 ; and doesn't contain whitespace, and zero-pads the output buffer,
1388 ; so "repe cmpsb" can do a compare.
1391 mov cx,FILENAME_MAX-1
1394 cmp al,' ' ; If control or space, end
1399 inc cx ; At least one null byte
1400 xor ax,ax ; Zero-fill name
1401 rep stosb ; Doesn't do anything if CX=0
1405 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1406 ; filename to the conventional representation. This is needed
1407 ; for the BOOT_IMAGE= parameter for the kernel.
1408 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1409 ; known to be shorter.
1411 ; DS:SI -> input mangled file name
1412 ; ES:DI -> output buffer
1414 ; On return, DI points to the first byte after the output name,
1415 ; which is set to a null byte.
1417 unmangle_name: call strcpy
1418 dec di ; Point to final null byte
1424 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1425 ; calling convention. This is a separate local routine so
1426 ; we can hook special things from it if necessary.
1430 .jump: call 0:pxe_thunk ; Default to calling the thunk
1431 cld ; Make sure DF <- 0
1434 ; Must be after function def due to NASM bug
1435 PXENVEntry equ pxenv.jump+1
1440 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1441 ; calling convention (using the stack.)
1443 ; This is called as a far routine so that we can just stick it into
1444 ; the PXENVEntry variable.
1452 cmc ; Set CF unless ax == 0
1455 ; Must be after function def due to NASM bug
1456 PXEEntry equ pxe_thunk.jump+1
1459 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1461 ; In this case, get multiple blocks from a specific TCP connection.
1465 ; SI -> TFTP socket pointer
1466 ; CX -> 512-byte block count; 0FFFFh = until end of file
1468 ; SI -> TFTP socket pointer (or 0 on EOF)
1479 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1480 jz .hit_eof ; Nothing to do?
1485 movzx eax,word [bx+tftp_bytesleft]
1489 jcxz .need_packet ; No bytes available?
1492 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1493 mov si,[bx+tftp_dataptr]
1494 sub [bx+tftp_bytesleft],cx
1495 fs rep movsb ; Copy from packet buffer
1496 mov [bx+tftp_dataptr],si
1507 ; Is there anything left of this?
1508 mov eax,[si+tftp_filesize]
1509 sub eax,[si+tftp_filepos]
1510 jnz .bytes_left ; CF <- 0
1512 cmp [si+tftp_bytesleft],ax
1513 jnz .bytes_left ; CF <- 0
1515 ; The socket is closed and the buffer drained
1516 ; Close socket structure and re-init for next user
1523 ; No data in buffer, check to see if we can get a packet...
1527 mov eax,[bx+tftp_filesize]
1528 cmp eax,[bx+tftp_filepos]
1529 je .hit_eof ; Already EOF'd; socket already closed
1541 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1548 ; Start by ACKing the previous packet; this should cause the
1549 ; next packet to be sent.
1551 mov word [PktTimeout],PKT_TIMEOUT
1553 .send_ack: push cx ; <D> Retry count
1555 mov ax,[si+tftp_lastpkt]
1556 call ack_packet ; Send ACK
1558 ; We used to test the error code here, but sometimes
1559 ; PXE would return negative status even though we really
1560 ; did send the ACK. Now, just treat a failed send as
1561 ; a normally lost packet, and let it time out in due
1564 .send_ok: ; Now wait for packet.
1565 mov dx,[BIOS_timer] ; Get current time
1568 .wait_data: push cx ; <E> Timeout
1569 push dx ; <F> Old time
1571 mov bx,[si+tftp_pktbuf]
1572 mov [pxe_udp_read_pkt.buffer],bx
1573 mov [pxe_udp_read_pkt.buffer+2],fs
1574 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1575 mov eax,[si+tftp_remoteip]
1576 mov [pxe_udp_read_pkt.sip],eax
1578 mov [pxe_udp_read_pkt.dip],eax
1579 mov ax,[si+tftp_remoteport]
1580 mov [pxe_udp_read_pkt.rport],ax
1581 mov ax,[si+tftp_localport]
1582 mov [pxe_udp_read_pkt.lport],ax
1583 mov di,pxe_udp_read_pkt
1584 mov bx,PXENV_UDP_READ
1591 ; No packet, or receive failure
1593 pop ax ; <F> Old time
1594 pop cx ; <E> Timeout
1595 cmp ax,dx ; Same time -> don't advance timeout
1596 je .wait_data ; Same clock tick
1597 loop .wait_data ; Decrease timeout
1599 pop cx ; <D> Didn't get any, send another ACK
1600 shl word [PktTimeout],1 ; Exponential backoff
1602 jmp kaboom ; Forget it...
1604 .recv_ok: pop dx ; <F>
1607 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1608 jb .wait_data ; Bad size for a DATA packet
1610 mov bx,[si+tftp_pktbuf]
1611 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1612 jne .wait_data ; Then wait for something else
1614 mov ax,[si+tftp_lastpkt]
1615 xchg ah,al ; Host byte order
1616 inc ax ; Which packet are we waiting for?
1617 xchg ah,al ; Network byte order
1621 ; Wrong packet, ACK the packet and then try again
1622 ; This is presumably because the ACK got lost,
1623 ; so the server just resent the previous packet
1626 jmp .send_ok ; Reset timeout
1628 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1630 pop cx ; <D> Don't need the retry count anymore
1632 mov [si+tftp_lastpkt],ax ; Update last packet number
1634 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1635 sub cx,byte 4 ; Skip TFTP header
1637 ; If this is a zero-length block, don't mess with the pointers,
1638 ; since we may have just set up the previous block that way
1641 ; Set pointer to data block
1642 lea ax,[bx+4] ; Data past TFTP header
1643 mov [si+tftp_dataptr],ax
1645 add [si+tftp_filepos],ecx
1646 mov [si+tftp_bytesleft],cx
1648 cmp cx,[si+tftp_blksize] ; Is it a full block?
1649 jb .last_block ; If so, it's not EOF
1651 ; If we had the exact right number of bytes, always get
1652 ; one more packet to get the (zero-byte) EOF packet and
1654 mov eax,[si+tftp_filepos]
1655 cmp [si+tftp_filesize],eax
1661 .last_block: ; Last block - ACK packet immediately
1665 ; Make sure we know we are at end of file
1666 mov eax,[si+tftp_filepos]
1667 mov [si+tftp_filesize],eax
1674 ; Send ACK packet. This is a common operation and so is worth canning.
1678 ; AX = Packet # to ack (network byte order)
1681 ; All registers preserved
1683 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1687 mov [ack_packet_buf+2],ax ; Packet number to ack
1689 mov [pxe_udp_write_pkt.lport],ax
1690 mov ax,[si+tftp_remoteport]
1691 mov [pxe_udp_write_pkt.rport],ax
1692 mov eax,[si+tftp_remoteip]
1693 mov [pxe_udp_write_pkt.sip],eax
1699 mov [pxe_udp_write_pkt.gip],eax
1700 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1701 mov [pxe_udp_write_pkt.buffersize], word 4
1702 mov di,pxe_udp_write_pkt
1703 mov bx,PXENV_UDP_WRITE
1705 cmp ax,byte 0 ; ZF = 1 if write OK
1712 ; This function unloads the PXE and UNDI stacks and unclaims
1716 test byte [KeepPXE],01h ; Should we keep PXE around?
1726 mov si,new_api_unload
1727 cmp byte [APIVer+1],2 ; Major API version >= 2?
1729 mov si,old_api_unload
1732 .call_loop: xor ax,ax
1737 mov di,pxe_unload_stack_pkt
1740 mov cx,pxe_unload_stack_pkt_len >> 1
1745 mov ax,word [pxe_unload_stack_pkt.status]
1746 cmp ax,PXENV_STATUS_SUCCESS
1751 %if USE_PXE_PROVIDED_STACK
1752 ; We need to switch to our local stack here...
1760 sub ax,[BaseStack+4] ; Are we using the base stack
1761 je .is_base_stack ; (as opposed to the COMBOOT stack)?
1763 lgs si,[SavedSSSP] ; COMBOOT stack
1769 mov [BaseStack+4],es
1772 mov [SavedSSSP],di ; New SP
1773 mov [SavedSSSP+2],es
1776 and ax,ax ; Remember which stack
1779 ; Update the base stack pointer since it's in use
1789 mov dx,[RealBaseMem]
1790 cmp dx,[BIOS_fbm] ; Sanity check
1794 ; Check that PXE actually unhooked the INT 1Ah chain
1795 movzx eax,word [4*0x1a]
1796 movzx ecx,word [4*0x1a+2]
1800 cmp ax,dx ; Not in range
1814 mov si,cant_free_msg
1830 ; We want to keep PXE around, but still we should reset
1831 ; it to the standard bootup configuration
1836 mov bx,PXENV_UDP_CLOSE
1837 mov di,pxe_udp_close_pkt
1845 ; Take an IP address (in network byte order) in EAX and
1846 ; output a dotted quad string to ES:DI.
1847 ; DI points to terminal null at end of string on exit.
1857 jb .lt10 ; If so, skip first 2 digits
1860 jb .lt100 ; If so, skip first digit
1863 ; Now AH = 100-digit; AL = remainder
1870 ; Now AH = 10-digit; AL = remainder
1881 ror eax,8 ; Move next char into LSB
1891 ; Parse a DHCP packet. This includes dealing with "overloaded"
1892 ; option fields (see RFC 2132, section 9.3)
1894 ; This should fill in the following global variables, if the
1895 ; information is present:
1897 ; MyIP - client IP address
1898 ; ServerIP - boot server IP address
1899 ; Netmask - network mask
1900 ; Gateway - default gateway router IP
1901 ; UseGW - zero if ServerIP local, otherwise Gateway
1902 ; BootFile - boot file name
1904 ; This assumes the DHCP packet is in "trackbuf" and the length
1905 ; of the packet in in CX on entry.
1909 mov byte [OverLoad],0 ; Assume no overload
1910 mov eax, [trackbuf+bootp.yip]
1913 cmp al,224 ; Class D or higher -> bad
1917 mov eax, [trackbuf+bootp.sip]
1920 cmp al,224 ; Class D or higher -> bad
1924 sub cx, bootp.options
1926 mov si, trackbuf+bootp.option_magic
1928 cmp eax, BOOTP_OPTION_MAGIC
1930 call parse_dhcp_options
1932 mov si, trackbuf+bootp.bootfile
1933 test byte [OverLoad],1
1936 call parse_dhcp_options
1937 jmp short .parsed_file
1940 jz .parsed_file ; No bootfile name
1945 stosb ; Null-terminate
1947 mov si, trackbuf+bootp.sname
1948 test byte [OverLoad],2
1951 call parse_dhcp_options
1953 ; Now adjust the gateway
1964 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
1965 ; size is CX -- some DHCP servers leave option fields unterminated
1966 ; in violation of the spec.
1968 ; For parse_some_dhcp_options, DH contains the minimum value for
1969 ; the option to recognize -- this is used to restrict parsing to
1970 ; PXELINUX-specific options only.
1975 parse_some_dhcp_options:
1982 jz .done ; Last byte; must be PAD, END or malformed
1983 cmp al, 0 ; PAD option
1985 cmp al,255 ; END option
1988 ; Anything else will have a length field
1989 mov dl,al ; DL <- option number
1991 lodsb ; AX <- option length
1993 sub cx,ax ; Decrement bytes left counter
1994 jb .done ; Malformed option: length > field size
1996 cmp dl,dh ; Is the option value
1999 cmp dl,1 ; SUBNET MASK option
2006 cmp dl,3 ; ROUTER option
2013 cmp dl,43 ; VENDOR ENCAPSULATED option
2016 mov dh,208 ; Only recognize PXELINUX options
2017 mov cx,ax ; Length of option = max bytes to parse
2018 call parse_some_dhcp_options ; Parse recursive structure
2023 cmp dl,52 ; OPTION OVERLOAD option
2030 cmp dl,67 ; BOOTFILE NAME option
2033 jmp short .copyoption
2035 ret ; This is here to make short jumps easier
2038 cmp dl,208 ; PXELINUX MAGIC option
2040 cmp al,4 ; Must have length == 4
2042 cmp dword [si], htonl(0xF100747E) ; Magic number
2044 or byte [DHCPMagic], byte 1 ; Found magic #
2048 cmp dl,209 ; PXELINUX CONFIGFILE option
2051 or byte [DHCPMagic], byte 2 ; Got config file
2052 jmp short .copyoption
2055 cmp dl,210 ; PXELINUX PATHPREFIX option
2058 or byte [DHCPMagic], byte 4 ; Got path prefix
2059 jmp short .copyoption
2062 cmp dl,211 ; PXELINUX REBOOTTIME option
2067 xchg bl,bh ; Convert to host byte order
2070 mov [RebootTime],ebx
2071 or byte [DHCPMagic], byte 8 ; Got RebootTime
2072 ; jmp short .opt_done
2075 ; Unknown option. Skip to the next one.
2081 ; Common code for copying an option verbatim
2085 xchg cx,ax ; Now ax == 0
2086 stosb ; Null-terminate
2087 jmp short .opt_done_noskip
2092 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2093 ; option into IPOption based on a DHCP packet in trackbuf.
2094 ; Assumes CS == DS == ES.
2115 call gendotquad ; Zero-terminates its output
2117 mov [IPOptionLen],di
2122 ; Call the receive loop while idle. This is done mostly so we can respond to
2123 ; ARP messages, but perhaps in the future this can be used to do network
2126 ; hpa sez: people using automatic control on the serial port get very
2127 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2128 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2129 ; passed since the last poll, and reset this when a character is
2130 ; received (RESET_IDLE).
2134 mov ax,[cs:BIOS_timer]
2135 mov [cs:IdleTimer],ax
2141 mov ax,[cs:BIOS_timer]
2142 sub ax,[cs:IdleTimer]
2154 mov [pxe_udp_read_pkt.status],al ; 0
2155 mov [pxe_udp_read_pkt.buffer],di
2156 mov [pxe_udp_read_pkt.buffer+2],ds
2157 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2159 mov [pxe_udp_read_pkt.dip],eax
2160 mov [pxe_udp_read_pkt.lport],ax ; 0
2161 mov di,pxe_udp_read_pkt
2162 mov bx,PXENV_UDP_READ
2171 ; -----------------------------------------------------------------------------
2173 ; -----------------------------------------------------------------------------
2175 %include "getc.inc" ; getc et al
2176 %include "conio.inc" ; Console I/O
2177 %include "writestr.inc" ; String output
2178 writestr equ cwritestr
2179 %include "writehex.inc" ; Hexadecimal output
2180 %include "parseconfig.inc" ; High-level config file handling
2181 %include "parsecmd.inc" ; Low-level config file handling
2182 %include "bcopy32.inc" ; 32-bit bcopy
2183 %include "loadhigh.inc" ; Load a file into high memory
2184 %include "font.inc" ; VGA font stuff
2185 %include "graphics.inc" ; VGA graphics
2186 %include "highmem.inc" ; High memory sizing
2187 %include "strcpy.inc" ; strcpy()
2188 %include "rawcon.inc" ; Console I/O w/o using the console functions
2190 ; -----------------------------------------------------------------------------
2191 ; Begin data section
2192 ; -----------------------------------------------------------------------------
2196 hextbl_lower db '0123456789abcdef'
2197 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2199 boot_prompt db 'boot: ', 0
2200 wipe_char db BS, ' ', BS, 0
2201 err_notfound db 'Could not find kernel image: ',0
2202 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2203 err_noram db 'It appears your computer has less than '
2205 db 'K of low ("DOS")'
2207 db 'RAM. Linux needs at least this amount to boot. If you get'
2209 db 'this message in error, hold down the Ctrl key while'
2211 db 'booting, and I will take your word for it.', CR, LF, 0
2212 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2213 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2214 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2215 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2216 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2217 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2219 err_notdos db ': attempted DOS system call', CR, LF, 0
2220 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2221 err_bssimage db 'BSS images not supported.', CR, LF, 0
2222 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2223 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2224 bailmsg equ err_bootfailed
2225 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2226 err_pxefailed db 'PXE API call failed, error ', 0
2227 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2228 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2229 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2230 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2231 apiver_str db 'PXE API version is ',0
2232 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2233 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2234 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2235 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2236 undi_data_msg db 'UNDI data segment at: ',0
2237 undi_data_len_msg db 'UNDI data segment size: ',0
2238 undi_code_msg db 'UNDI code segment at: ',0
2239 undi_code_len_msg db 'UNDI code segment size: ',0
2240 cant_free_msg db 'Failed to free base memory, error ', 0
2241 notfound_msg db 'not found', CR, LF, 0
2242 myipaddr_msg db 'My IP address seems to be ',0
2243 tftpprefix_msg db 'TFTP prefix: ', 0
2244 localboot_msg db 'Booting from local disk...', CR, LF, 0
2245 cmdline_msg db 'Command line: ', CR, LF, 0
2246 ready_msg db 'Ready.', CR, LF, 0
2247 trying_msg db 'Trying to load: ', 0
2248 crlfloading_msg db CR, LF ; Fall through
2249 loading_msg db 'Loading ', 0
2252 fourbs_msg db BS, BS, BS, BS, 0
2253 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2256 crff_msg db CR, FF, 0
2257 default_str db 'default', 0
2258 default_len equ ($-default_str)
2259 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2260 cfgprefix db 'pxelinux.cfg/' ; No final null!
2261 cfgprefix_len equ ($-cfgprefix)
2264 ; Command line options we'd like to take a look at
2266 ; mem= and vga= are handled as normal 32-bit integer values
2267 initrd_cmd db 'initrd='
2268 initrd_cmd_len equ $-initrd_cmd
2270 ; This one we make ourselves
2271 bootif_str db 'BOOTIF='
2272 bootif_str_len equ $-bootif_str
2274 ; Config file keyword table
2276 %include "keywords.inc"
2279 ; Extensions to search for (in *forward* order).
2280 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2283 exten_table: db '.cbt' ; COMBOOT (specific)
2284 db '.0', 0, 0 ; PXE bootstrap program
2285 db '.com' ; COMBOOT (same as DOS)
2288 dd 0, 0 ; Need 8 null bytes here
2291 ; PXE unload sequences
2295 db PXENV_UNDI_SHUTDOWN
2296 db PXENV_UNLOAD_STACK
2301 db PXENV_UNDI_SHUTDOWN
2302 db PXENV_UNLOAD_STACK
2303 db PXENV_UNDI_CLEANUP
2307 ; PXE query packets partially filled in
2309 pxe_bootp_query_pkt_2:
2310 .status: dw 0 ; Status
2311 .packettype: dw 2 ; DHCPACK packet
2312 .buffersize: dw trackbufsize ; Packet size
2313 .buffer: dw trackbuf, 0 ; seg:off of buffer
2314 .bufferlimit: dw trackbufsize ; Unused
2316 pxe_bootp_query_pkt_3:
2317 .status: dw 0 ; Status
2318 .packettype: dw 3 ; Boot server packet
2319 .buffersize: dw trackbufsize ; Packet size
2320 .buffer: dw trackbuf, 0 ; seg:off of buffer
2321 .bufferlimit: dw trackbufsize ; Unused
2323 pxe_bootp_size_query_pkt:
2324 .status: dw 0 ; Status
2325 .packettype: dw 2 ; DHCPACK packet
2326 .buffersize: dw 0 ; Packet size
2327 .buffer: dw 0, 0 ; seg:off of buffer
2328 .bufferlimit: dw 0 ; Unused
2331 .status: dw 0 ; Status
2332 .sip: dd 0 ; Source (our) IP
2335 .status: dw 0 ; Status
2338 .status: dw 0 ; Status
2339 .sip: dd 0 ; Server IP
2340 .gip: dd 0 ; Gateway IP
2341 .lport: dw 0 ; Local port
2342 .rport: dw 0 ; Remote port
2343 .buffersize: dw 0 ; Size of packet
2344 .buffer: dw 0, 0 ; seg:off of buffer
2347 .status: dw 0 ; Status
2348 .sip: dd 0 ; Source IP
2349 .dip: dd 0 ; Destination (our) IP
2350 .rport: dw 0 ; Remote port
2351 .lport: dw 0 ; Local port
2352 .buffersize: dw 0 ; Max packet size
2353 .buffer: dw 0, 0 ; seg:off of buffer
2356 ; Misc initialized (data) variables
2359 BaseStack dd StackBuf ; SS:ESP of base stack
2360 NextSocket dw 49152 ; Counter for allocating socket numbers
2361 KeepPXE db 0 ; Should PXE be kept around?
2366 tftp_tail db 'octet', 0 ; Octet mode
2367 tsize_str db 'tsize' ,0 ; Request size
2368 tsize_len equ ($-tsize_str)
2370 blksize_str db 'blksize', 0 ; Request large blocks
2371 blksize_len equ ($-blksize_str)
2372 asciidec TFTP_LARGEBLK
2374 tftp_tail_len equ ($-tftp_tail)
2378 ; Options negotiation parsing table (string pointer, string len, offset
2379 ; into socket structure)
2382 dw tsize_str, tsize_len, tftp_filesize
2383 dw blksize_str, blksize_len, tftp_blksize
2384 tftp_opts equ ($-tftp_opt_table)/6
2387 ; Error packet to return on options negotiation error
2389 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2390 dw TFTP_EOPTNEG ; ERROR 8: bad options
2391 db 'tsize option required', 0 ; Error message
2392 tftp_opt_err_len equ ($-tftp_opt_err)
2395 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2398 ; IP information (initialized to "unknown" values)
2399 MyIP dd 0 ; My IP address
2400 ServerIP dd 0 ; IP address of boot server
2401 Netmask dd 0 ; Netmask of this subnet
2402 Gateway dd 0 ; Default router
2403 UseGW dd 0 ; Router to use to get to ServerIP
2404 ServerPort dw TFTP_PORT ; TFTP server port
2407 ; Variables that are uninitialized in SYSLINUX but initialized here
2410 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2411 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2412 BufSafeBytes dw trackbufsize ; = how many bytes?
2413 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2415 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2416 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2419 IPAppend db 0 ; Default IPAPPEND option
2420 DHCPMagic db 0 ; DHCP site-specific option info