Next version on this branch would be 3.64
[syslinux.git] / pxelinux.asm
blob7cc8d748f2c98977b1d17de58d05ed0bdebd6f05
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
4 ; pxelinux.asm
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
8 ; MS-DOS floppies.
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 ; ****************************************************************************
20 %define IS_PXELINUX 1
21 %include "head.inc"
22 %include "pxe.inc"
25 ; Some semi-configurable constants... change on your own risk.
27 my_id equ pxelinux_id
28 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
29 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
30 NULLFILE equ 0 ; Zero byte == null file name
31 NULLOFFSET equ 4 ; Position in which to look
32 REBOOT_TIME equ 5*60 ; If failure, time until full reset
33 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
34 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
35 MAX_OPEN equ (1 << MAX_OPEN_LG2)
36 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
37 TFTP_PORT equ htons(69) ; Default TFTP port
38 PKT_RETRY equ 6 ; Packet transmit retry count
39 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
40 ; Desired TFTP block size
41 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
42 ; be a fair number of networks with "substandard" MTUs which break.
43 ; The code assumes TFTP_LARGEBLK <= 2K.
44 TFTP_MTU equ 1440
45 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
46 ; Standard TFTP block size
47 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
48 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
49 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
51 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
52 SECTOR_SIZE equ TFTP_BLOCKSIZE
55 ; This is what we need to do when idle
56 ; *** This is disabled because some PXE stacks wait for unacceptably
57 ; *** long if there are no packets receivable.
59 %define HAVE_IDLE 0 ; idle is not a noop
61 %if HAVE_IDLE
62 %macro RESET_IDLE 0
63 call reset_idle
64 %endmacro
65 %macro DO_IDLE 0
66 call check_for_arp
67 %endmacro
68 %else
69 %macro RESET_IDLE 0
70 ; Nothing
71 %endmacro
72 %macro DO_IDLE 0
73 ; Nothing
74 %endmacro
75 %endif
78 ; TFTP operation codes
80 TFTP_RRQ equ htons(1) ; Read request
81 TFTP_WRQ equ htons(2) ; Write request
82 TFTP_DATA equ htons(3) ; Data packet
83 TFTP_ACK equ htons(4) ; ACK packet
84 TFTP_ERROR equ htons(5) ; ERROR packet
85 TFTP_OACK equ htons(6) ; OACK packet
88 ; TFTP error codes
90 TFTP_EUNDEF equ htons(0) ; Unspecified error
91 TFTP_ENOTFOUND equ htons(1) ; File not found
92 TFTP_EACCESS equ htons(2) ; Access violation
93 TFTP_ENOSPACE equ htons(3) ; Disk full
94 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
95 TFTP_EBADID equ htons(5) ; Unknown transfer
96 TFTP_EEXISTS equ htons(6) ; File exists
97 TFTP_ENOUSER equ htons(7) ; No such user
98 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
101 ; The following structure is used for "virtual kernels"; i.e. LILO-style
102 ; option labels. The options we permit here are `kernel' and `append
103 ; Since there is no room in the bottom 64K for all of these, we
104 ; stick them in high memory and copy them down before we need them.
106 struc vkernel
107 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
108 vk_rname: resb FILENAME_MAX ; Real name
109 vk_ipappend: resb 1 ; "IPAPPEND" flag
110 vk_type: resb 1 ; Type of file
111 vk_appendlen: resw 1
112 alignb 4
113 vk_append: resb max_cmd_len+1 ; Command line
114 alignb 4
115 vk_end: equ $ ; Should be <= vk_size
116 endstruc
119 ; Segment assignments in the bottom 640K
120 ; 0000h - main code/data segment (and BIOS segment)
122 real_mode_seg equ 3000h
123 pktbuf_seg equ 2000h ; Packet buffers segments
124 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
125 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
128 ; BOOTP/DHCP packet pattern
130 struc bootp_t
131 bootp:
132 .opcode resb 1 ; BOOTP/DHCP "opcode"
133 .hardware resb 1 ; ARP hardware type
134 .hardlen resb 1 ; Hardware address length
135 .gatehops resb 1 ; Used by forwarders
136 .ident resd 1 ; Transaction ID
137 .seconds resw 1 ; Seconds elapsed
138 .flags resw 1 ; Broadcast flags
139 .cip resd 1 ; Client IP
140 .yip resd 1 ; "Your" IP
141 .sip resd 1 ; Next server IP
142 .gip resd 1 ; Relay agent IP
143 .macaddr resb 16 ; Client MAC address
144 .sname resb 64 ; Server name (optional)
145 .bootfile resb 128 ; Boot file name
146 .option_magic resd 1 ; Vendor option magic cookie
147 .options resb 1260 ; Vendor options
148 endstruc
150 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
153 ; TFTP connection data structure. Each one of these corresponds to a local
154 ; UDP port. The size of this structure must be a power of 2.
155 ; HBO = host byte order; NBO = network byte order
156 ; (*) = written by options negotiation code, must be dword sized
158 struc open_file_t
159 tftp_localport resw 1 ; Local port number (0 = not in use)
160 tftp_remoteport resw 1 ; Remote port number
161 tftp_remoteip resd 1 ; Remote IP address
162 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
163 tftp_filesize resd 1 ; Total file size(*)
164 tftp_blksize resd 1 ; Block size for this connection(*)
165 tftp_bytesleft resw 1 ; Unclaimed data bytes
166 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
167 tftp_dataptr resw 1 ; Pointer to available data
168 resw 2 ; Currently unusued
169 ; At end since it should not be zeroed on socked close
170 tftp_pktbuf resw 1 ; Packet buffer offset
171 endstruc
172 %ifndef DEPEND
173 %if (open_file_t_size & (open_file_t_size-1))
174 %error "open_file_t is not a power of 2"
175 %endif
176 %endif
178 ; ---------------------------------------------------------------------------
179 ; BEGIN CODE
180 ; ---------------------------------------------------------------------------
183 ; Memory below this point is reserved for the BIOS and the MBR
185 section .earlybss
186 trackbufsize equ 8192
187 trackbuf resb trackbufsize ; Track buffer goes here
188 ; ends at 2800h
190 alignb open_file_t_size
191 Files resb MAX_OPEN*open_file_t_size
193 alignb FILENAME_MAX
194 BootFile resb 256 ; Boot file from DHCP packet
195 PathPrefix resb 256 ; Path prefix derived from boot file
196 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
197 IPOption resb 80 ; ip= option buffer
198 InitStack resd 1 ; Pointer to reset stack (SS:SP)
199 PXEStack resd 1 ; Saved stack during PXE call
201 section .bss
202 alignb 4
203 RebootTime resd 1 ; Reboot timeout, if set by option
204 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
205 APIVer resw 1 ; PXE API version found
206 IPOptionLen resw 1 ; Length of IPOption
207 IdleTimer resw 1 ; Time to check for ARP?
208 LocalBootType resw 1 ; Local boot return code
209 PktTimeout resw 1 ; Timeout for current packet
210 RealBaseMem resw 1 ; Amount of DOS memory after freeing
211 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
212 DHCPMagic resb 1 ; PXELINUX magic flags
214 ; The relative position of these fields matter!
215 MAC_MAX equ 32 ; Handle hardware addresses this long
216 MACLen resb 1 ; MAC address len
217 MACType resb 1 ; MAC address type
218 MAC resb MAC_MAX+1 ; Actual MAC address
219 BOOTIFStr resb 7 ; Space for "BOOTIF="
220 MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
222 ; The relative position of these fields matter!
223 UUIDType resb 1 ; Type byte from DHCP option
224 UUID resb 16 ; UUID, from the PXE stack
225 UUIDNull resb 1 ; dhcp_copyoption zero-terminates
228 ; PXE packets which don't need static initialization
230 alignb 4
231 pxe_unload_stack_pkt:
232 .status: resw 1 ; Status
233 .reserved: resw 10 ; Reserved
234 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
236 alignb 16
237 ; BOOTP/DHCP packet buffer
239 section .bss2
240 alignb 16
241 packet_buf resb 2048 ; Transfer packet
242 packet_buf_size equ $-packet_buf
244 section .text
246 ; PXELINUX needs more BSS than the other derivatives;
247 ; therefore we relocate it from 7C00h on startup.
249 StackBuf equ $ ; Base of stack if we use our own
252 ; Primary entry point.
254 bootsec equ $
255 _start:
256 pushfd ; Paranoia... in case of return to PXE
257 pushad ; ... save as much state as possible
258 push ds
259 push es
260 push fs
261 push gs
263 xor ax,ax
264 mov ds,ax
265 mov es,ax
267 %ifndef DEPEND
268 %if TEXT_START != 0x7c00
269 ; This is uglier than it should be, but works around
270 ; some NASM 0.98.38 bugs.
271 mov di,section..bcopy32.start
272 add di,__bcopy_size-4
273 lea si,[di-(TEXT_START-7C00h)]
274 lea cx,[di-(TEXT_START-4)]
275 shr cx,2
276 std ; Overlapping areas, copy backwards
277 rep movsd
278 %endif
279 %endif
280 jmp 0:_start1 ; Canonicalize address
281 _start1:
282 mov bp,sp
283 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
285 ; That is all pushed onto the PXE stack. Save the pointer
286 ; to it and switch to an internal stack.
287 mov [InitStack],sp
288 mov [InitStack+2],ss
290 %if USE_PXE_PROVIDED_STACK
291 ; Apparently some platforms go bonkers if we
292 ; set up our own stack...
293 mov [BaseStack],sp
294 mov [BaseStack+4],ss
295 %endif
297 cli ; Paranoia
298 lss esp,[BaseStack]
300 sti ; Stack set up and ready
301 cld ; Copy upwards
304 ; Initialize screen (if we're using one)
306 push es ; Save ES -> PXE entry structure
307 push ds
308 pop es ; ES <- DS
309 %include "init.inc"
310 pop es ; Restore ES -> PXE entry structure
312 ; Tell the user we got this far
314 mov si,syslinux_banner
315 call writestr
317 mov si,copyright_str
318 call writestr
321 ; Assume API version 2.1, in case we find the !PXE structure without
322 ; finding the PXENV+ structure. This should really look at the Base
323 ; Code ROM ID structure in have_pxe, but this is adequate for now --
324 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
325 ; about higher versions than that.
327 mov word [APIVer],0201h
330 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
331 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
332 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
333 ; We should make that the second test, and not trash ES:BX...
335 cmp dword [es:bx], '!PXE'
336 je have_pxe
338 ; Uh-oh, not there... try plan B
339 mov ax, 5650h
340 %if USE_PXE_PROVIDED_STACK == 0
341 lss sp,[InitStack]
342 %endif
343 int 1Ah ; May trash regs
344 %if USE_PXE_PROVIDED_STACK == 0
345 lss esp,[BaseStack]
346 %endif
348 jc no_pxe
349 cmp ax,564Eh
350 jne no_pxe
352 ; Okay, that gave us the PXENV+ structure, find !PXE
353 ; structure from that (if available)
354 cmp dword [es:bx], 'PXEN'
355 jne no_pxe
356 cmp word [es:bx+4], 'V+'
357 je have_pxenv
359 ; Nothing there either. Last-ditch: scan memory
360 call memory_scan_for_pxe_struct ; !PXE scan
361 jnc have_pxe
362 call memory_scan_for_pxenv_struct ; PXENV+ scan
363 jnc have_pxenv
365 no_pxe: mov si,err_nopxe
366 call writestr
367 jmp kaboom
369 have_pxenv:
370 mov [StrucPtr],bx
371 mov [StrucPtr+2],es
373 mov si,found_pxenv
374 call writestr
376 mov si,apiver_str
377 call writestr
378 mov ax,[es:bx+6]
379 mov [APIVer],ax
380 call writehex4
381 call crlf
383 cmp ax,0201h ; API version 2.1 or higher
384 jb old_api
385 mov si,bx
386 mov ax,es
387 les bx,[es:bx+28h] ; !PXE structure pointer
388 cmp dword [es:bx],'!PXE'
389 je have_pxe
391 ; Nope, !PXE structure missing despite API 2.1+, or at least
392 ; the pointer is missing. Do a last-ditch attempt to find it.
393 call memory_scan_for_pxe_struct
394 jnc have_pxe
396 ; Otherwise, no dice, use PXENV+ structure
397 mov bx,si
398 mov es,ax
400 old_api: ; Need to use a PXENV+ structure
401 mov si,using_pxenv_msg
402 call writestr
404 mov eax,[es:bx+0Ah] ; PXE RM API
405 mov [PXENVEntry],eax
407 mov si,undi_data_msg
408 call writestr
409 mov ax,[es:bx+20h]
410 call writehex4
411 call crlf
412 mov si,undi_data_len_msg
413 call writestr
414 mov ax,[es:bx+22h]
415 call writehex4
416 call crlf
417 mov si,undi_code_msg
418 call writestr
419 mov ax,[es:bx+24h]
420 call writehex4
421 call crlf
422 mov si,undi_code_len_msg
423 call writestr
424 mov ax,[es:bx+26h]
425 call writehex4
426 call crlf
428 ; Compute base memory size from PXENV+ structure
429 xor esi,esi
430 movzx eax,word [es:bx+20h] ; UNDI data seg
431 cmp ax,[es:bx+24h] ; UNDI code seg
432 ja .use_data
433 mov ax,[es:bx+24h]
434 mov si,[es:bx+26h]
435 jmp short .combine
436 .use_data:
437 mov si,[es:bx+22h]
438 .combine:
439 shl eax,4
440 add eax,esi
441 shr eax,10 ; Convert to kilobytes
442 mov [RealBaseMem],ax
444 mov si,pxenventry_msg
445 call writestr
446 mov ax,[PXENVEntry+2]
447 call writehex4
448 mov al,':'
449 call writechr
450 mov ax,[PXENVEntry]
451 call writehex4
452 call crlf
453 jmp have_entrypoint
455 have_pxe:
456 mov [StrucPtr],bx
457 mov [StrucPtr+2],es
459 mov eax,[es:bx+10h]
460 mov [PXEEntry],eax
462 mov si,undi_data_msg
463 call writestr
464 mov eax,[es:bx+2Ah]
465 call writehex8
466 call crlf
467 mov si,undi_data_len_msg
468 call writestr
469 mov ax,[es:bx+2Eh]
470 call writehex4
471 call crlf
472 mov si,undi_code_msg
473 call writestr
474 mov ax,[es:bx+32h]
475 call writehex8
476 call crlf
477 mov si,undi_code_len_msg
478 call writestr
479 mov ax,[es:bx+36h]
480 call writehex4
481 call crlf
483 ; Compute base memory size from !PXE structure
484 xor esi,esi
485 mov eax,[es:bx+2Ah]
486 cmp eax,[es:bx+32h]
487 ja .use_data
488 mov eax,[es:bx+32h]
489 mov si,[es:bx+36h]
490 jmp short .combine
491 .use_data:
492 mov si,[es:bx+2Eh]
493 .combine:
494 add eax,esi
495 shr eax,10
496 mov [RealBaseMem],ax
498 mov si,pxeentry_msg
499 call writestr
500 mov ax,[PXEEntry+2]
501 call writehex4
502 mov al,':'
503 call writechr
504 mov ax,[PXEEntry]
505 call writehex4
506 call crlf
508 have_entrypoint:
509 push cs
510 pop es ; Restore CS == DS == ES
513 ; Network-specific initialization
515 xor ax,ax
516 mov [LocalDomain],al ; No LocalDomain received
519 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
520 ; packet (query info 1).
522 query_bootp_1:
523 mov dl,1
524 call pxe_get_cached_info
525 call parse_dhcp
527 ; We don't use flags from the request packet, so
528 ; this is a good time to initialize DHCPMagic...
529 ; Initialize it to 1 meaning we will accept options found;
530 ; in earlier versions of PXELINUX bit 0 was used to indicate
531 ; we have found option 208 with the appropriate magic number;
532 ; we no longer require that, but MAY want to re-introduce
533 ; it in the future for vendor encapsulated options.
534 mov byte [DHCPMagic],1
537 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
538 ; address). This lives in the DHCPACK packet (query info 2).
540 query_bootp_2:
541 mov dl,2
542 call pxe_get_cached_info
543 call parse_dhcp ; Parse DHCP packet
545 ; Save away MAC address (assume this is in query info 2. If this
546 ; turns out to be problematic it might be better getting it from
547 ; the query info 1 packet.)
549 .save_mac:
550 movzx cx,byte [trackbuf+bootp.hardlen]
551 cmp cx,16
552 jna .mac_ok
553 xor cx,cx ; Bad hardware address length
554 .mac_ok:
555 mov [MACLen],cl
556 mov al,[trackbuf+bootp.hardware]
557 mov [MACType],al
558 mov si,trackbuf+bootp.macaddr
559 mov di,MAC
560 rep movsb
562 ; Enable this if we really need to zero-pad this field...
563 ; mov cx,MAC+MAC_MAX+1
564 ; sub cx,di
565 ; xor ax,ax
566 ; rep stosb
569 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
570 ; packet (query info 3).
572 mov dl,3
573 call pxe_get_cached_info
574 call parse_dhcp ; Parse DHCP packet
577 ; Generate the bootif string, and the hardware-based config string.
579 make_bootif_string:
580 mov si,bootif_str
581 mov di,BOOTIFStr
582 mov cx,bootif_str_len
583 rep movsb
585 movzx cx,byte [MACLen]
586 mov si,MACType
587 inc cx
588 .hexify_mac:
589 push cx
590 mov cl,1 ; CH == 0 already
591 call lchexbytes
592 mov al,'-'
593 stosb
594 pop cx
595 loop .hexify_mac
596 mov [di-1],cl ; Null-terminate and strip final dash
598 ; Generate ip= option
600 call genipopt
603 ; Print IP address
605 mov eax,[MyIP]
606 mov di,DotQuadBuf
607 push di
608 call gendotquad ; This takes network byte order input
610 xchg ah,al ; Convert to host byte order
611 ror eax,16 ; (BSWAP doesn't work on 386)
612 xchg ah,al
614 mov si,myipaddr_msg
615 call writestr
616 call writehex8
617 mov al,' '
618 call writechr
619 pop si ; DotQuadBuf
620 call writestr
621 call crlf
623 mov si,IPOption
624 call writestr
625 call crlf
628 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
629 ; if we didn't get the magic enable, do not recognize any other options.
631 check_dhcp_magic:
632 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
633 jnz .got_magic
634 mov byte [DHCPMagic], 0 ; If not, kill all other options
635 .got_magic:
639 ; Initialize UDP stack
641 udp_init:
642 mov eax,[MyIP]
643 mov [pxe_udp_open_pkt.sip],eax
644 mov di,pxe_udp_open_pkt
645 mov bx,PXENV_UDP_OPEN
646 call pxenv
647 jc .failed
648 cmp word [pxe_udp_open_pkt.status], byte 0
649 je .success
650 .failed: mov si,err_udpinit
651 call writestr
652 jmp kaboom
653 .success:
656 ; Common initialization code
658 %include "cpuinit.inc"
661 ; Now we're all set to start with our *real* business. First load the
662 ; configuration file (if any) and parse it.
664 ; In previous versions I avoided using 32-bit registers because of a
665 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
666 ; random. I figure, though, that if there are any of those still left
667 ; they probably won't be trying to install Linux on them...
669 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
670 ; to take'm out. In fact, we may want to put them back if we're going
671 ; to boot ELKS at some point.
675 ; Store standard filename prefix
677 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
678 jnz .got_prefix
679 mov si,BootFile
680 mov di,PathPrefix
682 call strcpy
683 mov cx,di
684 sub cx,PathPrefix+1
686 lea si,[di-2] ; Skip final null!
687 .find_alnum: lodsb
688 or al,20h
689 cmp al,'.' ; Count . or - as alphanum
690 je .alnum
691 cmp al,'-'
692 je .alnum
693 cmp al,'0'
694 jb .notalnum
695 cmp al,'9'
696 jbe .alnum
697 cmp al,'a'
698 jb .notalnum
699 cmp al,'z'
700 ja .notalnum
701 .alnum: loop .find_alnum
702 dec si
703 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
705 .got_prefix:
706 mov si,tftpprefix_msg
707 call writestr
708 mov si,PathPrefix
709 call writestr
710 call crlf
713 ; Load configuration file
715 find_config:
718 ; Begin looking for configuration file
720 config_scan:
721 test byte [DHCPMagic], 02h
722 jz .no_option
724 ; We got a DHCP option, try it first
725 call .try
726 jnz .success
728 .no_option:
729 mov di,ConfigName
730 mov si,cfgprefix
731 mov cx,cfgprefix_len
732 rep movsb
734 ; Have to guess config file name...
736 ; Try loading by UUID.
737 cmp byte [HaveUUID],0
738 je .no_uuid
740 push di
741 mov bx,uuid_dashes
742 mov si,UUID
743 .gen_uuid:
744 movzx cx,byte [bx]
745 jcxz .done_uuid
746 inc bx
747 call lchexbytes
748 mov al,'-'
749 stosb
750 jmp .gen_uuid
751 .done_uuid:
752 mov [di-1],cl ; Remove last dash and zero-terminate
753 pop di
754 call .try
755 jnz .success
756 .no_uuid:
758 ; Try loading by MAC address
759 push di
760 mov si,MACStr
761 call strcpy
762 pop di
763 call .try
764 jnz .success
766 ; Nope, try hexadecimal IP prefixes...
767 .scan_ip:
768 mov cx,4
769 mov si,MyIP
770 call uchexbytes ; Convert to hex string
772 mov cx,8 ; Up to 8 attempts
773 .tryagain:
774 mov byte [di],0 ; Zero-terminate string
775 call .try
776 jnz .success
777 dec di ; Drop one character
778 loop .tryagain
780 ; Final attempt: "default" string
781 mov si,default_str ; "default" string
782 call strcpy
783 call .try
784 jnz .success
786 mov si,err_noconfig
787 call writestr
788 jmp kaboom
790 .try:
791 pusha
792 mov si,trying_msg
793 call writestr
794 mov di,ConfigName
795 mov si,di
796 call writestr
797 call crlf
798 mov si,di
799 mov di,KernelName ; Borrow this buffer for mangled name
800 call mangle_name
801 call open
802 popa
806 .success:
809 ; Linux kernel loading code is common. However, we need to define
810 ; a couple of helper macros...
813 ; Handle "ipappend" option
814 %define HAVE_SPECIAL_APPEND
815 %macro SPECIAL_APPEND 0
816 test byte [IPAppend],01h ; ip=
817 jz .noipappend1
818 mov si,IPOption
819 mov cx,[IPOptionLen]
820 rep movsb
821 mov al,' '
822 stosb
823 .noipappend1:
824 test byte [IPAppend],02h
825 jz .noipappend2
826 mov si,BOOTIFStr
827 call strcpy
828 mov byte [es:di-1],' ' ; Replace null with space
829 .noipappend2:
830 %endmacro
832 ; Unload PXE stack
833 %define HAVE_UNLOAD_PREP
834 %macro UNLOAD_PREP 0
835 call unload_pxe
836 %endmacro
839 ; Now we have the config file open. Parse the config file and
840 ; run the user interface.
842 %include "ui.inc"
845 ; Boot to the local disk by returning the appropriate PXE magic.
846 ; AX contains the appropriate return code.
848 %if HAS_LOCALBOOT
850 local_boot:
851 push cs
852 pop ds
853 mov [LocalBootType],ax
854 call vgaclearmode
855 mov si,localboot_msg
856 call writestr
857 ; Restore the environment we were called with
858 lss sp,[InitStack]
859 pop gs
860 pop fs
861 pop es
862 pop ds
863 popad
864 mov ax,[cs:LocalBootType]
865 popfd
866 retf ; Return to PXE
868 %endif
871 ; kaboom: write a message and bail out. Wait for quite a while,
872 ; or a user keypress, then do a hard reboot.
874 kaboom:
875 RESET_STACK_AND_SEGS AX
876 .patch: mov si,bailmsg
877 call writestr ; Returns with AL = 0
878 .drain: call pollchar
879 jz .drained
880 call getchar
881 jmp short .drain
882 .drained:
883 mov edi,[RebootTime]
884 mov al,[DHCPMagic]
885 and al,09h ; Magic+Timeout
886 cmp al,09h
887 je .time_set
888 mov edi,REBOOT_TIME
889 .time_set:
890 mov cx,18
891 .wait1: push cx
892 mov ecx,edi
893 .wait2: mov dx,[BIOS_timer]
894 .wait3: call pollchar
895 jnz .keypress
896 cmp dx,[BIOS_timer]
897 je .wait3
898 loop .wait2,ecx
899 mov al,'.'
900 call writechr
901 pop cx
902 loop .wait1
903 .keypress:
904 call crlf
905 mov word [BIOS_magic],0 ; Cold reboot
906 jmp 0F000h:0FFF0h ; Reset vector address
909 ; memory_scan_for_pxe_struct:
911 ; If none of the standard methods find the !PXE structure, look for it
912 ; by scanning memory.
914 ; On exit, if found:
915 ; CF = 0, ES:BX -> !PXE structure
916 ; Otherwise CF = 1, all registers saved
918 memory_scan_for_pxe_struct:
919 push ds
920 pusha
921 mov ax,cs
922 mov ds,ax
923 mov si,trymempxe_msg
924 call writestr
925 mov ax,[BIOS_fbm] ; Starting segment
926 shl ax,(10-4) ; Kilobytes -> paragraphs
927 ; mov ax,01000h ; Start to look here
928 dec ax ; To skip inc ax
929 .mismatch:
930 inc ax
931 cmp ax,0A000h ; End of memory
932 jae .not_found
933 call writehex4
934 mov si,fourbs_msg
935 call writestr
936 mov es,ax
937 mov edx,[es:0]
938 cmp edx,'!PXE'
939 jne .mismatch
940 movzx cx,byte [es:4] ; Length of structure
941 cmp cl,08h ; Minimum length
942 jb .mismatch
943 push ax
944 xor ax,ax
945 xor si,si
946 .checksum: es lodsb
947 add ah,al
948 loop .checksum
949 pop ax
950 jnz .mismatch ; Checksum must == 0
951 .found: mov bp,sp
952 xor bx,bx
953 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
954 mov ax,es
955 call writehex4
956 call crlf
957 popa
958 pop ds
961 .not_found: mov si,notfound_msg
962 call writestr
963 popa
964 pop ds
969 ; memory_scan_for_pxenv_struct:
971 ; If none of the standard methods find the PXENV+ structure, look for it
972 ; by scanning memory.
974 ; On exit, if found:
975 ; CF = 0, ES:BX -> PXENV+ structure
976 ; Otherwise CF = 1, all registers saved
978 memory_scan_for_pxenv_struct:
979 pusha
980 mov si,trymempxenv_msg
981 call writestr
982 ; mov ax,[BIOS_fbm] ; Starting segment
983 ; shl ax,(10-4) ; Kilobytes -> paragraphs
984 mov ax,01000h ; Start to look here
985 dec ax ; To skip inc ax
986 .mismatch:
987 inc ax
988 cmp ax,0A000h ; End of memory
989 jae .not_found
990 mov es,ax
991 mov edx,[es:0]
992 cmp edx,'PXEN'
993 jne .mismatch
994 mov dx,[es:4]
995 cmp dx,'V+'
996 jne .mismatch
997 movzx cx,byte [es:8] ; Length of structure
998 cmp cl,26h ; Minimum length
999 jb .mismatch
1000 xor ax,ax
1001 xor si,si
1002 .checksum: es lodsb
1003 add ah,al
1004 loop .checksum
1005 and ah,ah
1006 jnz .mismatch ; Checksum must == 0
1007 .found: mov bp,sp
1008 mov [bp+8],bx ; Save BX into stack frame
1009 mov ax,bx
1010 call writehex4
1011 call crlf
1014 .not_found: mov si,notfound_msg
1015 call writestr
1016 popad
1021 ; close_file:
1022 ; Deallocates a file structure (pointer in SI)
1023 ; Assumes CS == DS.
1025 ; XXX: We should check to see if this file is still open on the server
1026 ; side and send a courtesy ERROR packet to the server.
1028 close_file:
1029 and si,si
1030 jz .closed
1031 mov word [si],0 ; Not in use
1032 .closed: ret
1035 ; searchdir:
1037 ; Open a TFTP connection to the server
1039 ; On entry:
1040 ; DS:DI = mangled filename
1041 ; If successful:
1042 ; ZF clear
1043 ; SI = socket pointer
1044 ; DX:AX = file length in bytes
1045 ; If unsuccessful
1046 ; ZF set
1049 searchdir:
1050 push es
1051 push bx
1052 push cx
1053 mov ax,ds
1054 mov es,ax
1055 mov si,di
1056 push bp
1057 mov bp,sp
1059 call allocate_socket
1060 jz .ret
1062 mov ax,PKT_RETRY ; Retry counter
1063 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1065 .sendreq: push ax ; [bp-2] - Retry counter
1066 push si ; [bp-4] - File name
1068 mov di,packet_buf
1069 mov [pxe_udp_write_pkt.buffer],di
1071 mov ax,TFTP_RRQ ; TFTP opcode
1072 stosw
1074 lodsd ; EAX <- server override (if any)
1075 and eax,eax
1076 jnz .noprefix ; No prefix, and we have the server
1078 push si ; Add common prefix
1079 mov si,PathPrefix
1080 call strcpy
1081 dec di
1082 pop si
1084 mov eax,[ServerIP] ; Get default server
1086 .noprefix:
1087 call strcpy ; Filename
1089 mov [bx+tftp_remoteip],eax
1091 push bx ; [bp-6] - TFTP block
1092 mov bx,[bx]
1093 push bx ; [bp-8] - TID (local port no)
1095 mov [pxe_udp_write_pkt.status],byte 0
1096 mov [pxe_udp_write_pkt.sip],eax
1097 ; Now figure out the gateway
1098 xor eax,[MyIP]
1099 and eax,[Netmask]
1100 jz .nogwneeded
1101 mov eax,[Gateway]
1102 .nogwneeded:
1103 mov [pxe_udp_write_pkt.gip],eax
1104 mov [pxe_udp_write_pkt.lport],bx
1105 mov ax,[ServerPort]
1106 mov [pxe_udp_write_pkt.rport],ax
1107 mov si,tftp_tail
1108 mov cx,tftp_tail_len
1109 rep movsb
1110 sub di,packet_buf ; Get packet size
1111 mov [pxe_udp_write_pkt.buffersize],di
1113 mov di,pxe_udp_write_pkt
1114 mov bx,PXENV_UDP_WRITE
1115 call pxenv
1116 jc .failure
1117 cmp word [pxe_udp_write_pkt.status],byte 0
1118 jne .failure
1121 ; Danger, Will Robinson! We need to support timeout
1122 ; and retry lest we just lost a packet...
1125 ; Packet transmitted OK, now we need to receive
1126 .getpacket: push word [PktTimeout] ; [bp-10]
1127 push word [BIOS_timer] ; [bp-12]
1129 .pkt_loop: mov bx,[bp-8] ; TID
1130 mov di,packet_buf
1131 mov word [pxe_udp_read_pkt.status],0
1132 mov [pxe_udp_read_pkt.buffer],di
1133 mov [pxe_udp_read_pkt.buffer+2],ds
1134 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1135 mov eax,[MyIP]
1136 mov [pxe_udp_read_pkt.dip],eax
1137 mov [pxe_udp_read_pkt.lport],bx
1138 mov di,pxe_udp_read_pkt
1139 mov bx,PXENV_UDP_READ
1140 call pxenv
1141 and ax,ax
1142 jz .got_packet ; Wait for packet
1143 .no_packet:
1144 mov dx,[BIOS_timer]
1145 cmp dx,[bp-12]
1146 je .pkt_loop
1147 mov [bp-12],dx
1148 dec word [bp-10] ; Timeout
1149 jnz .pkt_loop
1150 pop ax ; Adjust stack
1151 pop ax
1152 shl word [PktTimeout],1 ; Exponential backoff
1153 jmp .failure
1155 .got_packet:
1156 mov si,[bp-6] ; TFTP pointer
1157 mov bx,[bp-8] ; TID
1159 mov eax,[si+tftp_remoteip]
1160 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1161 jne .no_packet
1163 ; Got packet - reset timeout
1164 mov word [PktTimeout],PKT_TIMEOUT
1166 pop ax ; Adjust stack
1167 pop ax
1169 mov ax,[pxe_udp_read_pkt.rport]
1170 mov [si+tftp_remoteport],ax
1172 ; filesize <- -1 == unknown
1173 mov dword [si+tftp_filesize], -1
1174 ; Default blksize unless blksize option negotiated
1175 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1177 mov cx,[pxe_udp_read_pkt.buffersize]
1178 sub cx,2 ; CX <- bytes after opcode
1179 jb .failure ; Garbled reply
1181 mov si,packet_buf
1182 lodsw
1184 cmp ax, TFTP_ERROR
1185 je .bailnow ; ERROR reply: don't try again
1187 cmp ax, TFTP_OACK
1188 jne .no_tsize
1190 ; Now we need to parse the OACK packet to get the transfer
1191 ; size. SI -> first byte of options; CX -> byte count
1192 .parse_oack:
1193 jcxz .no_tsize ; No options acked
1194 .get_opt_name:
1195 mov di,si
1196 mov bx,si
1197 .opt_name_loop: lodsb
1198 and al,al
1199 jz .got_opt_name
1200 or al,20h ; Convert to lowercase
1201 stosb
1202 loop .opt_name_loop
1203 ; We ran out, and no final null
1204 jmp .err_reply
1205 .got_opt_name: ; si -> option value
1206 dec cx ; bytes left in pkt
1207 jz .err_reply ; Option w/o value
1209 ; Parse option pointed to by bx; guaranteed to be
1210 ; null-terminated.
1211 push cx
1212 push si
1213 mov si,bx ; -> option name
1214 mov bx,tftp_opt_table
1215 mov cx,tftp_opts
1216 .opt_loop:
1217 push cx
1218 push si
1219 mov di,[bx] ; Option pointer
1220 mov cx,[bx+2] ; Option len
1221 repe cmpsb
1222 pop si
1223 pop cx
1224 je .get_value ; OK, known option
1225 add bx,6
1226 loop .opt_loop
1228 pop si
1229 pop cx
1230 jmp .err_reply ; Non-negotiated option returned
1232 .get_value: pop si ; si -> option value
1233 pop cx ; cx -> bytes left in pkt
1234 mov bx,[bx+4] ; Pointer to data target
1235 add bx,[bp-6] ; TFTP socket pointer
1236 xor eax,eax
1237 xor edx,edx
1238 .value_loop: lodsb
1239 and al,al
1240 jz .got_value
1241 sub al,'0'
1242 cmp al, 9
1243 ja .err_reply ; Not a decimal digit
1244 imul edx,10
1245 add edx,eax
1246 mov [bx],edx
1247 loop .value_loop
1248 ; Ran out before final null, accept anyway
1249 jmp short .done_pkt
1251 .got_value:
1252 dec cx
1253 jnz .get_opt_name ; Not end of packet
1255 ; ZF == 1
1257 ; Success, done!
1258 .done_pkt:
1259 pop si ; Junk
1260 pop si ; We want the packet ptr in SI
1262 mov eax,[si+tftp_filesize]
1263 cmp eax,-1
1264 jz .no_tsize
1265 mov edx,eax
1266 shr edx,16 ; DX:AX == EAX
1268 and eax,eax ; Set ZF depending on file size
1269 pop bp ; Junk
1270 pop bp ; Junk (retry counter)
1271 jz .error_si ; ZF = 1 need to free the socket
1272 .ret:
1273 pop bp
1274 pop cx
1275 pop bx
1276 pop es
1279 .no_tsize:
1280 .err_reply: ; Option negotiation error. Send ERROR reply.
1281 ; ServerIP and gateway are already programmed in
1282 mov si,[bp-6]
1283 mov ax,[si+tftp_remoteport]
1284 mov word [pxe_udp_write_pkt.rport],ax
1285 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1286 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1287 mov di,pxe_udp_write_pkt
1288 mov bx,PXENV_UDP_WRITE
1289 call pxenv
1291 ; Write an error message and explode
1292 mov si,err_oldtftp
1293 call writestr
1294 jmp kaboom
1296 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1298 .failure: pop bx ; Junk
1299 pop bx
1300 pop si
1301 pop ax
1302 dec ax ; Retry counter
1303 jnz .sendreq ; Try again
1305 .error: mov si,bx ; Socket pointer
1306 .error_si: ; Socket pointer already in SI
1307 call free_socket ; ZF <- 1, SI <- 0
1308 jmp .ret
1311 ; allocate_socket: Allocate a local UDP port structure
1313 ; If successful:
1314 ; ZF set
1315 ; BX = socket pointer
1316 ; If unsuccessful:
1317 ; ZF clear
1319 allocate_socket:
1320 push cx
1321 mov bx,Files
1322 mov cx,MAX_OPEN
1323 .check: cmp word [bx], byte 0
1324 je .found
1325 add bx,open_file_t_size
1326 loop .check
1327 xor cx,cx ; ZF = 1
1328 pop cx
1330 ; Allocate a socket number. Socket numbers are made
1331 ; guaranteed unique by including the socket slot number
1332 ; (inverted, because we use the loop counter cx); add a
1333 ; counter value to keep the numbers from being likely to
1334 ; get immediately reused.
1336 ; The NextSocket variable also contains the top two bits
1337 ; set. This generates a value in the range 49152 to
1338 ; 57343.
1339 .found:
1340 dec cx
1341 push ax
1342 mov ax,[NextSocket]
1343 inc ax
1344 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1345 mov [NextSocket],ax
1346 shl cx,13-MAX_OPEN_LG2
1347 add cx,ax ; ZF = 0
1348 xchg ch,cl ; Convert to network byte order
1349 mov [bx],cx ; Socket in use
1350 pop ax
1351 pop cx
1355 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1357 free_socket:
1358 push es
1359 pusha
1360 xor ax,ax
1361 mov es,ax
1362 mov di,si
1363 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1364 rep stosw
1365 popa
1366 pop es
1367 xor si,si
1371 ; parse_dotquad:
1372 ; Read a dot-quad pathname in DS:SI and output an IP
1373 ; address in EAX, with SI pointing to the first
1374 ; nonmatching character.
1376 ; Return CF=1 on error.
1378 ; No segment assumptions permitted.
1380 parse_dotquad:
1381 push cx
1382 mov cx,4
1383 xor eax,eax
1384 .parseloop:
1385 mov ch,ah
1386 mov ah,al
1387 lodsb
1388 sub al,'0'
1389 jb .notnumeric
1390 cmp al,9
1391 ja .notnumeric
1392 aad ; AL += 10 * AH; AH = 0;
1393 xchg ah,ch
1394 jmp .parseloop
1395 .notnumeric:
1396 cmp al,'.'-'0'
1397 pushf
1398 mov al,ah
1399 mov ah,ch
1400 xor ch,ch
1401 ror eax,8
1402 popf
1403 jne .error
1404 loop .parseloop
1405 jmp .done
1406 .error:
1407 loop .realerror ; If CX := 1 then we're done
1409 jmp .done
1410 .realerror:
1412 .done:
1413 dec si ; CF unchanged!
1414 pop cx
1417 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1418 ; to by ES:DI; ends on encountering any whitespace.
1419 ; DI is preserved.
1421 ; This verifies that a filename is < FILENAME_MAX characters
1422 ; and doesn't contain whitespace, and zero-pads the output buffer,
1423 ; so "repe cmpsb" can do a compare.
1425 ; The first four bytes of the manged name is the IP address of
1426 ; the download host.
1428 ; No segment assumptions permitted.
1430 mangle_name:
1431 push di
1432 push si
1433 mov eax,[cs:ServerIP]
1434 cmp byte [si],0
1435 je .noip ; Null filename?!?!
1436 cmp word [si],'::' ; Leading ::?
1437 je .gotprefix
1439 .more:
1440 inc si
1441 cmp byte [si],0
1442 je .noip
1443 cmp word [si],'::'
1444 jne .more
1446 ; We have a :: prefix of some sort, it could be either
1447 ; a DNS name or a dot-quad IP address. Try the dot-quad
1448 ; first...
1449 .here:
1450 pop si
1451 push si
1452 call parse_dotquad
1453 jc .notdq
1454 cmp word [si],'::'
1455 je .gotprefix
1456 .notdq:
1457 pop si
1458 push si
1459 call dns_resolv
1460 cmp word [si],'::'
1461 jne .noip
1462 and eax,eax
1463 jnz .gotprefix
1465 .noip:
1466 pop si
1467 xor eax,eax
1468 jmp .prefix_done
1470 .gotprefix:
1471 pop cx ; Adjust stack
1472 inc si ; Skip double colon
1473 inc si
1475 .prefix_done:
1476 stosd ; Save IP address prefix
1477 mov cx,FILENAME_MAX-5
1479 .mn_loop:
1480 lodsb
1481 cmp al,' ' ; If control or space, end
1482 jna .mn_end
1483 stosb
1484 loop .mn_loop
1485 .mn_end:
1486 inc cx ; At least one null byte
1487 xor ax,ax ; Zero-fill name
1488 rep stosb ; Doesn't do anything if CX=0
1489 pop di
1490 ret ; Done
1493 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1494 ; filename to the conventional representation. This is needed
1495 ; for the BOOT_IMAGE= parameter for the kernel.
1497 ; NOTE: The output buffer needs to be able to hold an
1498 ; expanded IP address.
1500 ; DS:SI -> input mangled file name
1501 ; ES:DI -> output buffer
1503 ; On return, DI points to the first byte after the output name,
1504 ; which is set to a null byte.
1506 unmangle_name:
1507 push eax
1508 lodsd
1509 and eax,eax
1510 jz .noip
1511 call gendotquad
1512 mov ax,'::'
1513 stosw
1514 .noip:
1515 call strcpy
1516 dec di ; Point to final null byte
1517 pop eax
1521 ; pxenv
1523 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1524 ; calling convention. This is a separate local routine so
1525 ; we can hook special things from it if necessary. In particular,
1526 ; some PXE stacks seem to not like being invoked from anything but
1527 ; the initial stack, so humour it.
1530 pxenv:
1531 %if USE_PXE_PROVIDED_STACK == 0
1532 mov [cs:PXEStack],sp
1533 mov [cs:PXEStack+2],ss
1534 lss sp,[cs:InitStack]
1535 %endif
1536 .jump: call 0:pxe_thunk ; Default to calling the thunk
1537 %if USE_PXE_PROVIDED_STACK == 0
1538 lss sp,[cs:PXEStack]
1539 %endif
1540 cld ; Make sure DF <- 0
1543 ; Must be after function def due to NASM bug
1544 PXENVEntry equ pxenv.jump+1
1547 ; pxe_thunk
1549 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1550 ; calling convention (using the stack.)
1552 ; This is called as a far routine so that we can just stick it into
1553 ; the PXENVEntry variable.
1555 pxe_thunk: push es
1556 push di
1557 push bx
1558 .jump: call 0:0
1559 add sp,byte 6
1560 cmp ax,byte 1
1561 cmc ; Set CF unless ax == 0
1562 retf
1564 ; Must be after function def due to NASM bug
1565 PXEEntry equ pxe_thunk.jump+1
1568 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1570 ; In this case, get multiple blocks from a specific TCP connection.
1572 ; On entry:
1573 ; ES:BX -> Buffer
1574 ; SI -> TFTP socket pointer
1575 ; CX -> 512-byte block count; 0FFFFh = until end of file
1576 ; On exit:
1577 ; SI -> TFTP socket pointer (or 0 on EOF)
1578 ; CF = 1 -> Hit EOF
1580 getfssec: push si
1581 push fs
1582 mov di,bx
1583 mov bx,si
1584 mov ax,pktbuf_seg
1585 mov fs,ax
1587 movzx ecx,cx
1588 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1589 jz .hit_eof ; Nothing to do?
1591 .need_more:
1592 push ecx
1594 movzx eax,word [bx+tftp_bytesleft]
1595 cmp ecx,eax
1596 jna .ok_size
1597 mov ecx,eax
1598 jcxz .need_packet ; No bytes available?
1599 .ok_size:
1601 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1602 mov si,[bx+tftp_dataptr]
1603 sub [bx+tftp_bytesleft],cx
1604 fs rep movsb ; Copy from packet buffer
1605 mov [bx+tftp_dataptr],si
1607 pop ecx
1608 sub ecx,eax
1609 jnz .need_more
1612 .hit_eof:
1613 pop fs
1614 pop si
1616 ; Is there anything left of this?
1617 mov eax,[si+tftp_filesize]
1618 sub eax,[si+tftp_filepos]
1619 jnz .bytes_left ; CF <- 0
1621 cmp [si+tftp_bytesleft],ax
1622 jnz .bytes_left ; CF <- 0
1624 ; The socket is closed and the buffer drained
1625 ; Close socket structure and re-init for next user
1626 call free_socket
1628 .bytes_left:
1632 ; No data in buffer, check to see if we can get a packet...
1634 .need_packet:
1635 pop ecx
1636 mov eax,[bx+tftp_filesize]
1637 cmp eax,[bx+tftp_filepos]
1638 je .hit_eof ; Already EOF'd; socket already closed
1640 pushad
1641 push es
1642 mov si,bx
1643 call get_packet
1644 pop es
1645 popad
1647 jmp .need_more
1650 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1652 get_packet:
1653 mov ax,ds
1654 mov es,ax
1656 .packet_loop:
1657 ; Start by ACKing the previous packet; this should cause the
1658 ; next packet to be sent.
1659 mov cx,PKT_RETRY
1660 mov word [PktTimeout],PKT_TIMEOUT
1662 .send_ack: push cx ; <D> Retry count
1664 mov ax,[si+tftp_lastpkt]
1665 call ack_packet ; Send ACK
1667 ; We used to test the error code here, but sometimes
1668 ; PXE would return negative status even though we really
1669 ; did send the ACK. Now, just treat a failed send as
1670 ; a normally lost packet, and let it time out in due
1671 ; course of events.
1673 .send_ok: ; Now wait for packet.
1674 mov dx,[BIOS_timer] ; Get current time
1676 mov cx,[PktTimeout]
1677 .wait_data: push cx ; <E> Timeout
1678 push dx ; <F> Old time
1680 mov bx,[si+tftp_pktbuf]
1681 mov [pxe_udp_read_pkt.buffer],bx
1682 mov [pxe_udp_read_pkt.buffer+2],fs
1683 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1684 mov eax,[si+tftp_remoteip]
1685 mov [pxe_udp_read_pkt.sip],eax
1686 mov eax,[MyIP]
1687 mov [pxe_udp_read_pkt.dip],eax
1688 mov ax,[si+tftp_remoteport]
1689 mov [pxe_udp_read_pkt.rport],ax
1690 mov ax,[si+tftp_localport]
1691 mov [pxe_udp_read_pkt.lport],ax
1692 mov di,pxe_udp_read_pkt
1693 mov bx,PXENV_UDP_READ
1694 push si ; <G>
1695 call pxenv
1696 pop si ; <G>
1697 and ax,ax
1698 jz .recv_ok
1700 ; No packet, or receive failure
1701 mov dx,[BIOS_timer]
1702 pop ax ; <F> Old time
1703 pop cx ; <E> Timeout
1704 cmp ax,dx ; Same time -> don't advance timeout
1705 je .wait_data ; Same clock tick
1706 loop .wait_data ; Decrease timeout
1708 pop cx ; <D> Didn't get any, send another ACK
1709 shl word [PktTimeout],1 ; Exponential backoff
1710 loop .send_ack
1711 jmp kaboom ; Forget it...
1713 .recv_ok: pop dx ; <F>
1714 pop cx ; <E>
1716 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1717 jb .wait_data ; Bad size for a DATA packet
1719 mov bx,[si+tftp_pktbuf]
1720 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1721 jne .wait_data ; Then wait for something else
1723 mov ax,[si+tftp_lastpkt]
1724 xchg ah,al ; Host byte order
1725 inc ax ; Which packet are we waiting for?
1726 xchg ah,al ; Network byte order
1727 cmp [fs:bx+2],ax
1728 je .right_packet
1730 ; Wrong packet, ACK the packet and then try again
1731 ; This is presumably because the ACK got lost,
1732 ; so the server just resent the previous packet
1733 mov ax,[fs:bx+2]
1734 call ack_packet
1735 jmp .send_ok ; Reset timeout
1737 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1739 pop cx ; <D> Don't need the retry count anymore
1741 mov [si+tftp_lastpkt],ax ; Update last packet number
1743 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1744 sub cx,byte 4 ; Skip TFTP header
1746 ; If this is a zero-length block, don't mess with the pointers,
1747 ; since we may have just set up the previous block that way
1748 jz .last_block
1750 ; Set pointer to data block
1751 lea ax,[bx+4] ; Data past TFTP header
1752 mov [si+tftp_dataptr],ax
1754 add [si+tftp_filepos],ecx
1755 mov [si+tftp_bytesleft],cx
1757 cmp cx,[si+tftp_blksize] ; Is it a full block?
1758 jb .last_block ; If so, it's not EOF
1760 ; If we had the exact right number of bytes, always get
1761 ; one more packet to get the (zero-byte) EOF packet and
1762 ; close the socket.
1763 mov eax,[si+tftp_filepos]
1764 cmp [si+tftp_filesize],eax
1765 je .packet_loop
1770 .last_block: ; Last block - ACK packet immediately
1771 mov ax,[fs:bx+2]
1772 call ack_packet
1774 ; Make sure we know we are at end of file
1775 mov eax,[si+tftp_filepos]
1776 mov [si+tftp_filesize],eax
1781 ; ack_packet:
1783 ; Send ACK packet. This is a common operation and so is worth canning.
1785 ; Entry:
1786 ; SI = TFTP block
1787 ; AX = Packet # to ack (network byte order)
1788 ; Exit:
1789 ; ZF = 0 -> Error
1790 ; All registers preserved
1792 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1794 ack_packet:
1795 pushad
1796 mov [ack_packet_buf+2],ax ; Packet number to ack
1797 mov ax,[si]
1798 mov [pxe_udp_write_pkt.lport],ax
1799 mov ax,[si+tftp_remoteport]
1800 mov [pxe_udp_write_pkt.rport],ax
1801 mov eax,[si+tftp_remoteip]
1802 mov [pxe_udp_write_pkt.sip],eax
1803 xor eax,[MyIP]
1804 and eax,[Netmask]
1805 jz .nogw
1806 mov eax,[Gateway]
1807 .nogw:
1808 mov [pxe_udp_write_pkt.gip],eax
1809 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1810 mov [pxe_udp_write_pkt.buffersize], word 4
1811 mov di,pxe_udp_write_pkt
1812 mov bx,PXENV_UDP_WRITE
1813 call pxenv
1814 cmp ax,byte 0 ; ZF = 1 if write OK
1815 popad
1819 ; unload_pxe:
1821 ; This function unloads the PXE and UNDI stacks and unclaims
1822 ; the memory.
1824 unload_pxe:
1825 test byte [KeepPXE],01h ; Should we keep PXE around?
1826 jnz reset_pxe
1828 push ds
1829 push es
1831 mov ax,cs
1832 mov ds,ax
1833 mov es,ax
1835 mov si,new_api_unload
1836 cmp byte [APIVer+1],2 ; Major API version >= 2?
1837 jae .new_api
1838 mov si,old_api_unload
1839 .new_api:
1841 .call_loop: xor ax,ax
1842 lodsb
1843 and ax,ax
1844 jz .call_done
1845 xchg bx,ax
1846 mov di,pxe_unload_stack_pkt
1847 push di
1848 xor ax,ax
1849 mov cx,pxe_unload_stack_pkt_len >> 1
1850 rep stosw
1851 pop di
1852 call pxenv
1853 jc .cant_free
1854 mov ax,word [pxe_unload_stack_pkt.status]
1855 cmp ax,PXENV_STATUS_SUCCESS
1856 jne .cant_free
1857 jmp .call_loop
1859 .call_done:
1860 mov bx,0FF00h
1862 mov dx,[RealBaseMem]
1863 cmp dx,[BIOS_fbm] ; Sanity check
1864 jna .cant_free
1865 inc bx
1867 ; Check that PXE actually unhooked the INT 1Ah chain
1868 movzx eax,word [4*0x1a]
1869 movzx ecx,word [4*0x1a+2]
1870 shl ecx,4
1871 add eax,ecx
1872 shr eax,10
1873 cmp ax,dx ; Not in range
1874 jae .ok
1875 cmp ax,[BIOS_fbm]
1876 jae .cant_free
1877 ; inc bx
1879 .ok:
1880 mov [BIOS_fbm],dx
1881 .pop_ret:
1882 pop es
1883 pop ds
1886 .cant_free:
1887 mov si,cant_free_msg
1888 call writestr
1889 push ax
1890 xchg bx,ax
1891 call writehex4
1892 mov al,'-'
1893 call writechr
1894 pop ax
1895 call writehex4
1896 mov al,'-'
1897 call writechr
1898 mov eax,[4*0x1a]
1899 call writehex8
1900 call crlf
1901 jmp .pop_ret
1903 ; We want to keep PXE around, but still we should reset
1904 ; it to the standard bootup configuration
1905 reset_pxe:
1906 push es
1907 push cs
1908 pop es
1909 mov bx,PXENV_UDP_CLOSE
1910 mov di,pxe_udp_close_pkt
1911 call pxenv
1912 pop es
1916 ; gendotquad
1918 ; Take an IP address (in network byte order) in EAX and
1919 ; output a dotted quad string to ES:DI.
1920 ; DI points to terminal null at end of string on exit.
1922 gendotquad:
1923 push eax
1924 push cx
1925 mov cx,4
1926 .genchar:
1927 push eax
1928 cmp al,10 ; < 10?
1929 jb .lt10 ; If so, skip first 2 digits
1931 cmp al,100 ; < 100
1932 jb .lt100 ; If so, skip first digit
1934 aam 100
1935 ; Now AH = 100-digit; AL = remainder
1936 add ah,'0'
1937 mov [es:di],ah
1938 inc di
1940 .lt100:
1941 aam 10
1942 ; Now AH = 10-digit; AL = remainder
1943 add ah,'0'
1944 mov [es:di],ah
1945 inc di
1947 .lt10:
1948 add al,'0'
1949 stosb
1950 mov al,'.'
1951 stosb
1952 pop eax
1953 ror eax,8 ; Move next char into LSB
1954 loop .genchar
1955 dec di
1956 mov [es:di], byte 0
1957 pop cx
1958 pop eax
1961 ; uchexbytes/lchexbytes
1963 ; Take a number of bytes in memory and convert to upper/lower-case
1964 ; hexadecimal
1966 ; Input:
1967 ; DS:SI = input bytes
1968 ; ES:DI = output buffer
1969 ; CX = number of bytes
1970 ; Output:
1971 ; DS:SI = first byte after
1972 ; ES:DI = first byte after
1973 ; CX = 0
1975 ; Trashes AX, DX
1978 lchexbytes:
1979 mov dl,'a'-'9'-1
1980 jmp xchexbytes
1981 uchexbytes:
1982 mov dl,'A'-'9'-1
1983 xchexbytes:
1984 .loop:
1985 lodsb
1986 mov ah,al
1987 shr al,4
1988 call .outchar
1989 mov al,ah
1990 call .outchar
1991 loop .loop
1993 .outchar:
1994 and al,0Fh
1995 add al,'0'
1996 cmp al,'9'
1997 jna .done
1998 add al,dl
1999 .done:
2000 stosb
2004 ; pxe_get_cached_info
2006 ; Get a DHCP packet from the PXE stack into the trackbuf.
2008 ; Input:
2009 ; DL = packet type
2010 ; Output:
2011 ; CX = buffer size
2013 ; Assumes CS == DS == ES.
2015 pxe_get_cached_info:
2016 pushad
2017 mov di,pxe_bootp_query_pkt
2018 push di
2019 xor ax,ax
2020 stosw ; Status
2021 movzx ax,dl
2022 stosw ; Packet type
2023 mov ax,trackbufsize
2024 stosw ; Buffer size
2025 mov ax,trackbuf
2026 stosw ; Buffer offset
2027 xor ax,ax
2028 stosw ; Buffer segment
2030 pop di ; DI -> parameter set
2031 mov bx,PXENV_GET_CACHED_INFO
2032 call pxenv
2033 jc .err
2034 and ax,ax
2035 jnz .err
2037 popad
2038 mov cx,[pxe_bootp_query_pkt.buffersize]
2041 .err:
2042 mov si,err_pxefailed
2043 jmp kaboom
2046 ; ip_ok
2048 ; Tests an IP address in EAX for validity; return with ZF=1 for bad.
2049 ; We used to refuse class E, but class E addresses are likely to become
2050 ; assignable unicast addresses in the near future.
2052 ip_ok:
2053 push ax
2054 cmp eax,-1 ; Refuse the all-ones address
2055 jz .out
2056 and al,al ; Refuse network zero
2057 jz .out
2058 cmp al,127 ; Refuse loopback
2059 jz .out
2060 and al,0F0h
2061 cmp al,224 ; Refuse class D
2062 .out:
2063 pop ax
2067 ; parse_dhcp
2069 ; Parse a DHCP packet. This includes dealing with "overloaded"
2070 ; option fields (see RFC 2132, section 9.3)
2072 ; This should fill in the following global variables, if the
2073 ; information is present:
2075 ; MyIP - client IP address
2076 ; ServerIP - boot server IP address
2077 ; Netmask - network mask
2078 ; Gateway - default gateway router IP
2079 ; BootFile - boot file name
2080 ; DNSServers - DNS server IPs
2081 ; LocalDomain - Local domain name
2082 ; MACLen, MAC - Client identifier, if MACLen == 0
2084 ; This assumes the DHCP packet is in "trackbuf" and the length
2085 ; of the packet in in CX on entry.
2088 parse_dhcp:
2089 mov byte [OverLoad],0 ; Assume no overload
2090 mov eax, [trackbuf+bootp.yip]
2091 call ip_ok
2092 jz .noyip
2093 mov [MyIP], eax
2094 .noyip:
2095 mov eax, [trackbuf+bootp.sip]
2096 and eax, eax
2097 call ip_ok
2098 jz .nosip
2099 mov [ServerIP], eax
2100 .nosip:
2101 sub cx, bootp.options
2102 jbe .nooptions
2103 mov si, trackbuf+bootp.option_magic
2104 lodsd
2105 cmp eax, BOOTP_OPTION_MAGIC
2106 jne .nooptions
2107 call parse_dhcp_options
2108 .nooptions:
2109 mov si, trackbuf+bootp.bootfile
2110 test byte [OverLoad],1
2111 jz .nofileoverload
2112 mov cx,128
2113 call parse_dhcp_options
2114 jmp short .parsed_file
2115 .nofileoverload:
2116 cmp byte [si], 0
2117 jz .parsed_file ; No bootfile name
2118 mov di,BootFile
2119 mov cx,32
2120 rep movsd
2121 xor al,al
2122 stosb ; Null-terminate
2123 .parsed_file:
2124 mov si, trackbuf+bootp.sname
2125 test byte [OverLoad],2
2126 jz .nosnameoverload
2127 mov cx,64
2128 call parse_dhcp_options
2129 .nosnameoverload:
2133 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2134 ; size is CX -- some DHCP servers leave option fields unterminated
2135 ; in violation of the spec.
2137 ; For parse_some_dhcp_options, DH contains the minimum value for
2138 ; the option to recognize -- this is used to restrict parsing to
2139 ; PXELINUX-specific options only.
2141 parse_dhcp_options:
2142 xor dx,dx
2144 parse_some_dhcp_options:
2145 .loop:
2146 and cx,cx
2147 jz .done
2149 lodsb
2150 dec cx
2151 jz .done ; Last byte; must be PAD, END or malformed
2152 cmp al, 0 ; PAD option
2153 je .loop
2154 cmp al,255 ; END option
2155 je .done
2157 ; Anything else will have a length field
2158 mov dl,al ; DL <- option number
2159 xor ax,ax
2160 lodsb ; AX <- option length
2161 dec cx
2162 sub cx,ax ; Decrement bytes left counter
2163 jb .done ; Malformed option: length > field size
2165 cmp dl,dh ; Is the option value valid?
2166 jb .opt_done
2168 mov bx,dhcp_option_list
2169 .find_option:
2170 cmp bx,dhcp_option_list_end
2171 jae .opt_done
2172 cmp dl,[bx]
2173 je .found_option
2174 add bx,3
2175 jmp .find_option
2176 .found_option:
2177 pushad
2178 call [bx+1]
2179 popad
2181 ; Fall through
2182 ; Unknown option. Skip to the next one.
2183 .opt_done:
2184 add si,ax
2185 jmp .loop
2186 .done:
2189 section .data
2190 dhcp_option_list:
2191 section .text
2193 %macro dopt 2
2194 section .data
2195 db %1
2196 dw dopt_%2
2197 section .text
2198 dopt_%2:
2199 %endmacro
2202 ; Parse individual DHCP options. SI points to the option data and
2203 ; AX to the option length. DL contains the option number.
2204 ; All registers are saved around the routine.
2206 dopt 1, subnet_mask
2207 mov ebx,[si]
2208 mov [Netmask],ebx
2211 dopt 3, router
2212 mov ebx,[si]
2213 mov [Gateway],ebx
2216 dopt 6, dns_servers
2217 mov cx,ax
2218 shr cx,2
2219 cmp cl,DNS_MAX_SERVERS
2220 jna .oklen
2221 mov cl,DNS_MAX_SERVERS
2222 .oklen:
2223 mov di,DNSServers
2224 rep movsd
2225 mov [LastDNSServer],di
2228 dopt 16, local_domain
2229 mov bx,si
2230 add bx,ax
2231 xor ax,ax
2232 xchg [bx],al ; Zero-terminate option
2233 mov di,LocalDomain
2234 call dns_mangle ; Convert to DNS label set
2235 mov [bx],al ; Restore ending byte
2238 dopt 43, vendor_encaps
2239 mov dh,208 ; Only recognize PXELINUX options
2240 mov cx,ax ; Length of option = max bytes to parse
2241 call parse_some_dhcp_options ; Parse recursive structure
2244 dopt 52, option_overload
2245 mov bl,[si]
2246 mov [OverLoad],bl
2249 dopt 54, server
2250 mov eax,[si]
2251 cmp dword [ServerIP],0
2252 jne .skip ; Already have a next server IP
2253 call ip_ok
2254 jz .skip
2255 mov [ServerIP],eax
2256 .skip: ret
2258 dopt 61, client_identifier
2259 cmp ax,MAC_MAX ; Too long?
2260 ja .skip
2261 cmp ax,2 ; Too short?
2262 jb .skip
2263 cmp [MACLen],ah ; Only do this if MACLen == 0
2264 jne .skip
2265 push ax
2266 lodsb ; Client identifier type
2267 cmp al,[MACType]
2268 pop ax
2269 jne .skip ; Client identifier is not a MAC
2270 dec ax
2271 mov [MACLen],al
2272 mov di,MAC
2273 jmp dhcp_copyoption
2274 .skip: ret
2276 dopt 67, bootfile_name
2277 mov di,BootFile
2278 jmp dhcp_copyoption
2280 dopt 97, uuid_client_identifier
2281 cmp ax,17 ; type byte + 16 bytes UUID
2282 jne .skip
2283 mov dl,[si] ; Must have type 0 == UUID
2284 or dl,[HaveUUID] ; Capture only the first instance
2285 jnz .skip
2286 mov byte [HaveUUID],1 ; Got UUID
2287 mov di,UUIDType
2288 jmp dhcp_copyoption
2289 .skip: ret
2291 dopt 209, pxelinux_configfile
2292 mov di,ConfigName
2293 or byte [DHCPMagic],2 ; Got config file
2294 jmp dhcp_copyoption
2296 dopt 210, pxelinux_pathprefix
2297 mov di,PathPrefix
2298 or byte [DHCPMagic],4 ; Got path prefix
2299 jmp dhcp_copyoption
2301 dopt 211, pxelinux_reboottime
2302 cmp al,4
2303 jne .done
2304 mov ebx,[si]
2305 xchg bl,bh ; Convert to host byte order
2306 rol ebx,16
2307 xchg bl,bh
2308 mov [RebootTime],ebx
2309 or byte [DHCPMagic],8 ; Got RebootTime
2310 .done: ret
2312 ; Common code for copying an option verbatim
2313 ; Copies the option into ES:DI and null-terminates it.
2314 ; Returns with AX=0 and SI past the option.
2315 dhcp_copyoption:
2316 xchg cx,ax ; CX <- option length
2317 rep movsb
2318 xchg cx,ax ; AX <- 0
2319 stosb ; Null-terminate
2322 section .data
2323 dhcp_option_list_end:
2324 section .text
2326 section .data
2327 HaveUUID db 0
2328 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2329 section .text
2332 ; genipopt
2334 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2335 ; option into IPOption based on a DHCP packet in trackbuf.
2336 ; Assumes CS == DS == ES.
2338 genipopt:
2339 pushad
2340 mov di,IPOption
2341 mov eax,'ip='
2342 stosd
2343 dec di
2344 mov eax,[MyIP]
2345 call gendotquad
2346 mov al,':'
2347 stosb
2348 mov eax,[ServerIP]
2349 call gendotquad
2350 mov al,':'
2351 stosb
2352 mov eax,[Gateway]
2353 call gendotquad
2354 mov al,':'
2355 stosb
2356 mov eax,[Netmask]
2357 call gendotquad ; Zero-terminates its output
2358 sub di,IPOption
2359 mov [IPOptionLen],di
2360 popad
2364 ; Call the receive loop while idle. This is done mostly so we can respond to
2365 ; ARP messages, but perhaps in the future this can be used to do network
2366 ; console.
2368 ; hpa sez: people using automatic control on the serial port get very
2369 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2370 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2371 ; passed since the last poll, and reset this when a character is
2372 ; received (RESET_IDLE).
2374 %if HAVE_IDLE
2376 reset_idle:
2377 push ax
2378 mov ax,[cs:BIOS_timer]
2379 mov [cs:IdleTimer],ax
2380 pop ax
2383 check_for_arp:
2384 push ax
2385 mov ax,[cs:BIOS_timer]
2386 sub ax,[cs:IdleTimer]
2387 cmp ax,4
2388 pop ax
2389 jae .need_poll
2391 .need_poll: pushad
2392 push ds
2393 push es
2394 mov ax,cs
2395 mov ds,ax
2396 mov es,ax
2397 mov di,packet_buf
2398 mov [pxe_udp_read_pkt.status],al ; 0
2399 mov [pxe_udp_read_pkt.buffer],di
2400 mov [pxe_udp_read_pkt.buffer+2],ds
2401 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2402 mov eax,[MyIP]
2403 mov [pxe_udp_read_pkt.dip],eax
2404 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2405 mov di,pxe_udp_read_pkt
2406 mov bx,PXENV_UDP_READ
2407 call pxenv
2408 ; Ignore result...
2409 pop es
2410 pop ds
2411 popad
2412 RESET_IDLE
2415 %endif ; HAVE_IDLE
2417 ; -----------------------------------------------------------------------------
2418 ; Common modules
2419 ; -----------------------------------------------------------------------------
2421 %include "getc.inc" ; getc et al
2422 %include "conio.inc" ; Console I/O
2423 %include "writestr.inc" ; String output
2424 writestr equ cwritestr
2425 %include "writehex.inc" ; Hexadecimal output
2426 %include "configinit.inc" ; Initialize configuration
2427 %include "parseconfig.inc" ; High-level config file handling
2428 %include "parsecmd.inc" ; Low-level config file handling
2429 %include "bcopy32.inc" ; 32-bit bcopy
2430 %include "loadhigh.inc" ; Load a file into high memory
2431 %include "font.inc" ; VGA font stuff
2432 %include "graphics.inc" ; VGA graphics
2433 %include "highmem.inc" ; High memory sizing
2434 %include "strcpy.inc" ; strcpy()
2435 %include "rawcon.inc" ; Console I/O w/o using the console functions
2436 %include "dnsresolv.inc" ; DNS resolver
2437 %include "adv.inc" ; Auxillary Data Vector
2439 ; -----------------------------------------------------------------------------
2440 ; Begin data section
2441 ; -----------------------------------------------------------------------------
2443 section .data
2445 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2446 db CR, LF, 0
2447 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2448 bailmsg equ err_bootfailed
2449 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2450 err_pxefailed db 'PXE API call failed, error ', 0
2451 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2452 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2453 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2454 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2455 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2456 apiver_str db 'PXE API version is ',0
2457 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2458 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2459 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2460 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2461 undi_data_msg db 'UNDI data segment at: ',0
2462 undi_data_len_msg db 'UNDI data segment size: ',0
2463 undi_code_msg db 'UNDI code segment at: ',0
2464 undi_code_len_msg db 'UNDI code segment size: ',0
2465 cant_free_msg db 'Failed to free base memory, error ', 0
2466 notfound_msg db 'not found', CR, LF, 0
2467 myipaddr_msg db 'My IP address seems to be ',0
2468 tftpprefix_msg db 'TFTP prefix: ', 0
2469 localboot_msg db 'Booting from local disk...', CR, LF, 0
2470 trying_msg db 'Trying to load: ', 0
2471 fourbs_msg db BS, BS, BS, BS, 0
2472 default_str db 'default', 0
2473 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2474 cfgprefix db 'pxelinux.cfg/' ; No final null!
2475 cfgprefix_len equ ($-cfgprefix)
2478 ; Command line options we'd like to take a look at
2480 ; mem= and vga= are handled as normal 32-bit integer values
2481 initrd_cmd db 'initrd='
2482 initrd_cmd_len equ $-initrd_cmd
2484 ; This one we make ourselves
2485 bootif_str db 'BOOTIF='
2486 bootif_str_len equ $-bootif_str
2488 ; Config file keyword table
2490 %include "keywords.inc"
2493 ; Extensions to search for (in *forward* order).
2494 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2496 align 4, db 0
2497 exten_table: db '.cbt' ; COMBOOT (specific)
2498 db '.0', 0, 0 ; PXE bootstrap program
2499 db '.com' ; COMBOOT (same as DOS)
2500 db '.c32' ; COM32
2501 exten_table_end:
2502 dd 0, 0 ; Need 8 null bytes here
2505 ; PXE unload sequences
2507 new_api_unload:
2508 db PXENV_UDP_CLOSE
2509 db PXENV_UNDI_SHUTDOWN
2510 db PXENV_UNLOAD_STACK
2511 db PXENV_STOP_UNDI
2512 db 0
2513 old_api_unload:
2514 db PXENV_UDP_CLOSE
2515 db PXENV_UNDI_SHUTDOWN
2516 db PXENV_UNLOAD_STACK
2517 db PXENV_UNDI_CLEANUP
2518 db 0
2521 ; PXE query packets partially filled in
2523 section .bss
2524 pxe_bootp_query_pkt:
2525 .status: resw 1 ; Status
2526 .packettype: resw 1 ; Boot server packet type
2527 .buffersize: resw 1 ; Packet size
2528 .buffer: resw 2 ; seg:off of buffer
2529 .bufferlimit: resw 1 ; Unused
2531 section .data
2532 pxe_udp_open_pkt:
2533 .status: dw 0 ; Status
2534 .sip: dd 0 ; Source (our) IP
2536 pxe_udp_close_pkt:
2537 .status: dw 0 ; Status
2539 pxe_udp_write_pkt:
2540 .status: dw 0 ; Status
2541 .sip: dd 0 ; Server IP
2542 .gip: dd 0 ; Gateway IP
2543 .lport: dw 0 ; Local port
2544 .rport: dw 0 ; Remote port
2545 .buffersize: dw 0 ; Size of packet
2546 .buffer: dw 0, 0 ; seg:off of buffer
2548 pxe_udp_read_pkt:
2549 .status: dw 0 ; Status
2550 .sip: dd 0 ; Source IP
2551 .dip: dd 0 ; Destination (our) IP
2552 .rport: dw 0 ; Remote port
2553 .lport: dw 0 ; Local port
2554 .buffersize: dw 0 ; Max packet size
2555 .buffer: dw 0, 0 ; seg:off of buffer
2558 ; Misc initialized (data) variables
2560 alignb 4, db 0
2561 BaseStack dd StackBuf ; ESP of base stack
2562 dw 0 ; SS of base stack
2563 NextSocket dw 49152 ; Counter for allocating socket numbers
2564 KeepPXE db 0 ; Should PXE be kept around?
2567 ; TFTP commands
2569 tftp_tail db 'octet', 0 ; Octet mode
2570 tsize_str db 'tsize' ,0 ; Request size
2571 tsize_len equ ($-tsize_str)
2572 db '0', 0
2573 blksize_str db 'blksize', 0 ; Request large blocks
2574 blksize_len equ ($-blksize_str)
2575 asciidec TFTP_LARGEBLK
2576 db 0
2577 tftp_tail_len equ ($-tftp_tail)
2579 alignb 2, db 0
2581 ; Options negotiation parsing table (string pointer, string len, offset
2582 ; into socket structure)
2584 tftp_opt_table:
2585 dw tsize_str, tsize_len, tftp_filesize
2586 dw blksize_str, blksize_len, tftp_blksize
2587 tftp_opts equ ($-tftp_opt_table)/6
2590 ; Error packet to return on options negotiation error
2592 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2593 dw TFTP_EOPTNEG ; ERROR 8: bad options
2594 db 'tsize option required', 0 ; Error message
2595 tftp_opt_err_len equ ($-tftp_opt_err)
2597 alignb 4, db 0
2598 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2601 ; IP information (initialized to "unknown" values)
2602 MyIP dd 0 ; My IP address
2603 ServerIP dd 0 ; IP address of boot server
2604 Netmask dd 0 ; Netmask of this subnet
2605 Gateway dd 0 ; Default router
2606 ServerPort dw TFTP_PORT ; TFTP server port
2609 ; Variables that are uninitialized in SYSLINUX but initialized here
2611 alignb 4, db 0
2612 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2613 BufSafeBytes dw trackbufsize ; = how many bytes?
2614 %ifndef DEPEND
2615 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2616 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2617 %endif
2618 %endif