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 1994-2008 H. Peter Anvin - All Rights Reserved
12 ; This program is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
15 ; Boston MA 02111-1307, USA; either version 2 of the License, or
16 ; (at your option) any later version; incorporated herein by reference.
18 ; ****************************************************************************
24 ; gPXE extensions support
28 ; Some semi-configurable constants... change on your own risk.
31 FILENAME_MAX_LG2
equ 7 ; log2(Max filename size Including final null)
32 FILENAME_MAX
equ (1 << FILENAME_MAX_LG2
)
33 NULLFILE
equ 0 ; Zero byte == null file name
34 NULLOFFSET
equ 4 ; Position in which to look
35 REBOOT_TIME
equ 5*60 ; If failure, time until full reset
36 %assign HIGHMEM_SLOP
128*1024 ; Avoid this much memory near the top
37 MAX_OPEN_LG2
equ 5 ; log2(Max number of open sockets)
38 MAX_OPEN
equ (1 << MAX_OPEN_LG2
)
39 PKTBUF_SIZE
equ (65536/MAX_OPEN
) ; Per-socket packet buffer size
40 TFTP_PORT
equ htons
(69) ; Default TFTP port
41 PKT_RETRY
equ 6 ; Packet transmit retry count
42 PKT_TIMEOUT
equ 12 ; Initial timeout, timer ticks @ 55 ms
43 ; Desired TFTP block size
44 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
45 ; be a fair number of networks with "substandard" MTUs which break.
46 ; The code assumes TFTP_LARGEBLK <= 2K.
48 TFTP_LARGEBLK
equ (TFTP_MTU
-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
49 ; Standard TFTP block size
50 TFTP_BLOCKSIZE_LG2
equ 9 ; log2(bytes/block)
51 TFTP_BLOCKSIZE
equ (1 << TFTP_BLOCKSIZE_LG2
)
52 %assign USE_PXE_PROVIDED_STACK
1 ; Use stack provided by PXE?
54 SECTOR_SHIFT
equ TFTP_BLOCKSIZE_LG2
55 SECTOR_SIZE
equ TFTP_BLOCKSIZE
58 ; This is what we need to do when idle
59 ; *** This is disabled because some PXE stacks wait for unacceptably
60 ; *** long if there are no packets receivable.
62 %define HAVE_IDLE
0 ; idle is not a noop
81 ; TFTP operation codes
83 TFTP_RRQ
equ htons
(1) ; Read request
84 TFTP_WRQ
equ htons
(2) ; Write request
85 TFTP_DATA
equ htons
(3) ; Data packet
86 TFTP_ACK
equ htons
(4) ; ACK packet
87 TFTP_ERROR
equ htons
(5) ; ERROR packet
88 TFTP_OACK
equ htons
(6) ; OACK packet
93 TFTP_EUNDEF
equ htons
(0) ; Unspecified error
94 TFTP_ENOTFOUND
equ htons
(1) ; File not found
95 TFTP_EACCESS
equ htons
(2) ; Access violation
96 TFTP_ENOSPACE
equ htons
(3) ; Disk full
97 TFTP_EBADOP
equ htons
(4) ; Invalid TFTP operation
98 TFTP_EBADID
equ htons
(5) ; Unknown transfer
99 TFTP_EEXISTS
equ htons
(6) ; File exists
100 TFTP_ENOUSER
equ htons
(7) ; No such user
101 TFTP_EOPTNEG
equ htons
(8) ; Option negotiation failure
104 ; The following structure is used for "virtual kernels"; i.e. LILO-style
105 ; option labels. The options we permit here are `kernel' and `append
106 ; Since there is no room in the bottom 64K for all of these, we
107 ; stick them in high memory and copy them down before we need them.
110 vk_vname: resb FILENAME_MAX
; Virtual name **MUST BE FIRST!**
111 vk_rname: resb FILENAME_MAX
; Real name
112 vk_ipappend: resb
1 ; "IPAPPEND" flag
113 vk_type: resb
1 ; Type of file
116 vk_append: resb max_cmd_len
+1 ; Command line
118 vk_end: equ $
; Should be <= vk_size
122 ; Segment assignments in the bottom 640K
123 ; 0000h - main code/data segment (and BIOS segment)
125 real_mode_seg
equ 3000h
126 pktbuf_seg
equ 2000h ; Packet buffers segments
127 xfer_buf_seg
equ 1000h ; Bounce buffer for I/O to high mem
128 comboot_seg
equ real_mode_seg
; COMBOOT image loading zone
131 ; BOOTP/DHCP packet pattern
135 .opcode resb
1 ; BOOTP/DHCP "opcode"
136 .hardware resb
1 ; ARP hardware type
137 .hardlen resb
1 ; Hardware address length
138 .gatehops resb
1 ; Used by forwarders
139 .ident resd
1 ; Transaction ID
140 .seconds resw
1 ; Seconds elapsed
141 .flags resw
1 ; Broadcast flags
142 .cip resd
1 ; Client IP
143 .yip resd
1 ; "Your" IP
144 .sip resd
1 ; Next server IP
145 .gip resd
1 ; Relay agent IP
146 .macaddr resb
16 ; Client MAC address
147 .sname resb
64 ; Server name (optional)
148 .bootfile resb
128 ; Boot file name
149 .option_magic resd
1 ; Vendor option magic cookie
150 .options resb
1260 ; Vendor options
153 BOOTP_OPTION_MAGIC
equ htonl
(0x63825363) ; See RFC 2132
156 ; TFTP connection data structure. Each one of these corresponds to a local
157 ; UDP port. The size of this structure must be a power of 2.
158 ; HBO = host byte order; NBO = network byte order
159 ; (*) = written by options negotiation code, must be dword sized
161 ; For a gPXE connection, we set the local port number to -1 and the
162 ; remote port number contains the gPXE file handle.
165 tftp_localport resw
1 ; Local port number (0 = not in use)
166 tftp_remoteport resw
1 ; Remote port number
167 tftp_remoteip resd
1 ; Remote IP address
168 tftp_filepos resd
1 ; Bytes downloaded (including buffer)
169 tftp_filesize resd
1 ; Total file size(*)
170 tftp_blksize resd
1 ; Block size for this connection(*)
171 tftp_bytesleft resw
1 ; Unclaimed data bytes
172 tftp_lastpkt resw
1 ; Sequence number of last packet (NBO)
173 tftp_dataptr resw
1 ; Pointer to available data
174 tftp_goteof resb
1 ; 1 if the EOF packet received
175 resb
3 ; Currently unusued
176 ; At end since it should not be zeroed on socked close
177 tftp_pktbuf resw
1 ; Packet buffer offset
180 %if
(open_file_t_size
& (open_file_t_size
-1))
181 %error
"open_file_t is not a power of 2"
185 ; ---------------------------------------------------------------------------
187 ; ---------------------------------------------------------------------------
190 ; Memory below this point is reserved for the BIOS and the MBR
193 trackbufsize
equ 8192
194 trackbuf resb trackbufsize
; Track buffer goes here
197 alignb open_file_t_size
198 Files resb MAX_OPEN
*open_file_t_size
201 BootFile resb
256 ; Boot file from DHCP packet
202 PathPrefix resb
256 ; Path prefix derived from boot file
203 DotQuadBuf resb
16 ; Buffer for dotted-quad IP address
204 IPOption resb
80 ; ip= option buffer
205 InitStack resd
1 ; Pointer to reset stack (SS:SP)
206 PXEStack resd
1 ; Saved stack during PXE call
210 RebootTime resd
1 ; Reboot timeout, if set by option
211 StrucPtr resd
1 ; Pointer to PXENV+ or !PXE structure
212 APIVer resw
1 ; PXE API version found
213 IPOptionLen resw
1 ; Length of IPOption
214 IdleTimer resw
1 ; Time to check for ARP?
215 LocalBootType resw
1 ; Local boot return code
216 PktTimeout resw
1 ; Timeout for current packet
217 RealBaseMem resw
1 ; Amount of DOS memory after freeing
218 OverLoad resb
1 ; Set if DHCP packet uses "overloading"
219 DHCPMagic resb
1 ; PXELINUX magic flags
221 ; The relative position of these fields matter!
222 MAC_MAX
equ 32 ; Handle hardware addresses this long
223 MACLen resb
1 ; MAC address len
224 MACType resb
1 ; MAC address type
225 MAC resb MAC_MAX
+1 ; Actual MAC address
226 BOOTIFStr resb
7 ; Space for "BOOTIF="
227 MACStr resb
3*(MAC_MAX
+1) ; MAC address as a string
229 ; The relative position of these fields matter!
230 UUIDType resb
1 ; Type byte from DHCP option
231 UUID resb
16 ; UUID, from the PXE stack
232 UUIDNull resb
1 ; dhcp_copyoption zero-terminates
235 ; PXE packets which don't need static initialization
238 pxe_unload_stack_pkt:
239 .
status: resw
1 ; Status
240 .
reserved: resw
10 ; Reserved
241 pxe_unload_stack_pkt_len
equ $
-pxe_unload_stack_pkt
244 ; BOOTP/DHCP packet buffer
248 packet_buf resb
2048 ; Transfer packet
249 packet_buf_size
equ $
-packet_buf
253 ; PXELINUX needs more BSS than the other derivatives;
254 ; therefore we relocate it from 7C00h on startup.
256 StackBuf
equ $
; Base of stack if we use our own
259 ; Primary entry point.
263 pushfd ; Paranoia... in case of return to PXE
264 pushad ; ... save as much state as possible
275 %if TEXT_START
!= 0x7c00
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
287 jmp 0:_start1
; Canonicalize address
290 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
292 ; That is all pushed onto the PXE stack. Save the pointer
293 ; to it and switch to an internal stack.
297 %if USE_PXE_PROVIDED_STACK
298 ; Apparently some platforms go bonkers if we
299 ; set up our own stack...
307 sti ; Stack set up and ready
311 ; Initialize screen (if we're using one)
313 push es ; Save ES -> PXE entry structure
317 pop es ; Restore ES -> PXE entry structure
319 ; Tell the user we got this far
321 mov si,syslinux_banner
328 ; Assume API version 2.1, in case we find the !PXE structure without
329 ; finding the PXENV+ structure. This should really look at the Base
330 ; Code ROM ID structure in have_pxe, but this is adequate for now --
331 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
332 ; about higher versions than that.
334 mov word [APIVer
],0201h
337 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
338 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
339 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
340 ; We should make that the second test, and not trash ES:BX...
342 cmp dword [es:bx], '!PXE'
345 ; Uh-oh, not there... try plan B
347 %if USE_PXE_PROVIDED_STACK
== 0
350 int 1Ah ; May trash regs
351 %if USE_PXE_PROVIDED_STACK
== 0
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
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 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
527 ; packet (query info 1).
531 call pxe_get_cached_info
534 ; We don't use flags from the request packet, so
535 ; this is a good time to initialize DHCPMagic...
536 ; Initialize it to 1 meaning we will accept options found;
537 ; in earlier versions of PXELINUX bit 0 was used to indicate
538 ; we have found option 208 with the appropriate magic number;
539 ; we no longer require that, but MAY want to re-introduce
540 ; it in the future for vendor encapsulated options.
541 mov byte [DHCPMagic
],1
544 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
545 ; address). This lives in the DHCPACK packet (query info 2).
549 call pxe_get_cached_info
550 call parse_dhcp
; Parse DHCP packet
552 ; Save away MAC address (assume this is in query info 2. If this
553 ; turns out to be problematic it might be better getting it from
554 ; the query info 1 packet.)
557 movzx cx,byte [trackbuf
+bootp.hardlen
]
560 xor cx,cx ; Bad hardware address length
563 mov al,[trackbuf
+bootp.hardware
]
565 mov si,trackbuf
+bootp.macaddr
569 ; Enable this if we really need to zero-pad this field...
570 ; mov cx,MAC+MAC_MAX+1
576 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
577 ; packet (query info 3).
580 call pxe_get_cached_info
581 call parse_dhcp
; Parse DHCP packet
584 ; Generate the bootif string, and the hardware-based config string.
589 mov cx,bootif_str_len
592 movzx cx,byte [MACLen
]
597 mov cl,1 ; CH == 0 already
603 mov [di-1],cl ; Null-terminate and strip final dash
605 ; Generate ip= option
615 call gendotquad
; This takes network byte order input
617 xchg ah,al ; Convert to host byte order
618 ror eax,16 ; (BSWAP doesn't work on 386)
635 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
636 ; if we didn't get the magic enable, do not recognize any other options.
639 test byte [DHCPMagic
], 1 ; If we didn't get the magic enable...
641 mov byte [DHCPMagic
], 0 ; If not, kill all other options
646 ; Initialize UDP stack
650 mov [pxe_udp_open_pkt.sip
],eax
651 mov di,pxe_udp_open_pkt
652 mov bx,PXENV_UDP_OPEN
655 cmp word [pxe_udp_open_pkt.status
], byte 0
657 .
failed: mov si,err_udpinit
663 ; Common initialization code
665 %include "cpuinit.inc"
668 ; Now we're all set to start with our *real* business. First load the
669 ; configuration file (if any) and parse it.
671 ; In previous versions I avoided using 32-bit registers because of a
672 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
673 ; random. I figure, though, that if there are any of those still left
674 ; they probably won't be trying to install Linux on them...
676 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
677 ; to take'm out. In fact, we may want to put them back if we're going
678 ; to boot ELKS at some point.
682 ; Store standard filename prefix
684 prefix: test byte [DHCPMagic
], 04h ; Did we get a path prefix option
693 lea si,[di-2] ; Skip final null!
696 cmp al,'.' ; Count . or - as alphanum
708 .
alnum: loop .find_alnum
710 .
notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
713 mov si,tftpprefix_msg
720 ; Load configuration file
725 ; Begin looking for configuration file
728 test byte [DHCPMagic
], 02h
731 ; We got a DHCP option, try it first
741 ; Have to guess config file name...
743 ; Try loading by UUID.
744 cmp byte [HaveUUID
],0
759 mov [di-1],cl ; Remove last dash and zero-terminate
765 ; Try loading by MAC address
773 ; Nope, try hexadecimal IP prefixes...
777 call uchexbytes
; Convert to hex string
779 mov cx,8 ; Up to 8 attempts
781 mov byte [di],0 ; Zero-terminate string
784 dec di ; Drop one character
787 ; Final attempt: "default" string
788 mov si,default_str
; "default" string
806 mov di,KernelName
; Borrow this buffer for mangled name
816 ; Linux kernel loading code is common. However, we need to define
817 ; a couple of helper macros...
820 ; Handle "ipappend" option
821 %define HAVE_SPECIAL_APPEND
822 %macro SPECIAL_APPEND
0
823 test byte [IPAppend
],01h ; ip=
831 test byte [IPAppend
],02h
835 mov byte [es:di-1],' ' ; Replace null with space
840 %define HAVE_UNLOAD_PREP
846 ; Now we have the config file open. Parse the config file and
847 ; run the user interface.
852 ; Boot to the local disk by returning the appropriate PXE magic.
853 ; AX contains the appropriate return code.
860 mov [LocalBootType
],ax
864 ; Restore the environment we were called with
871 mov ax,[cs:LocalBootType
]
878 ; kaboom: write a message and bail out. Wait for quite a while,
879 ; or a user keypress, then do a hard reboot.
882 RESET_STACK_AND_SEGS
AX
883 .
patch: mov si,bailmsg
884 call writestr
; Returns with AL = 0
885 .
drain: call pollchar
892 and al,09h ; Magic+Timeout
900 .
wait2: mov dx,[BIOS_timer
]
901 .
wait3: call pollchar
912 mov word [BIOS_magic
],0 ; Cold reboot
913 jmp 0F000h:0FFF0h
; Reset vector address
916 ; memory_scan_for_pxe_struct:
918 ; If none of the standard methods find the !PXE structure, look for it
919 ; by scanning memory.
922 ; CF = 0, ES:BX -> !PXE structure
923 ; Otherwise CF = 1, all registers saved
925 memory_scan_for_pxe_struct:
932 mov ax,[BIOS_fbm
] ; Starting segment
933 shl ax,(10-4) ; Kilobytes -> paragraphs
934 ; mov ax,01000h ; Start to look here
935 dec ax ; To skip inc ax
938 cmp ax,0A000h
; End of memory
947 movzx cx,byte [es:4] ; Length of structure
948 cmp cl,08h ; Minimum length
957 jnz .mismatch
; Checksum must == 0
960 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
968 .
not_found: mov si,notfound_msg
976 ; memory_scan_for_pxenv_struct:
978 ; If none of the standard methods find the PXENV+ structure, look for it
979 ; by scanning memory.
982 ; CF = 0, ES:BX -> PXENV+ structure
983 ; Otherwise CF = 1, all registers saved
985 memory_scan_for_pxenv_struct:
987 mov si,trymempxenv_msg
989 ; mov ax,[BIOS_fbm] ; Starting segment
990 ; shl ax,(10-4) ; Kilobytes -> paragraphs
991 mov ax,01000h ; Start to look here
992 dec ax ; To skip inc ax
995 cmp ax,0A000h
; End of memory
1004 movzx cx,byte [es:8] ; Length of structure
1005 cmp cl,26h ; Minimum length
1013 jnz .mismatch
; Checksum must == 0
1015 mov [bp+8],bx ; Save BX into stack frame
1021 .
not_found: mov si,notfound_msg
1029 ; Deallocates a file structure (pointer in SI)
1032 ; XXX: We should check to see if this file is still open on the server
1033 ; side and send a courtesy ERROR packet to the server.
1038 mov word [si],0 ; Not in use
1044 ; Open a TFTP connection to the server
1047 ; DS:DI = mangled filename
1050 ; SI = socket pointer
1051 ; EAX = file length in bytes, or -1 if unknown
1066 call allocate_socket
1069 mov ax,PKT_RETRY
; Retry counter
1070 mov word [PktTimeout
],PKT_TIMEOUT
; Initial timeout
1072 .
sendreq: push ax ; [bp-2] - Retry counter
1073 push si ; [bp-4] - File name
1076 mov [pxe_udp_write_pkt.buffer
],di
1078 mov ax,TFTP_RRQ
; TFTP opcode
1081 lodsd ; EAX <- server override (if any)
1083 jnz .noprefix
; No prefix, and we have the server
1085 push si ; Add common prefix
1091 mov eax,[ServerIP
] ; Get default server
1094 call strcpy
; Filename
1101 mov [bx+tftp_remoteip
],eax
1103 push bx ; [bp-6] - TFTP block
1105 push bx ; [bp-8] - TID (local port no)
1107 mov [pxe_udp_write_pkt.status
],byte 0
1108 mov [pxe_udp_write_pkt.sip
],eax
1109 ; Now figure out the gateway
1115 mov [pxe_udp_write_pkt.gip
],eax
1116 mov [pxe_udp_write_pkt.lport
],bx
1118 mov [pxe_udp_write_pkt.rport
],ax
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
1153 jnc .got_packet
; Wait for packet
1159 dec word [bp-10] ; Timeout
1161 pop ax ; Adjust stack
1163 shl word [PktTimeout
],1 ; Exponential backoff
1167 mov si,[bp-6] ; TFTP pointer
1170 ; Make sure the packet actually came from the server
1171 ; This is technically not to the TFTP spec?
1172 mov eax,[si+tftp_remoteip
]
1173 cmp [pxe_udp_read_pkt.sip
],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 movzx ecx,word [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
1200 ; If the server doesn't support any options, we'll get
1201 ; a DATA reply instead of OACK. Stash the data in
1202 ; the file buffer and go with the default value for
1208 jne .err_reply
; Unknown packet type
1210 ; Now we need to parse the OACK packet to get the transfer
1212 ; SI -> first byte of options; [E]CX -> byte count
1214 jcxz .done_pkt
; No options acked
1218 .
opt_name_loop: lodsb
1221 or al,20h ; Convert to lowercase
1224 ; We ran out, and no final null
1226 .
got_opt_name: ; si -> option value
1227 dec cx ; bytes left in pkt
1228 jz .err_reply
; Option w/o value
1230 ; Parse option pointed to by bx; guaranteed to be
1234 mov si,bx ; -> option name
1235 mov bx,tftp_opt_table
1240 mov di,[bx] ; Option pointer
1241 mov cx,[bx+2] ; Option len
1245 je .get_value
; OK, known option
1251 jmp .err_reply
; Non-negotiated option returned
1253 .
get_value: pop si ; si -> option value
1254 pop cx ; cx -> bytes left in pkt
1255 mov bx,[bx+4] ; Pointer to data target
1256 add bx,[bp-6] ; TFTP socket pointer
1264 ja .err_reply
; Not a decimal digit
1269 ; Ran out before final null, accept anyway
1274 jnz .get_opt_name
; Not end of packet
1281 pop si ; We want the packet ptr in SI
1283 mov eax,[si+tftp_filesize
]
1284 .
got_file: ; SI->socket structure, EAX = size
1285 and eax,eax ; Set ZF depending on file size
1286 jz .error_si
; ZF = 1 need to free the socket
1288 leave ; SP <- BP, POP BP
1295 .
no_oack: ; We got a DATA packet, meaning no options are
1296 ; suported. Save the data away and consider the length
1297 ; undefined, *unless* this is the only data packet...
1298 mov bx,[bp-6] ; File pointer
1299 sub cx,2 ; Too short?
1301 lodsw ; Block number
1304 mov [bx+tftp_lastpkt
],ax
1305 cmp cx,TFTP_BLOCKSIZE
1306 ja .err_reply
; Corrupt...
1308 ; This was the final EOF packet, already...
1309 ; We know the filesize, but we also want to ack the
1310 ; packet and set the EOF flag.
1311 mov [bx+tftp_filesize
],ecx
1312 mov byte [bx+tftp_goteof
],1
1315 ; AX = htons(1) already
1319 mov [bx+tftp_bytesleft
],cx
1324 mov [bx+tftp_dataptr
],di
1331 .
err_reply: ; Option negotiation error. Send ERROR reply.
1332 ; ServerIP and gateway are already programmed in
1334 mov ax,[si+tftp_remoteport
]
1335 mov word [pxe_udp_write_pkt.rport
],ax
1336 mov word [pxe_udp_write_pkt.buffer
],tftp_opt_err
1337 mov word [pxe_udp_write_pkt.buffersize
],tftp_opt_err_len
1338 mov di,pxe_udp_write_pkt
1339 mov bx,PXENV_UDP_WRITE
1342 ; Write an error message and explode
1347 .
bailnow: mov word [bp-2],1 ; Immediate error - no retry
1349 .
failure: pop bx ; Junk
1353 dec ax ; Retry counter
1354 jnz .sendreq
; Try again
1356 .
error: mov si,bx ; Socket pointer
1357 .
error_si: ; Socket pointer already in SI
1358 call free_socket
; ZF <- 1, SI <- 0
1364 push bx ; Socket pointer
1365 mov di,gpxe_file_open
1366 mov word [di],2 ; PXENV_STATUS_BAD_FUNC
1367 mov word [di+4],packet_buf
+2 ; Completed URL
1369 mov bx,PXENV_FILE_OPEN
1371 pop si ; Socket pointer in SI
1375 mov word [si+tftp_localport
],-1 ; gPXE URL
1376 mov [si+tftp_remoteport
],ax
1377 mov di,gpxe_get_file_size
1381 ; Disable this for now since gPXE doesn't always
1382 ; return valid information in PXENV_GET_FILE_SIZE
1383 mov bx,PXENV_GET_FILE_SIZE
1385 mov eax,[di+4] ; File size
1388 or eax,-1 ; Size unknown
1390 mov [si+tftp_filesize
],eax
1395 ; allocate_socket: Allocate a local UDP port structure
1399 ; BX = socket pointer
1407 .
check: cmp word [bx], byte 0
1409 add bx,open_file_t_size
1414 ; Allocate a socket number. Socket numbers are made
1415 ; guaranteed unique by including the socket slot number
1416 ; (inverted, because we use the loop counter cx); add a
1417 ; counter value to keep the numbers from being likely to
1418 ; get immediately reused.
1420 ; The NextSocket variable also contains the top two bits
1421 ; set. This generates a value in the range 49152 to
1428 and ax,((1 << (13-MAX_OPEN_LG2
))-1) |
0xC000
1430 shl cx,13-MAX_OPEN_LG2
1432 xchg ch,cl ; Convert to network byte order
1433 mov [bx],cx ; Socket in use
1439 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1447 mov cx,tftp_pktbuf
>> 1 ; tftp_pktbuf is not cleared
1456 ; Read a dot-quad pathname in DS:SI and output an IP
1457 ; address in EAX, with SI pointing to the first
1458 ; nonmatching character.
1460 ; Return CF=1 on error.
1462 ; No segment assumptions permitted.
1476 aad ; AL += 10 * AH; AH = 0;
1491 loop .realerror
; If CX := 1 then we're done
1497 dec si ; CF unchanged!
1502 ; is_url: Return CF=0 if and only if the buffer pointed to by
1503 ; DS:SI is a URL (contains ://). No registers modified.
1527 ; is_gpxe: Return CF=0 if and only if the buffer pointed to by
1528 ; DS:SI is a URL (contains ://) *and* the gPXE extensions
1529 ; API is available. No registers modified.
1533 jc .
ret ; Not a URL, don't bother
1535 cmp byte [HasGPXE
],1
1537 ; CF=1 if not available (0),
1538 ; CF=0 if known available (1).
1542 ; If we get here, the gPXE status is unknown.
1547 mov di,gpxe_file_api_check
1548 mov bx,PXENV_FILE_API_CHECK
; BH = 0
1551 cmp dword [di+4],0xe9c17b20
1553 mov ax,[di+12] ; Don't care about the upper half...
1554 not ax ; Set bits of *missing* functions...
1555 and ax,01001011b ; The functions we care about
1559 mov si,gpxe_warning_msg
1569 db 'URL syntax, but gPXE extensions not detected, '
1570 db 'trying plain TFTP...', CR
, LF
, 0
1571 HasGPXE
db -1 ; Unknown
1577 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1578 ; to by ES:DI; ends on encountering any whitespace.
1581 ; This verifies that a filename is < FILENAME_MAX characters
1582 ; and doesn't contain whitespace, and zero-pads the output buffer,
1583 ; so "repe cmpsb" can do a compare.
1585 ; The first four bytes of the manged name is the IP address of
1586 ; the download host, 0 for no host, or -1 for a gPXE URL.
1588 ; No segment assumptions permitted.
1595 or eax,-1 ; It's a URL
1600 mov eax,[cs:ServerIP
]
1602 je .noip
; Null filename?!?!
1603 cmp word [si],'::' ; Leading ::?
1613 ; We have a :: prefix of some sort, it could be either
1614 ; a DNS name or a dot-quad IP address. Try the dot-quad
1638 pop cx ; Adjust stack
1639 inc si ; Skip double colon
1643 stosd ; Save IP address prefix
1644 mov cx,FILENAME_MAX
-5
1648 cmp al,' ' ; If control or space, end
1653 inc cx ; At least one null byte
1654 xor ax,ax ; Zero-fill name
1655 rep stosb ; Doesn't do anything if CX=0
1660 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1661 ; filename to the conventional representation. This is needed
1662 ; for the BOOT_IMAGE= parameter for the kernel.
1664 ; NOTE: The output buffer needs to be able to hold an
1665 ; expanded IP address.
1667 ; DS:SI -> input mangled file name
1668 ; ES:DI -> output buffer
1670 ; On return, DI points to the first byte after the output name,
1671 ; which is set to a null byte.
1685 dec di ; Point to final null byte
1692 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1693 ; calling convention. This is a separate local routine so
1694 ; we can hook special things from it if necessary. In particular,
1695 ; some PXE stacks seem to not like being invoked from anything but
1696 ; the initial stack, so humour it.
1698 ; While we're at it, save and restore all registers.
1702 %if USE_PXE_PROVIDED_STACK
== 0
1703 mov [cs:PXEStack
],sp
1704 mov [cs:PXEStack
+2],ss
1705 lss sp,[cs:InitStack
]
1707 ; This works either for the PXENV+ or the !PXE calling
1708 ; convention, as long as we ignore CF (which is redundant
1715 mov [cs:PXEStatus
],ax
1716 add ax,-1 ; Set CF unless AX was 0
1718 %if USE_PXE_PROVIDED_STACK
== 0
1719 lss sp,[cs:PXEStack
]
1722 ; This clobbers the AX return, but we don't use it
1723 ; except for testing it against zero (and setting CF),
1724 ; which we did above. For anything else,
1725 ; use the Status field in the reply.
1726 ; For the COMBOOT function, the value is saved in
1727 ; the PXEStatus variable.
1729 cld ; Make sure DF <- 0
1732 ; Must be after function def due to NASM bug
1733 PXEEntry
equ pxenv.jump
+1
1742 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1744 ; In this case, get multiple blocks from a specific TCP connection.
1748 ; SI -> TFTP socket pointer
1749 ; CX -> 512-byte block count; 0FFFFh = until end of file
1751 ; SI -> TFTP socket pointer (or 0 on EOF)
1753 ; ECX -> number of bytes actually read
1767 shl ecx,TFTP_BLOCKSIZE_LG2
; Convert to bytes
1768 push ecx ; Initial request size
1769 jz .hit_eof
; Nothing to do?
1773 movzx eax,word [si+tftp_bytesleft
]
1782 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1783 mov bx,[si+tftp_dataptr
]
1784 sub [si+tftp_bytesleft
],cx
1786 fs rep movsb ; Copy from packet buffer
1788 mov [si+tftp_dataptr
],bx
1797 pop eax ; Initial request amount
1799 sub ecx,eax ; ... minus anything not gotten
1804 ; Is there anything left of this?
1805 mov eax,[si+tftp_filesize
]
1806 sub eax,[si+tftp_filepos
]
1809 cmp [si+tftp_bytesleft
],ax ; AX == 0
1812 cmp byte [si+tftp_goteof
],0
1814 ; I'm 99% sure this can't happen, but...
1815 call fill_buffer
; Receive/ACK the EOF packet
1817 ; The socket is closed and the buffer drained
1818 ; Close socket structure and re-init for next user
1831 ; Get a fresh packet if the buffer is drained, and we haven't hit
1832 ; EOF yet. The buffer should be filled immediately after draining!
1834 ; expects fs -> pktbuf_seg and ds:si -> socket structure
1837 cmp word [si+tftp_bytesleft
],0
1839 ret ; Otherwise, nothing to do
1847 ; Note: getting the EOF packet is not the same thing
1848 ; as tftp_filepos == tftp_filesize; if the EOF packet
1849 ; is empty the latter condition can be true without
1850 ; having gotten the official EOF.
1851 cmp byte [si+tftp_goteof
],0
1852 jne .
ret ; Already EOF
1855 cmp word [si+tftp_localport
], -1
1856 jne .get_packet_tftp
1857 call get_packet_gpxe
1864 ; Start by ACKing the previous packet; this should cause the
1865 ; next packet to be sent.
1867 mov word [PktTimeout
],PKT_TIMEOUT
1869 .
send_ack: push cx ; <D> Retry count
1871 mov ax,[si+tftp_lastpkt
]
1872 call ack_packet
; Send ACK
1874 ; We used to test the error code here, but sometimes
1875 ; PXE would return negative status even though we really
1876 ; did send the ACK. Now, just treat a failed send as
1877 ; a normally lost packet, and let it time out in due
1880 .
send_ok: ; Now wait for packet.
1881 mov dx,[BIOS_timer
] ; Get current time
1884 .
wait_data: push cx ; <E> Timeout
1885 push dx ; <F> Old time
1887 mov bx,[si+tftp_pktbuf
]
1888 mov [pxe_udp_read_pkt.buffer
],bx
1889 mov [pxe_udp_read_pkt.buffer
+2],fs
1890 mov [pxe_udp_read_pkt.buffersize
],word PKTBUF_SIZE
1891 mov eax,[si+tftp_remoteip
]
1892 mov [pxe_udp_read_pkt.sip
],eax
1894 mov [pxe_udp_read_pkt.dip
],eax
1895 mov ax,[si+tftp_remoteport
]
1896 mov [pxe_udp_read_pkt.rport
],ax
1897 mov ax,[si+tftp_localport
]
1898 mov [pxe_udp_read_pkt.lport
],ax
1899 mov di,pxe_udp_read_pkt
1900 mov bx,PXENV_UDP_READ
1904 ; No packet, or receive failure
1906 pop ax ; <F> Old time
1907 pop cx ; <E> Timeout
1908 cmp ax,dx ; Same time -> don't advance timeout
1909 je .wait_data
; Same clock tick
1910 loop .wait_data
; Decrease timeout
1912 pop cx ; <D> Didn't get any, send another ACK
1913 shl word [PktTimeout
],1 ; Exponential backoff
1915 jmp kaboom
; Forget it...
1917 .
recv_ok: pop dx ; <F>
1920 cmp word [pxe_udp_read_pkt.buffersize
],byte 4
1921 jb .wait_data
; Bad size for a DATA packet
1923 mov bx,[si+tftp_pktbuf
]
1924 cmp word [fs:bx],TFTP_DATA
; Not a data packet?
1925 jne .wait_data
; Then wait for something else
1927 mov ax,[si+tftp_lastpkt
]
1928 xchg ah,al ; Host byte order
1929 inc ax ; Which packet are we waiting for?
1930 xchg ah,al ; Network byte order
1934 ; Wrong packet, ACK the packet and then try again
1935 ; This is presumably because the ACK got lost,
1936 ; so the server just resent the previous packet
1939 jmp .send_ok
; Reset timeout
1941 .
right_packet: ; It's the packet we want. We're also EOF if the
1944 pop cx ; <D> Don't need the retry count anymore
1946 mov [si+tftp_lastpkt
],ax ; Update last packet number
1948 movzx ecx,word [pxe_udp_read_pkt.buffersize
]
1949 sub cx,byte 4 ; Skip TFTP header
1951 ; Set pointer to data block
1952 lea ax,[bx+4] ; Data past TFTP header
1953 mov [si+tftp_dataptr
],ax
1955 add [si+tftp_filepos
],ecx
1956 mov [si+tftp_bytesleft
],cx
1958 cmp cx,[si+tftp_blksize
] ; Is it a full block?
1959 jb .last_block
; If not, it's EOF
1967 .
last_block: ; Last block - ACK packet immediately
1971 ; Make sure we know we are at end of file
1972 mov eax,[si+tftp_filepos
]
1973 mov [si+tftp_filesize
],eax
1974 mov byte [si+tftp_goteof
],1
1981 ; Send ACK packet. This is a common operation and so is worth canning.
1985 ; AX = Packet # to ack (network byte order)
1987 ; All registers preserved
1989 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1993 mov [ack_packet_buf
+2],ax ; Packet number to ack
1995 mov [pxe_udp_write_pkt.lport
],ax
1996 mov ax,[si+tftp_remoteport
]
1997 mov [pxe_udp_write_pkt.rport
],ax
1998 mov eax,[si+tftp_remoteip
]
1999 mov [pxe_udp_write_pkt.sip
],eax
2005 mov [pxe_udp_write_pkt.gip
],eax
2006 mov [pxe_udp_write_pkt.buffer
],word ack_packet_buf
2007 mov [pxe_udp_write_pkt.buffersize
], word 4
2008 mov di,pxe_udp_write_pkt
2009 mov bx,PXENV_UDP_WRITE
2016 ; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
2017 ; and ds:si -> socket structure
2019 ; Assumes CS == DS == ES.
2022 mov di,gpxe_file_read
2024 mov ax,[si+tftp_remoteport
] ; gPXE filehandle
2026 mov ax,[si+tftp_pktbuf
]
2028 mov [si+tftp_dataptr
],ax
2032 mov word [di+4],PKTBUF_SIZE
2033 mov bx,PXENV_FILE_READ
2035 jnc .ok
; Got data or EOF
2036 cmp word [di],PXENV_STATUS_TFTP_OPEN
; == EWOULDBLOCK
2038 jmp kaboom
; Otherwise error...
2041 movzx eax,word [di+4] ; Bytes read
2042 mov [si+tftp_bytesleft
],ax ; Bytes in buffer
2043 add [si+tftp_filepos
],eax ; Position in file
2046 mov eax,[si+tftp_filepos
]
2050 ; We got EOF here, make sure the upper layers know
2051 mov [si+tftp_filesize
],eax
2054 ; If we're done here, close the file
2055 cmp [si+tftp_filesize
],eax
2056 ja .done
; Not EOF, there is still data...
2058 ; Reuse the previous [es:di] structure since the
2059 ; relevant fields are all the same
2060 mov byte [si+tftp_goteof
],1
2062 mov bx,PXENV_FILE_CLOSE
2072 ; This function unloads the PXE and UNDI stacks and unclaims
2076 test byte [KeepPXE
],01h ; Should we keep PXE around?
2086 mov si,new_api_unload
2087 cmp byte [APIVer
+1],2 ; Major API version >= 2?
2089 mov si,old_api_unload
2092 .
call_loop: xor ax,ax
2097 mov di,pxe_unload_stack_pkt
2100 mov cx,pxe_unload_stack_pkt_len
>> 1
2105 mov ax,word [pxe_unload_stack_pkt.status
]
2106 cmp ax,PXENV_STATUS_SUCCESS
2113 mov dx,[RealBaseMem
]
2114 cmp dx,[BIOS_fbm
] ; Sanity check
2118 ; Check that PXE actually unhooked the INT 1Ah chain
2119 movzx eax,word [4*0x1a]
2120 movzx ecx,word [4*0x1a+2]
2124 cmp ax,dx ; Not in range
2138 mov si,cant_free_msg
2154 ; We want to keep PXE around, but still we should reset
2155 ; it to the standard bootup configuration
2160 mov bx,PXENV_UDP_CLOSE
2161 mov di,pxe_udp_close_pkt
2169 ; Take an IP address (in network byte order) in EAX and
2170 ; output a dotted quad string to ES:DI.
2171 ; DI points to terminal null at end of string on exit.
2180 jb .lt10
; If so, skip first 2 digits
2183 jb .lt100
; If so, skip first digit
2186 ; Now AH = 100-digit; AL = remainder
2193 ; Now AH = 10-digit; AL = remainder
2204 ror eax,8 ; Move next char into LSB
2212 ; uchexbytes/lchexbytes
2214 ; Take a number of bytes in memory and convert to upper/lower-case
2218 ; DS:SI = input bytes
2219 ; ES:DI = output buffer
2220 ; CX = number of bytes
2222 ; DS:SI = first byte after
2223 ; ES:DI = first byte after
2255 ; pxe_get_cached_info
2257 ; Get a DHCP packet from the PXE stack into the trackbuf.
2264 ; Assumes CS == DS == ES.
2266 pxe_get_cached_info:
2268 mov si,get_packet_msg
2273 mov di,pxe_bootp_query_pkt
2282 stosw ; Buffer offset
2284 stosw ; Buffer segment
2286 pop di ; DI -> parameter set
2287 mov bx,PXENV_GET_CACHED_INFO
2294 mov cx,[pxe_bootp_query_pkt.buffersize
]
2298 mov si,err_pxefailed
2305 get_packet_msg
db 'Getting cached packet ', 0
2311 ; Tests an IP address in EAX for validity; return with ZF=1 for bad.
2312 ; We used to refuse class E, but class E addresses are likely to become
2313 ; assignable unicast addresses in the near future.
2317 cmp eax,-1 ; Refuse the all-ones address
2319 and al,al ; Refuse network zero
2321 cmp al,127 ; Refuse loopback
2324 cmp al,224 ; Refuse class D
2332 ; Parse a DHCP packet. This includes dealing with "overloaded"
2333 ; option fields (see RFC 2132, section 9.3)
2335 ; This should fill in the following global variables, if the
2336 ; information is present:
2338 ; MyIP - client IP address
2339 ; ServerIP - boot server IP address
2340 ; Netmask - network mask
2341 ; Gateway - default gateway router IP
2342 ; BootFile - boot file name
2343 ; DNSServers - DNS server IPs
2344 ; LocalDomain - Local domain name
2345 ; MACLen, MAC - Client identifier, if MACLen == 0
2347 ; This assumes the DHCP packet is in "trackbuf" and the length
2348 ; of the packet in in CX on entry.
2352 mov byte [OverLoad
],0 ; Assume no overload
2353 mov eax, [trackbuf
+bootp.yip
]
2358 mov eax, [trackbuf
+bootp.sip
]
2364 sub cx, bootp.options
2366 mov si, trackbuf
+bootp.option_magic
2368 cmp eax, BOOTP_OPTION_MAGIC
2370 call parse_dhcp_options
2372 mov si, trackbuf
+bootp.bootfile
2373 test byte [OverLoad
],1
2376 call parse_dhcp_options
2377 jmp short .parsed_file
2380 jz .parsed_file
; No bootfile name
2385 stosb ; Null-terminate
2387 mov si, trackbuf
+bootp.sname
2388 test byte [OverLoad
],2
2391 call parse_dhcp_options
2396 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2397 ; size is CX -- some DHCP servers leave option fields unterminated
2398 ; in violation of the spec.
2400 ; For parse_some_dhcp_options, DH contains the minimum value for
2401 ; the option to recognize -- this is used to restrict parsing to
2402 ; PXELINUX-specific options only.
2407 parse_some_dhcp_options:
2414 jz .done
; Last byte; must be PAD, END or malformed
2415 cmp al, 0 ; PAD option
2417 cmp al,255 ; END option
2420 ; Anything else will have a length field
2421 mov dl,al ; DL <- option number
2423 lodsb ; AX <- option length
2425 sub cx,ax ; Decrement bytes left counter
2426 jb .done
; Malformed option: length > field size
2428 cmp dl,dh ; Is the option value valid?
2431 mov bx,dhcp_option_list
2433 cmp bx,dhcp_option_list_end
2445 ; Unknown option. Skip to the next one.
2465 ; Parse individual DHCP options. SI points to the option data and
2466 ; AX to the option length. DL contains the option number.
2467 ; All registers are saved around the routine.
2482 cmp cl,DNS_MAX_SERVERS
2484 mov cl,DNS_MAX_SERVERS
2488 mov [LastDNSServer
],di
2491 dopt
16, local_domain
2495 xchg [bx],al ; Zero-terminate option
2497 call dns_mangle
; Convert to DNS label set
2498 mov [bx],al ; Restore ending byte
2501 dopt
43, vendor_encaps
2502 mov dh,208 ; Only recognize PXELINUX options
2503 mov cx,ax ; Length of option = max bytes to parse
2504 call parse_some_dhcp_options
; Parse recursive structure
2507 dopt
52, option_overload
2514 cmp dword [ServerIP
],0
2515 jne .skip
; Already have a next server IP
2521 dopt
61, client_identifier
2522 cmp ax,MAC_MAX
; Too long?
2524 cmp ax,2 ; Too short?
2526 cmp [MACLen
],ah ; Only do this if MACLen == 0
2529 lodsb ; Client identifier type
2532 jne .skip
; Client identifier is not a MAC
2539 dopt
67, bootfile_name
2543 dopt
97, uuid_client_identifier
2544 cmp ax,17 ; type byte + 16 bytes UUID
2546 mov dl,[si] ; Must have type 0 == UUID
2547 or dl,[HaveUUID
] ; Capture only the first instance
2549 mov byte [HaveUUID
],1 ; Got UUID
2554 dopt
209, pxelinux_configfile
2556 or byte [DHCPMagic
],2 ; Got config file
2559 dopt
210, pxelinux_pathprefix
2561 or byte [DHCPMagic
],4 ; Got path prefix
2564 dopt
211, pxelinux_reboottime
2568 xchg bl,bh ; Convert to host byte order
2571 mov [RebootTime
],ebx
2572 or byte [DHCPMagic
],8 ; Got RebootTime
2575 ; Common code for copying an option verbatim
2576 ; Copies the option into ES:DI and null-terminates it.
2577 ; Returns with AX=0 and SI past the option.
2579 xchg cx,ax ; CX <- option length
2581 xchg cx,ax ; AX <- 0
2582 stosb ; Null-terminate
2586 dhcp_option_list_end:
2591 uuid_dashes
db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2597 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2598 ; option into IPOption based on a DHCP packet in trackbuf.
2599 ; Assumes CS == DS == ES.
2620 call gendotquad
; Zero-terminates its output
2622 mov [IPOptionLen
],di
2627 ; Call the receive loop while idle. This is done mostly so we can respond to
2628 ; ARP messages, but perhaps in the future this can be used to do network
2631 ; hpa sez: people using automatic control on the serial port get very
2632 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2633 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2634 ; passed since the last poll, and reset this when a character is
2635 ; received (RESET_IDLE).
2641 mov ax,[cs:BIOS_timer
]
2642 mov [cs:IdleTimer
],ax
2648 mov ax,[cs:BIOS_timer
]
2649 sub ax,[cs:IdleTimer
]
2661 mov [pxe_udp_read_pkt.status
],al ; 0
2662 mov [pxe_udp_read_pkt.buffer
],di
2663 mov [pxe_udp_read_pkt.buffer
+2],ds
2664 mov word [pxe_udp_read_pkt.buffersize
],packet_buf_size
2666 mov [pxe_udp_read_pkt.dip
],eax
2667 mov word [pxe_udp_read_pkt.lport
],htons
(9) ; discard port
2668 mov di,pxe_udp_read_pkt
2669 mov bx,PXENV_UDP_READ
2680 ; -----------------------------------------------------------------------------
2682 ; -----------------------------------------------------------------------------
2684 %include "getc.inc" ; getc et al
2685 %include "conio.inc" ; Console I/O
2686 %include "writestr.inc" ; String output
2687 writestr
equ cwritestr
2688 %include "writehex.inc" ; Hexadecimal output
2689 %include "configinit.inc" ; Initialize configuration
2690 %include "parseconfig.inc" ; High-level config file handling
2691 %include "parsecmd.inc" ; Low-level config file handling
2692 %include "bcopy32.inc" ; 32-bit bcopy
2693 %include "loadhigh.inc" ; Load a file into high memory
2694 %include "font.inc" ; VGA font stuff
2695 %include "graphics.inc" ; VGA graphics
2696 %include "highmem.inc" ; High memory sizing
2697 %include "strcpy.inc" ; strcpy()
2698 %include "rawcon.inc" ; Console I/O w/o using the console functions
2699 %include "dnsresolv.inc" ; DNS resolver
2700 %include "adv.inc" ; Auxillary Data Vector
2702 ; -----------------------------------------------------------------------------
2703 ; Begin data section
2704 ; -----------------------------------------------------------------------------
2708 copyright_str
db ' Copyright (C) 1994-', year
, ' H. Peter Anvin'
2710 err_bootfailed
db CR
, LF
, 'Boot failed: press a key to retry, or wait for reset...', CR
, LF
, 0
2711 bailmsg
equ err_bootfailed
2712 err_nopxe
db "No !PXE or PXENV+ API found; we're dead...", CR
, LF
, 0
2713 err_pxefailed
db 'PXE API call failed, error ', 0
2714 err_udpinit
db 'Failed to initialize UDP stack', CR
, LF
, 0
2715 err_noconfig
db 'Unable to locate configuration file', CR
, LF
, 0
2716 err_damage
db 'TFTP server sent an incomprehesible reply', CR
, LF
, 0
2717 found_pxenv
db 'Found PXENV+ structure', CR
, LF
, 0
2718 using_pxenv_msg
db 'Old PXE API detected, using PXENV+ structure', CR
, LF
, 0
2719 apiver_str
db 'PXE API version is ',0
2720 pxeentry_msg
db 'PXE entry point found (we hope) at ', 0
2721 pxenventry_msg
db 'PXENV entry point found (we hope) at ', 0
2722 trymempxe_msg
db 'Scanning memory for !PXE structure... ', 0
2723 trymempxenv_msg
db 'Scanning memory for PXENV+ structure... ', 0
2724 undi_data_msg
db 'UNDI data segment at: ',0
2725 undi_data_len_msg
db 'UNDI data segment size: ',0
2726 undi_code_msg
db 'UNDI code segment at: ',0
2727 undi_code_len_msg
db 'UNDI code segment size: ',0
2728 cant_free_msg
db 'Failed to free base memory, error ', 0
2729 notfound_msg
db 'not found', CR
, LF
, 0
2730 myipaddr_msg
db 'My IP address seems to be ',0
2731 tftpprefix_msg
db 'TFTP prefix: ', 0
2732 localboot_msg
db 'Booting from local disk...', CR
, LF
, 0
2733 trying_msg
db 'Trying to load: ', 0
2734 fourbs_msg
db BS
, BS
, BS
, BS
, 0
2735 default_str
db 'default', 0
2736 syslinux_banner
db CR
, LF
, 'PXELINUX ', version_str
, ' ', date
, ' ', 0
2737 cfgprefix
db 'pxelinux.cfg/' ; No final null!
2738 cfgprefix_len
equ ($
-cfgprefix
)
2741 ; Command line options we'd like to take a look at
2743 ; mem= and vga= are handled as normal 32-bit integer values
2744 initrd_cmd
db 'initrd='
2745 initrd_cmd_len
equ $
-initrd_cmd
2747 ; This one we make ourselves
2748 bootif_str
db 'BOOTIF='
2749 bootif_str_len
equ $
-bootif_str
2751 ; Config file keyword table
2753 %include "keywords.inc"
2756 ; Extensions to search for (in *forward* order).
2757 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2760 exten_table: db '.cbt' ; COMBOOT (specific)
2761 db '.0', 0, 0 ; PXE bootstrap program
2762 db '.com' ; COMBOOT (same as DOS)
2765 dd 0, 0 ; Need 8 null bytes here
2768 ; PXE unload sequences
2772 db PXENV_UNDI_SHUTDOWN
2773 db PXENV_UNLOAD_STACK
2778 db PXENV_UNDI_SHUTDOWN
2779 db PXENV_UNLOAD_STACK
2780 db PXENV_UNDI_CLEANUP
2784 ; PXE query packets partially filled in
2787 pxe_bootp_query_pkt:
2788 .
status: resw
1 ; Status
2789 .
packettype: resw
1 ; Boot server packet type
2790 .
buffersize: resw
1 ; Packet size
2791 .
buffer: resw
2 ; seg:off of buffer
2792 .
bufferlimit: resw
1 ; Unused
2796 .
status: dw 0 ; Status
2797 .
sip: dd 0 ; Source (our) IP
2800 .
status: dw 0 ; Status
2803 .
status: dw 0 ; Status
2804 .
sip: dd 0 ; Server IP
2805 .
gip: dd 0 ; Gateway IP
2806 .
lport: dw 0 ; Local port
2807 .
rport: dw 0 ; Remote port
2808 .
buffersize: dw 0 ; Size of packet
2809 .
buffer: dw 0, 0 ; seg:off of buffer
2812 .
status: dw 0 ; Status
2813 .
sip: dd 0 ; Source IP
2814 .
dip: dd 0 ; Destination (our) IP
2815 .
rport: dw 0 ; Remote port
2816 .
lport: dw 0 ; Local port
2817 .
buffersize: dw 0 ; Max packet size
2818 .
buffer: dw 0, 0 ; seg:off of buffer
2822 gpxe_file_api_check:
2823 .
status: dw 0 ; Status
2824 .
size: dw 20 ; Size in bytes
2825 .
magic: dd 0x91d447b2 ; Magic number
2831 .
status: dw 0 ; Status
2832 .
filehandle: dw 0 ; FileHandle
2833 .
filename: dd 0 ; seg:off of FileName
2837 .
status: dw 0 ; Status
2838 .
filehandle: dw 0 ; FileHandle
2839 .
filesize: dd 0 ; FileSize
2842 .
status: dw 0 ; Status
2843 .
filehandle: dw 0 ; FileHandle
2844 .
buffersize: dw 0 ; BufferSize
2845 .
buffer: dd 0 ; seg:off of buffer
2850 ; Misc initialized (data) variables
2853 BaseStack
dd StackBuf
; ESP of base stack
2854 dw 0 ; SS of base stack
2855 NextSocket
dw 49152 ; Counter for allocating socket numbers
2856 KeepPXE
db 0 ; Should PXE be kept around?
2861 tftp_tail
db 'octet', 0 ; Octet mode
2862 tsize_str
db 'tsize' ,0 ; Request size
2863 tsize_len
equ ($
-tsize_str
)
2865 blksize_str
db 'blksize', 0 ; Request large blocks
2866 blksize_len
equ ($
-blksize_str
)
2867 asciidec TFTP_LARGEBLK
2869 tftp_tail_len
equ ($
-tftp_tail
)
2873 ; Options negotiation parsing table (string pointer, string len, offset
2874 ; into socket structure)
2877 dw tsize_str
, tsize_len
, tftp_filesize
2878 dw blksize_str
, blksize_len
, tftp_blksize
2879 tftp_opts
equ ($
-tftp_opt_table
)/6
2882 ; Error packet to return on options negotiation error
2884 tftp_opt_err
dw TFTP_ERROR
; ERROR packet
2885 dw TFTP_EOPTNEG
; ERROR 8: bad options
2886 db 'tsize option required', 0 ; Error message
2887 tftp_opt_err_len
equ ($
-tftp_opt_err
)
2890 ack_packet_buf: dw TFTP_ACK
, 0 ; TFTP ACK packet
2893 ; IP information (initialized to "unknown" values)
2894 MyIP
dd 0 ; My IP address
2895 ServerIP
dd 0 ; IP address of boot server
2896 Netmask
dd 0 ; Netmask of this subnet
2897 Gateway
dd 0 ; Default router
2898 ServerPort
dw TFTP_PORT
; TFTP server port
2901 ; Variables that are uninitialized in SYSLINUX but initialized here
2904 BufSafe
dw trackbufsize
/TFTP_BLOCKSIZE
; Clusters we can load into trackbuf
2905 BufSafeBytes
dw trackbufsize
; = how many bytes?
2907 %if
( trackbufsize
% TFTP_BLOCKSIZE
) != 0
2908 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE