1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id: pxelinux.asm,v 1.168 2005/01/20 18:43:22 hpa Exp $
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-2005 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"
31 ; Some semi-configurable constants... change on your own risk.
34 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
35 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
36 NULLFILE equ 0 ; Zero byte == null file name
37 NULLOFFSET equ 4 ; Position in which to look
38 REBOOT_TIME equ 5*60 ; If failure, time until full reset
39 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
40 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
41 MAX_OPEN equ (1 << MAX_OPEN_LG2)
42 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
43 TFTP_PORT equ htons(69) ; Default TFTP port
44 PKT_RETRY equ 6 ; Packet transmit retry count
45 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
46 ; Desired TFTP block size
47 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
48 ; be a fair number of networks with "substandard" MTUs which break.
49 ; The code assumes TFTP_LARGEBLK <= 2K.
51 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
52 ; Standard TFTP block size
53 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
54 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
55 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
57 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
58 SECTOR_SIZE equ TFTP_BLOCKSIZE
61 ; This is what we need to do when idle
71 ; TFTP operation codes
73 TFTP_RRQ equ htons(1) ; Read request
74 TFTP_WRQ equ htons(2) ; Write request
75 TFTP_DATA equ htons(3) ; Data packet
76 TFTP_ACK equ htons(4) ; ACK packet
77 TFTP_ERROR equ htons(5) ; ERROR packet
78 TFTP_OACK equ htons(6) ; OACK packet
83 TFTP_EUNDEF equ htons(0) ; Unspecified error
84 TFTP_ENOTFOUND equ htons(1) ; File not found
85 TFTP_EACCESS equ htons(2) ; Access violation
86 TFTP_ENOSPACE equ htons(3) ; Disk full
87 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
88 TFTP_EBADID equ htons(5) ; Unknown transfer
89 TFTP_EEXISTS equ htons(6) ; File exists
90 TFTP_ENOUSER equ htons(7) ; No such user
91 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
94 ; The following structure is used for "virtual kernels"; i.e. LILO-style
95 ; option labels. The options we permit here are `kernel' and `append
96 ; Since there is no room in the bottom 64K for all of these, we
97 ; stick them at vk_seg:0000 and copy them down before we need them.
100 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
101 vk_rname: resb FILENAME_MAX ; Real name
102 vk_ipappend: resb 1 ; "IPAPPEND" flag
106 vk_append: resb max_cmd_len+1 ; Command line
108 vk_end: equ $ ; Should be <= vk_size
112 ; Segment assignments in the bottom 640K
113 ; 0000h - main code/data segment (and BIOS segment)
115 real_mode_seg equ 4000h
116 vk_seg equ 3000h ; Virtual kernels
117 xfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mem
118 pktbuf_seg equ 1000h ; Packet buffers segments
119 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
122 ; BOOTP/DHCP packet pattern
126 .opcode resb 1 ; BOOTP/DHCP "opcode"
127 .hardware resb 1 ; ARP hardware type
128 .hardlen resb 1 ; Hardware address length
129 .gatehops resb 1 ; Used by forwarders
130 .ident resd 1 ; Transaction ID
131 .seconds resw 1 ; Seconds elapsed
132 .flags resw 1 ; Broadcast flags
133 .cip resd 1 ; Client IP
134 .yip resd 1 ; "Your" IP
135 .sip resd 1 ; Next server IP
136 .gip resd 1 ; Relay agent IP
137 .macaddr resb 16 ; Client MAC address
138 .sname resb 64 ; Server name (optional)
139 .bootfile resb 128 ; Boot file name
140 .option_magic resd 1 ; Vendor option magic cookie
141 .options resb 1260 ; Vendor options
144 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
147 ; TFTP connection data structure. Each one of these corresponds to a local
148 ; UDP port. The size of this structure must be a power of 2.
149 ; HBO = host byte order; NBO = network byte order
150 ; (*) = written by options negotiation code, must be dword sized
153 tftp_localport resw 1 ; Local port number (0 = not in use)
154 tftp_remoteport resw 1 ; Remote port number
155 tftp_remoteip resd 1 ; Remote IP address
156 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
157 tftp_filesize resd 1 ; Total file size(*)
158 tftp_blksize resd 1 ; Block size for this connection(*)
159 tftp_bytesleft resw 1 ; Unclaimed data bytes
160 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
161 tftp_dataptr resw 1 ; Pointer to available data
162 resw 2 ; Currently unusued
163 ; At end since it should not be zeroed on socked close
164 tftp_pktbuf resw 1 ; Packet buffer offset
167 %if (open_file_t_size & (open_file_t_size-1))
168 %error "open_file_t is not a power of 2"
172 ; ---------------------------------------------------------------------------
174 ; ---------------------------------------------------------------------------
177 ; Memory below this point is reserved for the BIOS and the MBR
180 trackbufsize equ 8192
181 trackbuf resb trackbufsize ; Track buffer goes here
182 getcbuf resb trackbufsize
185 ; Put some large buffers here, before RBFG_brainfuck,
186 ; where we can still carefully control the address
189 alignb open_file_t_size
190 Files resb MAX_OPEN*open_file_t_size
193 BootFile resb 256 ; Boot file from DHCP packet
194 ConfigServer resd 1 ; Null prefix for mangled config name
195 ConfigName resb 256-4 ; Configuration file from DHCP option
196 PathPrefix resb 256 ; Path prefix derived from boot file
197 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
198 IPOption resb 80 ; ip= option buffer
199 InitStack resd 1 ; Pointer to reset stack
201 ; Warning here: RBFG build 22 randomly overwrites memory location
202 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
203 ; screws up the pointer to its own internal packet buffer and starts
204 ; writing a received ARP packet into low memory.
205 RBFG_brainfuck resb 0E00h
209 RebootTime resd 1 ; Reboot timeout, if set by option
210 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
211 APIVer resw 1 ; PXE API version found
212 IPOptionLen resw 1 ; Length of IPOption
213 IdleTimer resw 1 ; Time to check for ARP?
214 LocalBootType resw 1 ; Local boot return code
215 PktTimeout resw 1 ; Timeout for current packet
216 RealBaseMem resw 1 ; Amount of DOS memory after freeing
217 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
219 ; The relative position of these fields matter!
220 MACLen resb 1 ; MAC address len
221 MACType resb 1 ; MAC address type
222 MAC resb 16 ; Actual MAC address
223 BOOTIFStr resb 7 ; Space for "BOOTIF="
224 MACStr resb 3*17 ; MAC address as a string
227 ; PXE packets which don't need static initialization
230 pxe_unload_stack_pkt:
231 .status: resw 1 ; Status
232 .reserved: resw 10 ; Reserved
233 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
236 ; BOOTP/DHCP packet buffer
239 packet_buf resb 2048 ; Transfer packet
240 packet_buf_size equ $-packet_buf
243 ; Constants for the xfer_buf_seg
245 ; The xfer_buf_seg is also used to store message file buffers. We
246 ; need two trackbuffers (text and graphics), plus a work buffer
247 ; for the graphics decompressor.
249 xbs_textbuf equ 0 ; Also hard-coded, do not change
250 xbs_vgabuf equ trackbufsize
251 xbs_vgatmpbuf equ 2*trackbufsize
255 ; PXELINUX needs more BSS than the other derivatives;
256 ; therefore we relocate it from 7C00h on startup.
258 StackBuf equ $ ; Base of stack if we use our own
261 ; Primary entry point.
265 pushfd ; Paranoia... in case of return to PXE
266 pushad ; ... save as much state as possible
276 ; This is uglier than it should be, but works around
277 ; some NASM 0.98.38 bugs.
278 mov di,section..bcopy32.start
279 add di,__bcopy_size-4
280 lea si,[di-(TEXT_START-7C00h)]
281 lea cx,[di-(TEXT_START-4)]
283 std ; Overlapping areas, copy backwards
286 jmp 0:_start1 ; Canonicalize address
289 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
291 ; That is all pushed onto the PXE stack. Save the pointer
292 ; to it and switch to an internal stack.
296 %if USE_PXE_PROVIDED_STACK
297 ; Apparently some platforms go bonkers if we
298 ; set up our own stack...
306 sti ; Stack set up and ready
310 ; Initialize screen (if we're using one)
312 ; Now set up screen parameters
315 ; Wipe the F-key area
318 mov cx,10*(1 << FILENAME_MAX_LG2)
319 push es ; Save ES -> PXE structure
326 ; Tell the user we got this far
328 mov si,syslinux_banner
335 ; Assume API version 2.1, in case we find the !PXE structure without
336 ; finding the PXENV+ structure. This should really look at the Base
337 ; Code ROM ID structure in have_pxe, but this is adequate for now --
338 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
339 ; about higher versions than that.
341 mov word [APIVer],0201h
344 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
345 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
346 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
347 ; We should make that the second test, and not trash ES:BX...
349 cmp dword [es:bx], '!PXE'
352 ; Uh-oh, not there... try plan B
354 int 1Ah ; May trash regs
359 ; Okay, that gave us the PXENV+ structure, find !PXE
360 ; structure from that (if available)
361 cmp dword [es:bx], 'PXEN'
363 cmp word [es:bx+4], 'V+'
366 ; Nothing there either. Last-ditch: scan memory
367 call memory_scan_for_pxe_struct ; !PXE scan
369 call memory_scan_for_pxenv_struct ; PXENV+ scan
372 no_pxe: mov si,err_nopxe
390 cmp ax,0201h ; API version 2.1 or higher
394 les bx,[es:bx+28h] ; !PXE structure pointer
395 cmp dword [es:bx],'!PXE'
398 ; Nope, !PXE structure missing despite API 2.1+, or at least
399 ; the pointer is missing. Do a last-ditch attempt to find it.
400 call memory_scan_for_pxe_struct
403 ; Otherwise, no dice, use PXENV+ structure
407 old_api: ; Need to use a PXENV+ structure
408 mov si,using_pxenv_msg
411 mov eax,[es:bx+0Ah] ; PXE RM API
419 mov si,undi_data_len_msg
429 mov si,undi_code_len_msg
435 ; Compute base memory size from PXENV+ structure
437 movzx eax,word [es:bx+20h] ; UNDI data seg
438 cmp ax,[es:bx+24h] ; UNDI code seg
448 shr eax,10 ; Convert to kilobytes
451 mov si,pxenventry_msg
453 mov ax,[PXENVEntry+2]
474 mov si,undi_data_len_msg
484 mov si,undi_code_len_msg
490 ; Compute base memory size from !PXE structure
517 pop es ; Restore CS == DS == ES
520 ; Network-specific initialization
523 mov [LocalDomain],al ; No LocalDomain received
526 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
527 ; address). This lives in the DHCPACK packet (query info 2).
530 mov di,pxe_bootp_query_pkt_2
531 mov bx,PXENV_GET_CACHED_INFO
534 push word [pxe_bootp_query_pkt_2.status]
539 mov di,pxe_bootp_size_query_pkt
540 mov bx,PXENV_GET_CACHED_INFO
545 mov ax,[pxe_bootp_size_query_pkt.buffersize]
558 jmp kaboom ; We're dead
561 pop cx ; Forget status
562 mov cx,[pxe_bootp_query_pkt_2.buffersize]
563 call parse_dhcp ; Parse DHCP packet
565 ; Save away MAC address (assume this is in query info 2. If this
566 ; turns out to be problematic it might be better getting it from
567 ; the query info 1 packet.)
570 movzx cx,byte [trackbuf+bootp.hardlen]
572 mov al,[trackbuf+bootp.hardware]
574 mov si,trackbuf+bootp.macaddr
585 mov cx,bootif_str_len
605 mov [di-1],byte 0 ; Null-terminate and strip final colon
608 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
609 ; packet (query info 3).
611 mov [pxe_bootp_size_query_pkt.packettype], byte 3
613 mov di,pxe_bootp_query_pkt_3
614 mov bx,PXENV_GET_CACHED_INFO
617 push word [pxe_bootp_query_pkt_3.status]
622 ; Packet loaded OK...
623 pop cx ; Forget status
624 mov cx,[pxe_bootp_query_pkt_3.buffersize]
625 call parse_dhcp ; Parse DHCP packet
627 ; Generate ip= option
637 call gendotquad ; This takes network byte order input
639 xchg ah,al ; Convert to host byte order
640 ror eax,16 ; (BSWAP doesn't work on 386)
657 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
658 ; if we didn't get the magic enable, do not recognize any other options.
661 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
663 mov byte [DHCPMagic], 0 ; If not, kill all other options
668 ; Initialize UDP stack
672 mov [pxe_udp_open_pkt.sip],eax
673 mov di,pxe_udp_open_pkt
674 mov bx,PXENV_UDP_OPEN
677 cmp word [pxe_udp_open_pkt.status], byte 0
679 .failed: mov si,err_udpinit
685 ; Common initialization code
688 %include "cpuinit.inc"
691 ; Now we're all set to start with our *real* business. First load the
692 ; configuration file (if any) and parse it.
694 ; In previous versions I avoided using 32-bit registers because of a
695 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
696 ; random. I figure, though, that if there are any of those still left
697 ; they probably won't be trying to install Linux on them...
699 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
700 ; to take'm out. In fact, we may want to put them back if we're going
701 ; to boot ELKS at some point.
705 ; Store standard filename prefix
707 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
716 lea si,[di-2] ; Skip final null!
719 cmp al,'.' ; Count . or - as alphanum
731 .alnum: loop .find_alnum
733 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
736 mov si,tftpprefix_msg
743 ; Load configuration file
748 ; Begin looking for configuration file
753 stosd ; The config file is always from the server
755 test byte [DHCPMagic], 02h
758 ; We got a DHCP option, try it first
761 ; mov di,ConfigName ; - already the case
775 ; Try loading by MAC address
776 ; Have to guess config file name
795 xchg ah,al ; Convert to host byte order
798 .hexify_loop: rol eax,4
810 mov cx,9 ; Up to 9 attempts
812 .tryagain: mov byte [di],0
818 rep movsb ; Copy "default" string
839 ; Now we have the config file open. Parse the config file and
840 ; run the user interface.
845 ; Linux kernel loading code is common. However, we need to define
846 ; a couple of helper macros...
849 ; Handle "ipappend" option
850 %define HAVE_SPECIAL_APPEND
851 %macro SPECIAL_APPEND 0
852 test byte [IPAppend],01h ; ip=
860 test byte [IPAppend],02h
864 mov byte [es:di-1],' ' ; Replace null with space
869 %define HAVE_UNLOAD_PREP
874 %include "runkernel.inc"
877 ; COMBOOT-loading code
879 %include "comboot.inc"
881 %include "cmdline.inc"
884 ; Boot sector loading code
886 %include "bootsect.inc"
889 ; Boot to the local disk by returning the appropriate PXE magic.
890 ; AX contains the appropriate return code.
894 mov ds,si ; Restore DI
896 mov [LocalBootType],ax
900 ; Restore the environment we were called with
907 mov ax,[cs:LocalBootType]
912 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
923 ac_kill: mov si,aborted_msg
926 ; abort_load: Called by various routines which wants to print a fatal
927 ; error message and return to the command prompt. Since this
928 ; may happen at just about any stage of the boot process, assume
929 ; our state is messed up, and just reset the segment registers
930 ; and the stack forcibly.
932 ; SI = offset (in _text) of error message to print
935 mov ax,cs ; Restore CS = DS = ES
940 call cwritestr ; Expects SI -> error msg
941 al_ok: jmp enter_command ; Return to command prompt
950 ; kaboom: write a message and bail out. Wait for quite a while,
951 ; or a user keypress, then do a hard reboot.
959 .patch: mov si,bailmsg
960 call writestr ; Returns with AL = 0
961 .drain: call pollchar
968 and al,09h ; Magic+Timeout
976 .wait2: mov dx,[BIOS_timer]
977 .wait3: call pollchar
988 mov word [BIOS_magic],0 ; Cold reboot
989 jmp 0F000h:0FFF0h ; Reset vector address
992 ; memory_scan_for_pxe_struct:
994 ; If none of the standard methods find the !PXE structure, look for it
995 ; by scanning memory.
998 ; CF = 0, ES:BX -> !PXE structure
999 ; Otherwise CF = 1, all registers saved
1001 memory_scan_for_pxe_struct:
1006 mov si,trymempxe_msg
1008 mov ax,[BIOS_fbm] ; Starting segment
1009 shl ax,(10-4) ; Kilobytes -> paragraphs
1010 ; mov ax,01000h ; Start to look here
1011 dec ax ; To skip inc ax
1014 cmp ax,0A000h ; End of memory
1023 movzx cx,byte [es:4] ; Length of structure
1024 cmp cl,08h ; Minimum length
1033 jnz .mismatch ; Checksum must == 0
1036 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1044 .not_found: mov si,notfound_msg
1052 ; memory_scan_for_pxenv_struct:
1054 ; If none of the standard methods find the PXENV+ structure, look for it
1055 ; by scanning memory.
1057 ; On exit, if found:
1058 ; CF = 0, ES:BX -> PXENV+ structure
1059 ; Otherwise CF = 1, all registers saved
1061 memory_scan_for_pxenv_struct:
1063 mov si,trymempxenv_msg
1065 ; mov ax,[BIOS_fbm] ; Starting segment
1066 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1067 mov ax,01000h ; Start to look here
1068 dec ax ; To skip inc ax
1071 cmp ax,0A000h ; End of memory
1080 movzx cx,byte [es:8] ; Length of structure
1081 cmp cl,26h ; Minimum length
1089 jnz .mismatch ; Checksum must == 0
1091 mov [bp+8],bx ; Save BX into stack frame
1097 .not_found: mov si,notfound_msg
1106 ; Open a TFTP connection to the server
1109 ; DS:DI = mangled filename
1112 ; SI = socket pointer
1113 ; DX:AX = file length in bytes
1126 call allocate_socket
1129 mov ax,PKT_RETRY ; Retry counter
1130 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1132 .sendreq: push ax ; [bp-2] - Retry counter
1133 push si ; [bp-4] - File name
1136 mov [pxe_udp_write_pkt.buffer],di
1138 mov ax,TFTP_RRQ ; TFTP opcode
1141 lodsd ; EAX <- server override (if any)
1143 jnz .noprefix ; No prefix, and we have the server
1145 push si ; Add common prefix
1151 mov eax,[ServerIP] ; Get default server
1154 call strcpy ; Filename
1156 mov [bx+tftp_remoteip],eax
1158 push bx ; [bp-6] - TFTP block
1160 push bx ; [bp-8] - TID (local port no)
1162 mov [pxe_udp_write_pkt.status],byte 0
1163 mov [pxe_udp_write_pkt.sip],eax
1164 ; Now figure out the gateway
1170 mov [pxe_udp_write_pkt.gip],eax
1171 mov [pxe_udp_write_pkt.lport],bx
1173 mov [pxe_udp_write_pkt.rport],ax
1175 mov cx,tftp_tail_len
1177 sub di,packet_buf ; Get packet size
1178 mov [pxe_udp_write_pkt.buffersize],di
1180 mov di,pxe_udp_write_pkt
1181 mov bx,PXENV_UDP_WRITE
1184 cmp word [pxe_udp_write_pkt.status],byte 0
1188 ; Danger, Will Robinson! We need to support timeout
1189 ; and retry lest we just lost a packet...
1192 ; Packet transmitted OK, now we need to receive
1193 .getpacket: push word [PktTimeout] ; [bp-10]
1194 push word [BIOS_timer] ; [bp-12]
1196 .pkt_loop: mov bx,[bp-8] ; TID
1198 mov word [pxe_udp_read_pkt.status],0
1199 mov [pxe_udp_read_pkt.buffer],di
1200 mov [pxe_udp_read_pkt.buffer+2],ds
1201 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1203 mov [pxe_udp_read_pkt.dip],eax
1204 mov [pxe_udp_read_pkt.lport],bx
1205 mov di,pxe_udp_read_pkt
1206 mov bx,PXENV_UDP_READ
1209 jz .got_packet ; Wait for packet
1215 dec word [bp-10] ; Timeout
1217 pop ax ; Adjust stack
1219 shl word [PktTimeout],1 ; Exponential backoff
1223 mov si,[bp-6] ; TFTP pointer
1226 mov eax,[si+tftp_remoteip]
1227 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1230 ; Got packet - reset timeout
1231 mov word [PktTimeout],PKT_TIMEOUT
1233 pop ax ; Adjust stack
1236 mov ax,[pxe_udp_read_pkt.rport]
1237 mov [si+tftp_remoteport],ax
1239 ; filesize <- -1 == unknown
1240 mov dword [si+tftp_filesize], -1
1241 ; Default blksize unless blksize option negotiated
1242 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1244 mov cx,[pxe_udp_read_pkt.buffersize]
1245 sub cx,2 ; CX <- bytes after opcode
1246 jb .failure ; Garbled reply
1252 je .bailnow ; ERROR reply: don't try again
1257 ; Now we need to parse the OACK packet to get the transfer
1258 ; size. SI -> first byte of options; CX -> byte count
1260 jcxz .no_tsize ; No options acked
1264 .opt_name_loop: lodsb
1267 or al,20h ; Convert to lowercase
1270 ; We ran out, and no final null
1272 .got_opt_name: ; si -> option value
1273 dec cx ; bytes left in pkt
1274 jz .err_reply ; Option w/o value
1276 ; Parse option pointed to by bx; guaranteed to be
1280 mov si,bx ; -> option name
1281 mov bx,tftp_opt_table
1286 mov di,[bx] ; Option pointer
1287 mov cx,[bx+2] ; Option len
1291 je .get_value ; OK, known option
1297 jmp .err_reply ; Non-negotiated option returned
1299 .get_value: pop si ; si -> option value
1300 pop cx ; cx -> bytes left in pkt
1301 mov bx,[bx+4] ; Pointer to data target
1302 add bx,[bp-6] ; TFTP socket pointer
1310 ja .err_reply ; Not a decimal digit
1315 ; Ran out before final null, accept anyway
1320 jnz .get_opt_name ; Not end of packet
1327 pop si ; We want the packet ptr in SI
1329 mov eax,[si+tftp_filesize]
1333 shr edx,16 ; DX:AX == EAX
1335 and eax,eax ; Set ZF depending on file size
1337 pop bp ; Junk (retry counter)
1338 jz .error_si ; ZF = 1 need to free the socket
1344 .err_reply: ; Option negotiation error. Send ERROR reply.
1345 ; ServerIP and gateway are already programmed in
1347 mov ax,[si+tftp_remoteport]
1348 mov word [pxe_udp_write_pkt.rport],ax
1349 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1350 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1351 mov di,pxe_udp_write_pkt
1352 mov bx,PXENV_UDP_WRITE
1355 ; Write an error message and explode
1360 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1362 .failure: pop bx ; Junk
1366 dec ax ; Retry counter
1367 jnz .sendreq ; Try again
1369 .error: mov si,bx ; Socket pointer
1370 .error_si: ; Socket pointer already in SI
1371 call free_socket ; ZF <- 1, SI <- 0
1377 ; allocate_socket: Allocate a local UDP port structure
1381 ; BX = socket pointer
1389 .check: cmp word [bx], byte 0
1391 add bx,open_file_t_size
1396 ; Allocate a socket number. Socket numbers are made
1397 ; guaranteed unique by including the socket slot number
1398 ; (inverted, because we use the loop counter cx); add a
1399 ; counter value to keep the numbers from being likely to
1400 ; get immediately reused.
1402 ; The NextSocket variable also contains the top two bits
1403 ; set. This generates a value in the range 49152 to
1410 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1412 shl cx,13-MAX_OPEN_LG2
1414 xchg ch,cl ; Convert to network byte order
1415 mov [bx],cx ; Socket in use
1421 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1429 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1438 ; Read a dot-quad pathname in DS:SI and output an IP
1439 ; address in EAX, with SI pointing to the first
1440 ; nonmatching character.
1442 ; Return CF=1 on error.
1456 aad ; AL += 10 * AH; AH = 0;
1471 loop .realerror ; If CX := 1 then we're done
1477 dec si ; CF unchanged!
1481 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1482 ; to by ES:DI; ends on encountering any whitespace.
1484 ; This verifies that a filename is < FILENAME_MAX characters
1485 ; and doesn't contain whitespace, and zero-pads the output buffer,
1486 ; so "repe cmpsb" can do a compare.
1488 ; The first four bytes of the manged name is the IP address of
1489 ; the download host.
1495 je .noip ; Null filename?!?!
1496 cmp word [si],'::' ; Leading ::?
1506 ; We have a :: prefix of some sort, it could be either
1507 ; a DNS name or a dot-quad IP address. Try the dot-quad
1531 pop cx ; Adjust stack
1532 inc si ; Skip double colon
1536 stosd ; Save IP address prefix
1537 mov cx,FILENAME_MAX-5
1541 cmp al,' ' ; If control or space, end
1546 inc cx ; At least one null byte
1547 xor ax,ax ; Zero-fill name
1548 rep stosb ; Doesn't do anything if CX=0
1552 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1553 ; filename to the conventional representation. This is needed
1554 ; for the BOOT_IMAGE= parameter for the kernel.
1555 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1556 ; known to be shorter.
1558 ; DS:SI -> input mangled file name
1559 ; ES:DI -> output buffer
1561 ; On return, DI points to the first byte after the output name,
1562 ; which is set to a null byte.
1574 dec di ; Point to final null byte
1581 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1582 ; calling convention. This is a separate local routine so
1583 ; we can hook special things from it if necessary.
1587 .jump: call 0:pxe_thunk ; Default to calling the thunk
1588 cld ; Make sure DF <- 0
1591 ; Must be after function def due to NASM bug
1592 PXENVEntry equ pxenv.jump+1
1597 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1598 ; calling convention (using the stack.)
1600 ; This is called as a far routine so that we can just stick it into
1601 ; the PXENVEntry variable.
1609 cmc ; Set CF unless ax == 0
1612 ; Must be after function def due to NASM bug
1613 PXEEntry equ pxe_thunk.jump+1
1616 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1618 ; In this case, get multiple blocks from a specific TCP connection.
1622 ; SI -> TFTP socket pointer
1623 ; CX -> 512-byte block count; 0FFFFh = until end of file
1625 ; SI -> TFTP socket pointer (or 0 on EOF)
1636 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1637 jz .hit_eof ; Nothing to do?
1642 movzx eax,word [bx+tftp_bytesleft]
1646 jcxz .need_packet ; No bytes available?
1649 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1650 mov si,[bx+tftp_dataptr]
1651 sub [bx+tftp_bytesleft],cx
1652 fs rep movsb ; Copy from packet buffer
1653 mov [bx+tftp_dataptr],si
1664 ; Is there anything left of this?
1665 mov eax,[si+tftp_filesize]
1666 sub eax,[si+tftp_filepos]
1667 jnz .bytes_left ; CF <- 0
1669 cmp [si+tftp_bytesleft],ax
1670 jnz .bytes_left ; CF <- 0
1672 ; The socket is closed and the buffer drained
1673 ; Close socket structure and re-init for next user
1680 ; No data in buffer, check to see if we can get a packet...
1684 mov eax,[bx+tftp_filesize]
1685 cmp eax,[bx+tftp_filepos]
1686 je .hit_eof ; Already EOF'd; socket already closed
1698 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1705 ; Start by ACKing the previous packet; this should cause the
1706 ; next packet to be sent.
1708 mov word [PktTimeout],PKT_TIMEOUT
1710 .send_ack: push cx ; <D> Retry count
1712 mov ax,[si+tftp_lastpkt]
1713 call ack_packet ; Send ACK
1715 ; We used to test the error code here, but sometimes
1716 ; PXE would return negative status even though we really
1717 ; did send the ACK. Now, just treat a failed send as
1718 ; a normally lost packet, and let it time out in due
1721 .send_ok: ; Now wait for packet.
1722 mov dx,[BIOS_timer] ; Get current time
1725 .wait_data: push cx ; <E> Timeout
1726 push dx ; <F> Old time
1728 mov bx,[si+tftp_pktbuf]
1729 mov [pxe_udp_read_pkt.buffer],bx
1730 mov [pxe_udp_read_pkt.buffer+2],fs
1731 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1732 mov eax,[si+tftp_remoteip]
1733 mov [pxe_udp_read_pkt.sip],eax
1735 mov [pxe_udp_read_pkt.dip],eax
1736 mov ax,[si+tftp_remoteport]
1737 mov [pxe_udp_read_pkt.rport],ax
1738 mov ax,[si+tftp_localport]
1739 mov [pxe_udp_read_pkt.lport],ax
1740 mov di,pxe_udp_read_pkt
1741 mov bx,PXENV_UDP_READ
1748 ; No packet, or receive failure
1750 pop ax ; <F> Old time
1751 pop cx ; <E> Timeout
1752 cmp ax,dx ; Same time -> don't advance timeout
1753 je .wait_data ; Same clock tick
1754 loop .wait_data ; Decrease timeout
1756 pop cx ; <D> Didn't get any, send another ACK
1757 shl word [PktTimeout],1 ; Exponential backoff
1759 jmp kaboom ; Forget it...
1761 .recv_ok: pop dx ; <F>
1764 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1765 jb .wait_data ; Bad size for a DATA packet
1767 mov bx,[si+tftp_pktbuf]
1768 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1769 jne .wait_data ; Then wait for something else
1771 mov ax,[si+tftp_lastpkt]
1772 xchg ah,al ; Host byte order
1773 inc ax ; Which packet are we waiting for?
1774 xchg ah,al ; Network byte order
1778 ; Wrong packet, ACK the packet and then try again
1779 ; This is presumably because the ACK got lost,
1780 ; so the server just resent the previous packet
1783 jmp .send_ok ; Reset timeout
1785 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1787 pop cx ; <D> Don't need the retry count anymore
1789 mov [si+tftp_lastpkt],ax ; Update last packet number
1791 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1792 sub cx,byte 4 ; Skip TFTP header
1794 ; If this is a zero-length block, don't mess with the pointers,
1795 ; since we may have just set up the previous block that way
1798 ; Set pointer to data block
1799 lea ax,[bx+4] ; Data past TFTP header
1800 mov [si+tftp_dataptr],ax
1802 add [si+tftp_filepos],ecx
1803 mov [si+tftp_bytesleft],cx
1805 cmp cx,[si+tftp_blksize] ; Is it a full block?
1806 jb .last_block ; If so, it's not EOF
1808 ; If we had the exact right number of bytes, always get
1809 ; one more packet to get the (zero-byte) EOF packet and
1811 mov eax,[si+tftp_filepos]
1812 cmp [si+tftp_filesize],eax
1818 .last_block: ; Last block - ACK packet immediately
1822 ; Make sure we know we are at end of file
1823 mov eax,[si+tftp_filepos]
1824 mov [si+tftp_filesize],eax
1831 ; Send ACK packet. This is a common operation and so is worth canning.
1835 ; AX = Packet # to ack (network byte order)
1838 ; All registers preserved
1840 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1844 mov [ack_packet_buf+2],ax ; Packet number to ack
1846 mov [pxe_udp_write_pkt.lport],ax
1847 mov ax,[si+tftp_remoteport]
1848 mov [pxe_udp_write_pkt.rport],ax
1849 mov eax,[si+tftp_remoteip]
1850 mov [pxe_udp_write_pkt.sip],eax
1856 mov [pxe_udp_write_pkt.gip],eax
1857 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1858 mov [pxe_udp_write_pkt.buffersize], word 4
1859 mov di,pxe_udp_write_pkt
1860 mov bx,PXENV_UDP_WRITE
1862 cmp ax,byte 0 ; ZF = 1 if write OK
1869 ; This function unloads the PXE and UNDI stacks and unclaims
1873 test byte [KeepPXE],01h ; Should we keep PXE around?
1883 mov si,new_api_unload
1884 cmp byte [APIVer+1],2 ; Major API version >= 2?
1886 mov si,old_api_unload
1889 .call_loop: xor ax,ax
1894 mov di,pxe_unload_stack_pkt
1897 mov cx,pxe_unload_stack_pkt_len >> 1
1902 mov ax,word [pxe_unload_stack_pkt.status]
1903 cmp ax,PXENV_STATUS_SUCCESS
1909 ; This isn't necessary anymore; we can use the memory area previously
1910 ; used by the PXE stack indefinitely, and the chainload code sets up
1911 ; a new stack independently. Leave the source code in here for now,
1912 ; but expect to rip it out soonish.
1914 %if 0 ; USE_PXE_PROVIDED_STACK
1915 ; We need to switch to our local stack here...
1923 sub ax,[BaseStack+4] ; Are we using the base stack
1924 je .is_base_stack ; (as opposed to the COMBOOT stack)?
1926 lgs si,[SavedSSSP] ; COMBOOT stack
1932 mov [BaseStack+4],es
1935 mov [SavedSSSP],di ; New SP
1936 mov [SavedSSSP+2],es
1939 and ax,ax ; Remember which stack
1942 ; Update the base stack pointer since it's in use
1952 mov dx,[RealBaseMem]
1953 cmp dx,[BIOS_fbm] ; Sanity check
1957 ; Check that PXE actually unhooked the INT 1Ah chain
1958 movzx eax,word [4*0x1a]
1959 movzx ecx,word [4*0x1a+2]
1963 cmp ax,dx ; Not in range
1977 mov si,cant_free_msg
1993 ; We want to keep PXE around, but still we should reset
1994 ; it to the standard bootup configuration
1999 mov bx,PXENV_UDP_CLOSE
2000 mov di,pxe_udp_close_pkt
2008 ; Take an IP address (in network byte order) in EAX and
2009 ; output a dotted quad string to ES:DI.
2010 ; DI points to terminal null at end of string on exit.
2019 jb .lt10 ; If so, skip first 2 digits
2022 jb .lt100 ; If so, skip first digit
2025 ; Now AH = 100-digit; AL = remainder
2032 ; Now AH = 10-digit; AL = remainder
2043 ror eax,8 ; Move next char into LSB
2054 ; Parse a DHCP packet. This includes dealing with "overloaded"
2055 ; option fields (see RFC 2132, section 9.3)
2057 ; This should fill in the following global variables, if the
2058 ; information is present:
2060 ; MyIP - client IP address
2061 ; ServerIP - boot server IP address
2062 ; Netmask - network mask
2063 ; Gateway - default gateway router IP
2064 ; BootFile - boot file name
2065 ; DNSServers - DNS server IPs
2066 ; LocalDomain - Local domain name
2068 ; This assumes the DHCP packet is in "trackbuf" and the length
2069 ; of the packet in in CX on entry.
2073 mov byte [OverLoad],0 ; Assume no overload
2074 mov eax, [trackbuf+bootp.yip]
2077 cmp al,224 ; Class D or higher -> bad
2081 mov eax, [trackbuf+bootp.sip]
2084 cmp al,224 ; Class D or higher -> bad
2088 sub cx, bootp.options
2090 mov si, trackbuf+bootp.option_magic
2092 cmp eax, BOOTP_OPTION_MAGIC
2094 call parse_dhcp_options
2096 mov si, trackbuf+bootp.bootfile
2097 test byte [OverLoad],1
2100 call parse_dhcp_options
2101 jmp short .parsed_file
2104 jz .parsed_file ; No bootfile name
2109 stosb ; Null-terminate
2111 mov si, trackbuf+bootp.sname
2112 test byte [OverLoad],2
2115 call parse_dhcp_options
2120 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2121 ; size is CX -- some DHCP servers leave option fields unterminated
2122 ; in violation of the spec.
2124 ; For parse_some_dhcp_options, DH contains the minimum value for
2125 ; the option to recognize -- this is used to restrict parsing to
2126 ; PXELINUX-specific options only.
2131 parse_some_dhcp_options:
2138 jz .done ; Last byte; must be PAD, END or malformed
2139 cmp al, 0 ; PAD option
2141 cmp al,255 ; END option
2144 ; Anything else will have a length field
2145 mov dl,al ; DL <- option number
2147 lodsb ; AX <- option length
2149 sub cx,ax ; Decrement bytes left counter
2150 jb .done ; Malformed option: length > field size
2152 cmp dl,dh ; Is the option value valid?
2155 cmp dl,1 ; SUBNET MASK option
2162 cmp dl,3 ; ROUTER option
2169 cmp dl,6 ; DNS SERVERS option
2174 cmp cl,DNS_MAX_SERVERS
2176 mov cl,DNS_MAX_SERVERS
2180 mov [LastDNSServer],di
2185 cmp dl,15 ; DNS LOCAL DOMAIN option
2186 jne .not_localdomain
2191 xchg [bx],al ; Zero-terminate option
2193 call dns_mangle ; Convert to DNS label set
2194 mov [bx],al ; Restore ending byte
2199 cmp dl,43 ; VENDOR ENCAPSULATED option
2202 mov dh,208 ; Only recognize PXELINUX options
2203 mov cx,ax ; Length of option = max bytes to parse
2204 call parse_some_dhcp_options ; Parse recursive structure
2209 cmp dl,52 ; OPTION OVERLOAD option
2216 cmp dl,67 ; BOOTFILE NAME option
2219 jmp short .copyoption
2221 ret ; This is here to make short jumps easier
2224 cmp dl,208 ; PXELINUX MAGIC option
2226 cmp al,4 ; Must have length == 4
2228 cmp dword [si], htonl(0xF100747E) ; Magic number
2230 or byte [DHCPMagic], byte 1 ; Found magic #
2234 cmp dl,209 ; PXELINUX CONFIGFILE option
2237 or byte [DHCPMagic], byte 2 ; Got config file
2238 jmp short .copyoption
2241 cmp dl,210 ; PXELINUX PATHPREFIX option
2244 or byte [DHCPMagic], byte 4 ; Got path prefix
2245 jmp short .copyoption
2248 cmp dl,211 ; PXELINUX REBOOTTIME option
2253 xchg bl,bh ; Convert to host byte order
2256 mov [RebootTime],ebx
2257 or byte [DHCPMagic], byte 8 ; Got RebootTime
2258 ; jmp short .opt_done
2261 ; Unknown option. Skip to the next one.
2267 ; Common code for copying an option verbatim
2271 xchg cx,ax ; Now ax == 0
2272 stosb ; Null-terminate
2273 jmp short .opt_done_noskip
2278 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2279 ; option into IPOption based on a DHCP packet in trackbuf.
2280 ; Assumes CS == DS == ES.
2301 call gendotquad ; Zero-terminates its output
2303 mov [IPOptionLen],di
2308 ; Call the receive loop while idle. This is done mostly so we can respond to
2309 ; ARP messages, but perhaps in the future this can be used to do network
2312 ; hpa sez: people using automatic control on the serial port get very
2313 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2314 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2315 ; passed since the last poll, and reset this when a character is
2316 ; received (RESET_IDLE).
2320 mov ax,[cs:BIOS_timer]
2321 mov [cs:IdleTimer],ax
2327 mov ax,[cs:BIOS_timer]
2328 sub ax,[cs:IdleTimer]
2340 mov [pxe_udp_read_pkt.status],al ; 0
2341 mov [pxe_udp_read_pkt.buffer],di
2342 mov [pxe_udp_read_pkt.buffer+2],ds
2343 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2345 mov [pxe_udp_read_pkt.dip],eax
2346 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2347 mov di,pxe_udp_read_pkt
2348 mov bx,PXENV_UDP_READ
2357 ; -----------------------------------------------------------------------------
2359 ; -----------------------------------------------------------------------------
2361 %include "getc.inc" ; getc et al
2362 %include "conio.inc" ; Console I/O
2363 %include "writestr.inc" ; String output
2364 writestr equ cwritestr
2365 %include "writehex.inc" ; Hexadecimal output
2366 %include "parseconfig.inc" ; High-level config file handling
2367 %include "parsecmd.inc" ; Low-level config file handling
2368 %include "bcopy32.inc" ; 32-bit bcopy
2369 %include "loadhigh.inc" ; Load a file into high memory
2370 %include "font.inc" ; VGA font stuff
2371 %include "graphics.inc" ; VGA graphics
2372 %include "highmem.inc" ; High memory sizing
2373 %include "strcpy.inc" ; strcpy()
2374 %include "rawcon.inc" ; Console I/O w/o using the console functions
2375 %include "dnsresolv.inc" ; DNS resolver
2377 ; -----------------------------------------------------------------------------
2378 ; Begin data section
2379 ; -----------------------------------------------------------------------------
2383 hextbl_lower db '0123456789abcdef'
2384 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2386 boot_prompt db 'boot: ', 0
2387 wipe_char db BS, ' ', BS, 0
2388 err_notfound db 'Could not find kernel image: ',0
2389 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2390 err_noram db 'It appears your computer has less than '
2392 db 'K of low ("DOS")'
2394 db 'RAM. Linux needs at least this amount to boot. If you get'
2396 db 'this message in error, hold down the Ctrl key while'
2398 db 'booting, and I will take your word for it.', CR, LF, 0
2399 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2400 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2401 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2402 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2403 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2404 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2406 err_notdos db ': attempted DOS system call', CR, LF, 0
2407 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2408 err_bssimage db 'BSS images not supported.', CR, LF, 0
2409 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2410 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2411 bailmsg equ err_bootfailed
2412 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2413 err_pxefailed db 'PXE API call failed, error ', 0
2414 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2415 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2416 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2417 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2418 apiver_str db 'PXE API version is ',0
2419 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2420 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2421 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2422 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2423 undi_data_msg db 'UNDI data segment at: ',0
2424 undi_data_len_msg db 'UNDI data segment size: ',0
2425 undi_code_msg db 'UNDI code segment at: ',0
2426 undi_code_len_msg db 'UNDI code segment size: ',0
2427 cant_free_msg db 'Failed to free base memory, error ', 0
2428 notfound_msg db 'not found', CR, LF, 0
2429 myipaddr_msg db 'My IP address seems to be ',0
2430 tftpprefix_msg db 'TFTP prefix: ', 0
2431 localboot_msg db 'Booting from local disk...', CR, LF, 0
2432 cmdline_msg db 'Command line: ', CR, LF, 0
2433 ready_msg db 'Ready.', CR, LF, 0
2434 trying_msg db 'Trying to load: ', 0
2435 crlfloading_msg db CR, LF ; Fall through
2436 loading_msg db 'Loading ', 0
2439 fourbs_msg db BS, BS, BS, BS, 0
2440 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2443 crff_msg db CR, FF, 0
2444 default_str db 'default', 0
2445 default_len equ ($-default_str)
2446 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2447 cfgprefix db 'pxelinux.cfg/' ; No final null!
2448 cfgprefix_len equ ($-cfgprefix)
2451 ; Command line options we'd like to take a look at
2453 ; mem= and vga= are handled as normal 32-bit integer values
2454 initrd_cmd db 'initrd='
2455 initrd_cmd_len equ $-initrd_cmd
2457 ; This one we make ourselves
2458 bootif_str db 'BOOTIF='
2459 bootif_str_len equ $-bootif_str
2461 ; Config file keyword table
2463 %include "keywords.inc"
2466 ; Extensions to search for (in *forward* order).
2467 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2470 exten_table: db '.cbt' ; COMBOOT (specific)
2471 db '.0', 0, 0 ; PXE bootstrap program
2472 db '.com' ; COMBOOT (same as DOS)
2475 dd 0, 0 ; Need 8 null bytes here
2478 ; PXE unload sequences
2482 db PXENV_UNDI_SHUTDOWN
2483 db PXENV_UNLOAD_STACK
2488 db PXENV_UNDI_SHUTDOWN
2489 db PXENV_UNLOAD_STACK
2490 db PXENV_UNDI_CLEANUP
2494 ; PXE query packets partially filled in
2496 pxe_bootp_query_pkt_2:
2497 .status: dw 0 ; Status
2498 .packettype: dw 2 ; DHCPACK packet
2499 .buffersize: dw trackbufsize ; Packet size
2500 .buffer: dw trackbuf, 0 ; seg:off of buffer
2501 .bufferlimit: dw trackbufsize ; Unused
2503 pxe_bootp_query_pkt_3:
2504 .status: dw 0 ; Status
2505 .packettype: dw 3 ; Boot server packet
2506 .buffersize: dw trackbufsize ; Packet size
2507 .buffer: dw trackbuf, 0 ; seg:off of buffer
2508 .bufferlimit: dw trackbufsize ; Unused
2510 pxe_bootp_size_query_pkt:
2511 .status: dw 0 ; Status
2512 .packettype: dw 2 ; DHCPACK packet
2513 .buffersize: dw 0 ; Packet size
2514 .buffer: dw 0, 0 ; seg:off of buffer
2515 .bufferlimit: dw 0 ; Unused
2518 .status: dw 0 ; Status
2519 .sip: dd 0 ; Source (our) IP
2522 .status: dw 0 ; Status
2525 .status: dw 0 ; Status
2526 .sip: dd 0 ; Server IP
2527 .gip: dd 0 ; Gateway IP
2528 .lport: dw 0 ; Local port
2529 .rport: dw 0 ; Remote port
2530 .buffersize: dw 0 ; Size of packet
2531 .buffer: dw 0, 0 ; seg:off of buffer
2534 .status: dw 0 ; Status
2535 .sip: dd 0 ; Source IP
2536 .dip: dd 0 ; Destination (our) IP
2537 .rport: dw 0 ; Remote port
2538 .lport: dw 0 ; Local port
2539 .buffersize: dw 0 ; Max packet size
2540 .buffer: dw 0, 0 ; seg:off of buffer
2543 ; Misc initialized (data) variables
2546 BaseStack dd StackBuf ; SS:ESP of base stack
2547 NextSocket dw 49152 ; Counter for allocating socket numbers
2548 KeepPXE db 0 ; Should PXE be kept around?
2553 tftp_tail db 'octet', 0 ; Octet mode
2554 tsize_str db 'tsize' ,0 ; Request size
2555 tsize_len equ ($-tsize_str)
2557 blksize_str db 'blksize', 0 ; Request large blocks
2558 blksize_len equ ($-blksize_str)
2559 asciidec TFTP_LARGEBLK
2561 tftp_tail_len equ ($-tftp_tail)
2565 ; Options negotiation parsing table (string pointer, string len, offset
2566 ; into socket structure)
2569 dw tsize_str, tsize_len, tftp_filesize
2570 dw blksize_str, blksize_len, tftp_blksize
2571 tftp_opts equ ($-tftp_opt_table)/6
2574 ; Error packet to return on options negotiation error
2576 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2577 dw TFTP_EOPTNEG ; ERROR 8: bad options
2578 db 'tsize option required', 0 ; Error message
2579 tftp_opt_err_len equ ($-tftp_opt_err)
2582 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2585 ; IP information (initialized to "unknown" values)
2586 MyIP dd 0 ; My IP address
2587 ServerIP dd 0 ; IP address of boot server
2588 Netmask dd 0 ; Netmask of this subnet
2589 Gateway dd 0 ; Default router
2590 ServerPort dw TFTP_PORT ; TFTP server port
2593 ; Variables that are uninitialized in SYSLINUX but initialized here
2596 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2597 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2598 BufSafeBytes dw trackbufsize ; = how many bytes?
2599 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2601 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2602 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2605 IPAppend db 0 ; Default IPAPPEND option
2606 DHCPMagic db 0 ; DHCP site-specific option info