pxelinux: fix inadvertent breakage of TFTP
[syslinux.git] / pxelinux.asm
blob94069b48bbf2a5054b490b7d0d50743e80592128
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 local_boot:
849 push cs
850 pop ds
851 mov [LocalBootType],ax
852 call vgaclearmode
853 mov si,localboot_msg
854 call writestr
855 ; Restore the environment we were called with
856 lss sp,[InitStack]
857 pop gs
858 pop fs
859 pop es
860 pop ds
861 popad
862 mov ax,[cs:LocalBootType]
863 popfd
864 retf ; Return to PXE
867 ; kaboom: write a message and bail out. Wait for quite a while,
868 ; or a user keypress, then do a hard reboot.
870 kaboom:
871 RESET_STACK_AND_SEGS AX
872 .patch: mov si,bailmsg
873 call writestr ; Returns with AL = 0
874 .drain: call pollchar
875 jz .drained
876 call getchar
877 jmp short .drain
878 .drained:
879 mov edi,[RebootTime]
880 mov al,[DHCPMagic]
881 and al,09h ; Magic+Timeout
882 cmp al,09h
883 je .time_set
884 mov edi,REBOOT_TIME
885 .time_set:
886 mov cx,18
887 .wait1: push cx
888 mov ecx,edi
889 .wait2: mov dx,[BIOS_timer]
890 .wait3: call pollchar
891 jnz .keypress
892 cmp dx,[BIOS_timer]
893 je .wait3
894 loop .wait2,ecx
895 mov al,'.'
896 call writechr
897 pop cx
898 loop .wait1
899 .keypress:
900 call crlf
901 mov word [BIOS_magic],0 ; Cold reboot
902 jmp 0F000h:0FFF0h ; Reset vector address
905 ; memory_scan_for_pxe_struct:
907 ; If none of the standard methods find the !PXE structure, look for it
908 ; by scanning memory.
910 ; On exit, if found:
911 ; CF = 0, ES:BX -> !PXE structure
912 ; Otherwise CF = 1, all registers saved
914 memory_scan_for_pxe_struct:
915 push ds
916 pusha
917 mov ax,cs
918 mov ds,ax
919 mov si,trymempxe_msg
920 call writestr
921 mov ax,[BIOS_fbm] ; Starting segment
922 shl ax,(10-4) ; Kilobytes -> paragraphs
923 ; mov ax,01000h ; Start to look here
924 dec ax ; To skip inc ax
925 .mismatch:
926 inc ax
927 cmp ax,0A000h ; End of memory
928 jae .not_found
929 call writehex4
930 mov si,fourbs_msg
931 call writestr
932 mov es,ax
933 mov edx,[es:0]
934 cmp edx,'!PXE'
935 jne .mismatch
936 movzx cx,byte [es:4] ; Length of structure
937 cmp cl,08h ; Minimum length
938 jb .mismatch
939 push ax
940 xor ax,ax
941 xor si,si
942 .checksum: es lodsb
943 add ah,al
944 loop .checksum
945 pop ax
946 jnz .mismatch ; Checksum must == 0
947 .found: mov bp,sp
948 xor bx,bx
949 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
950 mov ax,es
951 call writehex4
952 call crlf
953 popa
954 pop ds
957 .not_found: mov si,notfound_msg
958 call writestr
959 popa
960 pop ds
965 ; memory_scan_for_pxenv_struct:
967 ; If none of the standard methods find the PXENV+ structure, look for it
968 ; by scanning memory.
970 ; On exit, if found:
971 ; CF = 0, ES:BX -> PXENV+ structure
972 ; Otherwise CF = 1, all registers saved
974 memory_scan_for_pxenv_struct:
975 pusha
976 mov si,trymempxenv_msg
977 call writestr
978 ; mov ax,[BIOS_fbm] ; Starting segment
979 ; shl ax,(10-4) ; Kilobytes -> paragraphs
980 mov ax,01000h ; Start to look here
981 dec ax ; To skip inc ax
982 .mismatch:
983 inc ax
984 cmp ax,0A000h ; End of memory
985 jae .not_found
986 mov es,ax
987 mov edx,[es:0]
988 cmp edx,'PXEN'
989 jne .mismatch
990 mov dx,[es:4]
991 cmp dx,'V+'
992 jne .mismatch
993 movzx cx,byte [es:8] ; Length of structure
994 cmp cl,26h ; Minimum length
995 jb .mismatch
996 xor ax,ax
997 xor si,si
998 .checksum: es lodsb
999 add ah,al
1000 loop .checksum
1001 and ah,ah
1002 jnz .mismatch ; Checksum must == 0
1003 .found: mov bp,sp
1004 mov [bp+8],bx ; Save BX into stack frame
1005 mov ax,bx
1006 call writehex4
1007 call crlf
1010 .not_found: mov si,notfound_msg
1011 call writestr
1012 popad
1017 ; close_file:
1018 ; Deallocates a file structure (pointer in SI)
1019 ; Assumes CS == DS.
1021 ; XXX: We should check to see if this file is still open on the server
1022 ; side and send a courtesy ERROR packet to the server.
1024 close_file:
1025 and si,si
1026 jz .closed
1027 mov word [si],0 ; Not in use
1028 .closed: ret
1031 ; searchdir:
1033 ; Open a TFTP connection to the server
1035 ; On entry:
1036 ; DS:DI = mangled filename
1037 ; If successful:
1038 ; ZF clear
1039 ; SI = socket pointer
1040 ; DX:AX = file length in bytes
1041 ; If unsuccessful
1042 ; ZF set
1045 searchdir:
1046 push es
1047 push bx
1048 push cx
1049 mov ax,ds
1050 mov es,ax
1051 mov si,di
1052 push bp
1053 mov bp,sp
1055 call allocate_socket
1056 jz .ret
1058 mov ax,PKT_RETRY ; Retry counter
1059 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1061 .sendreq: push ax ; [bp-2] - Retry counter
1062 push si ; [bp-4] - File name
1064 mov di,packet_buf
1065 mov [pxe_udp_write_pkt.buffer],di
1067 mov ax,TFTP_RRQ ; TFTP opcode
1068 stosw
1070 lodsd ; EAX <- server override (if any)
1071 and eax,eax
1072 jnz .noprefix ; No prefix, and we have the server
1074 push si ; Add common prefix
1075 mov si,PathPrefix
1076 call strcpy
1077 dec di
1078 pop si
1080 mov eax,[ServerIP] ; Get default server
1082 .noprefix:
1083 call strcpy ; Filename
1085 mov [bx+tftp_remoteip],eax
1087 push bx ; [bp-6] - TFTP block
1088 mov bx,[bx]
1089 push bx ; [bp-8] - TID (local port no)
1091 mov [pxe_udp_write_pkt.status],byte 0
1092 mov [pxe_udp_write_pkt.sip],eax
1093 ; Now figure out the gateway
1094 xor eax,[MyIP]
1095 and eax,[Netmask]
1096 jz .nogwneeded
1097 mov eax,[Gateway]
1098 .nogwneeded:
1099 mov [pxe_udp_write_pkt.gip],eax
1100 mov [pxe_udp_write_pkt.lport],bx
1101 mov ax,[ServerPort]
1102 mov [pxe_udp_write_pkt.rport],ax
1103 mov si,tftp_tail
1104 mov cx,tftp_tail_len
1105 rep movsb
1106 sub di,packet_buf ; Get packet size
1107 mov [pxe_udp_write_pkt.buffersize],di
1109 mov di,pxe_udp_write_pkt
1110 mov bx,PXENV_UDP_WRITE
1111 call pxenv
1112 jc .failure
1113 cmp word [pxe_udp_write_pkt.status],byte 0
1114 jne .failure
1117 ; Danger, Will Robinson! We need to support timeout
1118 ; and retry lest we just lost a packet...
1121 ; Packet transmitted OK, now we need to receive
1122 .getpacket: push word [PktTimeout] ; [bp-10]
1123 push word [BIOS_timer] ; [bp-12]
1125 .pkt_loop: mov bx,[bp-8] ; TID
1126 mov di,packet_buf
1127 mov word [pxe_udp_read_pkt.status],0
1128 mov [pxe_udp_read_pkt.buffer],di
1129 mov [pxe_udp_read_pkt.buffer+2],ds
1130 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1131 mov eax,[MyIP]
1132 mov [pxe_udp_read_pkt.dip],eax
1133 mov [pxe_udp_read_pkt.lport],bx
1134 mov di,pxe_udp_read_pkt
1135 mov bx,PXENV_UDP_READ
1136 call pxenv
1137 and ax,ax
1138 jz .got_packet ; Wait for packet
1139 .no_packet:
1140 mov dx,[BIOS_timer]
1141 cmp dx,[bp-12]
1142 je .pkt_loop
1143 mov [bp-12],dx
1144 dec word [bp-10] ; Timeout
1145 jnz .pkt_loop
1146 pop ax ; Adjust stack
1147 pop ax
1148 shl word [PktTimeout],1 ; Exponential backoff
1149 jmp .failure
1151 .got_packet:
1152 mov si,[bp-6] ; TFTP pointer
1153 mov bx,[bp-8] ; TID
1155 ; Make sure the packet actually came from the server
1156 ; This is technically not to the TFTP spec?
1157 mov eax,[si+tftp_remoteip]
1158 cmp [pxe_udp_read_pkt.sip],eax
1159 jne .no_packet
1161 ; Got packet - reset timeout
1162 mov word [PktTimeout],PKT_TIMEOUT
1164 pop ax ; Adjust stack
1165 pop ax
1167 mov ax,[pxe_udp_read_pkt.rport]
1168 mov [si+tftp_remoteport],ax
1170 ; filesize <- -1 == unknown
1171 mov dword [si+tftp_filesize], -1
1172 ; Default blksize unless blksize option negotiated
1173 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1175 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1176 sub cx,2 ; CX <- bytes after opcode
1177 jb .failure ; Garbled reply
1179 mov si,packet_buf
1180 lodsw
1182 cmp ax, TFTP_ERROR
1183 je .bailnow ; ERROR reply: don't try again
1185 ; If the server doesn't support any options, we'll get
1186 ; a DATA reply instead of OACK. Stash the data in
1187 ; the file buffer and go with the default value for
1188 ; all options...
1189 cmp ax, TFTP_DATA
1190 je .no_oack
1192 cmp ax, TFTP_OACK
1193 jne .err_reply ; Unknown packet type
1195 ; Now we need to parse the OACK packet to get the transfer
1196 ; and packet sizes.
1197 ; SI -> first byte of options; [E]CX -> byte count
1198 .parse_oack:
1199 jcxz .done_pkt ; No options acked
1200 .get_opt_name:
1201 mov di,si
1202 mov bx,si
1203 .opt_name_loop: lodsb
1204 and al,al
1205 jz .got_opt_name
1206 or al,20h ; Convert to lowercase
1207 stosb
1208 loop .opt_name_loop
1209 ; We ran out, and no final null
1210 jmp .err_reply
1211 .got_opt_name: ; si -> option value
1212 dec cx ; bytes left in pkt
1213 jz .err_reply ; Option w/o value
1215 ; Parse option pointed to by bx; guaranteed to be
1216 ; null-terminated.
1217 push cx
1218 push si
1219 mov si,bx ; -> option name
1220 mov bx,tftp_opt_table
1221 mov cx,tftp_opts
1222 .opt_loop:
1223 push cx
1224 push si
1225 mov di,[bx] ; Option pointer
1226 mov cx,[bx+2] ; Option len
1227 repe cmpsb
1228 pop si
1229 pop cx
1230 je .get_value ; OK, known option
1231 add bx,6
1232 loop .opt_loop
1234 pop si
1235 pop cx
1236 jmp .err_reply ; Non-negotiated option returned
1238 .get_value: pop si ; si -> option value
1239 pop cx ; cx -> bytes left in pkt
1240 mov bx,[bx+4] ; Pointer to data target
1241 add bx,[bp-6] ; TFTP socket pointer
1242 xor eax,eax
1243 xor edx,edx
1244 .value_loop: lodsb
1245 and al,al
1246 jz .got_value
1247 sub al,'0'
1248 cmp al, 9
1249 ja .err_reply ; Not a decimal digit
1250 imul edx,10
1251 add edx,eax
1252 mov [bx],edx
1253 loop .value_loop
1254 ; Ran out before final null, accept anyway
1255 jmp short .done_pkt
1257 .got_value:
1258 dec cx
1259 jnz .get_opt_name ; Not end of packet
1261 ; ZF == 1
1263 ; Success, done!
1264 .done_pkt:
1265 pop si ; Junk
1266 pop si ; We want the packet ptr in SI
1268 mov eax,[si+tftp_filesize]
1269 mov edx,eax
1270 shr edx,16 ; DX:AX == EAX
1272 and eax,eax ; Set ZF depending on file size
1273 pop bp ; Junk
1274 pop bp ; Junk (retry counter)
1275 jz .error_si ; ZF = 1 need to free the socket
1276 .ret:
1277 pop bp
1278 pop cx
1279 pop bx
1280 pop es
1284 .no_oack: ; We got a DATA packet, meaning no options are
1285 ; suported. Save the data away and consider the length
1286 ; undefined, *unless* this is the only data packet...
1287 mov bx,[bp-6] ; File pointer
1288 sub cx,2 ; Too short?
1289 jb .failure
1290 lodsw ; Block number
1291 cmp ax,1
1292 jne .failure
1293 mov [bx+tftp_lastpkt],ax
1294 cmp cx,TFTP_BLOCKSIZE
1295 ja .err_reply ; Corrupt...
1296 je .unknown_size
1297 mov [bx+tftp_filesize],ecx
1298 .unknown_size: mov [bx+tftp_bytesleft],cx
1299 mov ax,pktbuf_seg
1300 push es
1301 mov es,ax
1302 mov di,tftp_pktbuf
1303 mov [bx+tftp_dataptr],di
1304 add cx,3
1305 shr cx,2
1306 rep movsd
1307 pop es
1308 jmp .done_pkt
1310 .err_reply: ; Option negotiation error. Send ERROR reply.
1311 ; ServerIP and gateway are already programmed in
1312 mov si,[bp-6]
1313 mov ax,[si+tftp_remoteport]
1314 mov word [pxe_udp_write_pkt.rport],ax
1315 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1316 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1317 mov di,pxe_udp_write_pkt
1318 mov bx,PXENV_UDP_WRITE
1319 call pxenv
1321 ; Write an error message and explode
1322 mov si,err_oldtftp
1323 call writestr
1324 jmp kaboom
1326 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1328 .failure: pop bx ; Junk
1329 pop bx
1330 pop si
1331 pop ax
1332 dec ax ; Retry counter
1333 jnz .sendreq ; Try again
1335 .error: mov si,bx ; Socket pointer
1336 .error_si: ; Socket pointer already in SI
1337 call free_socket ; ZF <- 1, SI <- 0
1338 jmp .ret
1341 ; allocate_socket: Allocate a local UDP port structure
1343 ; If successful:
1344 ; ZF set
1345 ; BX = socket pointer
1346 ; If unsuccessful:
1347 ; ZF clear
1349 allocate_socket:
1350 push cx
1351 mov bx,Files
1352 mov cx,MAX_OPEN
1353 .check: cmp word [bx], byte 0
1354 je .found
1355 add bx,open_file_t_size
1356 loop .check
1357 xor cx,cx ; ZF = 1
1358 pop cx
1360 ; Allocate a socket number. Socket numbers are made
1361 ; guaranteed unique by including the socket slot number
1362 ; (inverted, because we use the loop counter cx); add a
1363 ; counter value to keep the numbers from being likely to
1364 ; get immediately reused.
1366 ; The NextSocket variable also contains the top two bits
1367 ; set. This generates a value in the range 49152 to
1368 ; 57343.
1369 .found:
1370 dec cx
1371 push ax
1372 mov ax,[NextSocket]
1373 inc ax
1374 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1375 mov [NextSocket],ax
1376 shl cx,13-MAX_OPEN_LG2
1377 add cx,ax ; ZF = 0
1378 xchg ch,cl ; Convert to network byte order
1379 mov [bx],cx ; Socket in use
1380 pop ax
1381 pop cx
1385 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1387 free_socket:
1388 push es
1389 pusha
1390 xor ax,ax
1391 mov es,ax
1392 mov di,si
1393 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1394 rep stosw
1395 popa
1396 pop es
1397 xor si,si
1401 ; parse_dotquad:
1402 ; Read a dot-quad pathname in DS:SI and output an IP
1403 ; address in EAX, with SI pointing to the first
1404 ; nonmatching character.
1406 ; Return CF=1 on error.
1408 ; No segment assumptions permitted.
1410 parse_dotquad:
1411 push cx
1412 mov cx,4
1413 xor eax,eax
1414 .parseloop:
1415 mov ch,ah
1416 mov ah,al
1417 lodsb
1418 sub al,'0'
1419 jb .notnumeric
1420 cmp al,9
1421 ja .notnumeric
1422 aad ; AL += 10 * AH; AH = 0;
1423 xchg ah,ch
1424 jmp .parseloop
1425 .notnumeric:
1426 cmp al,'.'-'0'
1427 pushf
1428 mov al,ah
1429 mov ah,ch
1430 xor ch,ch
1431 ror eax,8
1432 popf
1433 jne .error
1434 loop .parseloop
1435 jmp .done
1436 .error:
1437 loop .realerror ; If CX := 1 then we're done
1439 jmp .done
1440 .realerror:
1442 .done:
1443 dec si ; CF unchanged!
1444 pop cx
1447 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1448 ; to by ES:DI; ends on encountering any whitespace.
1449 ; DI is preserved.
1451 ; This verifies that a filename is < FILENAME_MAX characters
1452 ; and doesn't contain whitespace, and zero-pads the output buffer,
1453 ; so "repe cmpsb" can do a compare.
1455 ; The first four bytes of the manged name is the IP address of
1456 ; the download host.
1458 ; No segment assumptions permitted.
1460 mangle_name:
1461 push di
1462 push si
1463 mov eax,[cs:ServerIP]
1464 cmp byte [si],0
1465 je .noip ; Null filename?!?!
1466 cmp word [si],'::' ; Leading ::?
1467 je .gotprefix
1469 .more:
1470 inc si
1471 cmp byte [si],0
1472 je .noip
1473 cmp word [si],'::'
1474 jne .more
1476 ; We have a :: prefix of some sort, it could be either
1477 ; a DNS name or a dot-quad IP address. Try the dot-quad
1478 ; first...
1479 .here:
1480 pop si
1481 push si
1482 call parse_dotquad
1483 jc .notdq
1484 cmp word [si],'::'
1485 je .gotprefix
1486 .notdq:
1487 pop si
1488 push si
1489 call dns_resolv
1490 cmp word [si],'::'
1491 jne .noip
1492 and eax,eax
1493 jnz .gotprefix
1495 .noip:
1496 pop si
1497 xor eax,eax
1498 jmp .prefix_done
1500 .gotprefix:
1501 pop cx ; Adjust stack
1502 inc si ; Skip double colon
1503 inc si
1505 .prefix_done:
1506 stosd ; Save IP address prefix
1507 mov cx,FILENAME_MAX-5
1509 .mn_loop:
1510 lodsb
1511 cmp al,' ' ; If control or space, end
1512 jna .mn_end
1513 stosb
1514 loop .mn_loop
1515 .mn_end:
1516 inc cx ; At least one null byte
1517 xor ax,ax ; Zero-fill name
1518 rep stosb ; Doesn't do anything if CX=0
1519 pop di
1520 ret ; Done
1523 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1524 ; filename to the conventional representation. This is needed
1525 ; for the BOOT_IMAGE= parameter for the kernel.
1527 ; NOTE: The output buffer needs to be able to hold an
1528 ; expanded IP address.
1530 ; DS:SI -> input mangled file name
1531 ; ES:DI -> output buffer
1533 ; On return, DI points to the first byte after the output name,
1534 ; which is set to a null byte.
1536 unmangle_name:
1537 push eax
1538 lodsd
1539 and eax,eax
1540 jz .noip
1541 call gendotquad
1542 mov ax,'::'
1543 stosw
1544 .noip:
1545 call strcpy
1546 dec di ; Point to final null byte
1547 pop eax
1551 ; pxenv
1553 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1554 ; calling convention. This is a separate local routine so
1555 ; we can hook special things from it if necessary. In particular,
1556 ; some PXE stacks seem to not like being invoked from anything but
1557 ; the initial stack, so humour it.
1560 pxenv:
1561 %if USE_PXE_PROVIDED_STACK == 0
1562 mov [cs:PXEStack],sp
1563 mov [cs:PXEStack+2],ss
1564 lss sp,[cs:InitStack]
1565 %endif
1566 .jump: call 0:pxe_thunk ; Default to calling the thunk
1567 %if USE_PXE_PROVIDED_STACK == 0
1568 lss sp,[cs:PXEStack]
1569 %endif
1570 cld ; Make sure DF <- 0
1573 ; Must be after function def due to NASM bug
1574 PXENVEntry equ pxenv.jump+1
1577 ; pxe_thunk
1579 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1580 ; calling convention (using the stack.)
1582 ; This is called as a far routine so that we can just stick it into
1583 ; the PXENVEntry variable.
1585 pxe_thunk: push es
1586 push di
1587 push bx
1588 .jump: call 0:0
1589 add sp,byte 6
1590 cmp ax,byte 1
1591 cmc ; Set CF unless ax == 0
1592 retf
1594 ; Must be after function def due to NASM bug
1595 PXEEntry equ pxe_thunk.jump+1
1598 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1600 ; In this case, get multiple blocks from a specific TCP connection.
1602 ; On entry:
1603 ; ES:BX -> Buffer
1604 ; SI -> TFTP socket pointer
1605 ; CX -> 512-byte block count; 0FFFFh = until end of file
1606 ; On exit:
1607 ; SI -> TFTP socket pointer (or 0 on EOF)
1608 ; CF = 1 -> Hit EOF
1610 getfssec: push si
1611 push fs
1612 mov di,bx
1613 mov bx,si
1614 mov ax,pktbuf_seg
1615 mov fs,ax
1617 movzx ecx,cx
1618 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1619 jz .hit_eof ; Nothing to do?
1621 .need_more:
1622 push ecx
1624 movzx eax,word [bx+tftp_bytesleft]
1625 cmp ecx,eax
1626 jna .ok_size
1627 mov ecx,eax
1628 jcxz .need_packet ; No bytes available?
1629 .ok_size:
1631 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1632 mov si,[bx+tftp_dataptr]
1633 sub [bx+tftp_bytesleft],cx
1634 fs rep movsb ; Copy from packet buffer
1635 mov [bx+tftp_dataptr],si
1637 pop ecx
1638 sub ecx,eax
1639 jnz .need_more
1642 .hit_eof:
1643 pop fs
1644 pop si
1646 ; Is there anything left of this?
1647 mov eax,[si+tftp_filesize]
1648 sub eax,[si+tftp_filepos]
1649 jnz .bytes_left ; CF <- 0
1651 cmp [si+tftp_bytesleft],ax
1652 jnz .bytes_left ; CF <- 0
1654 ; The socket is closed and the buffer drained
1655 ; Close socket structure and re-init for next user
1656 call free_socket
1658 .bytes_left:
1662 ; No data in buffer, check to see if we can get a packet...
1664 .need_packet:
1665 pop ecx
1666 mov eax,[bx+tftp_filesize]
1667 cmp eax,[bx+tftp_filepos]
1668 je .hit_eof ; Already EOF'd; socket already closed
1670 pushad
1671 push es
1672 mov si,bx
1673 call get_packet
1674 pop es
1675 popad
1677 jmp .need_more
1680 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1682 get_packet:
1683 mov ax,ds
1684 mov es,ax
1686 .packet_loop:
1687 ; Start by ACKing the previous packet; this should cause the
1688 ; next packet to be sent.
1689 mov cx,PKT_RETRY
1690 mov word [PktTimeout],PKT_TIMEOUT
1692 .send_ack: push cx ; <D> Retry count
1694 mov ax,[si+tftp_lastpkt]
1695 call ack_packet ; Send ACK
1697 ; We used to test the error code here, but sometimes
1698 ; PXE would return negative status even though we really
1699 ; did send the ACK. Now, just treat a failed send as
1700 ; a normally lost packet, and let it time out in due
1701 ; course of events.
1703 .send_ok: ; Now wait for packet.
1704 mov dx,[BIOS_timer] ; Get current time
1706 mov cx,[PktTimeout]
1707 .wait_data: push cx ; <E> Timeout
1708 push dx ; <F> Old time
1710 mov bx,[si+tftp_pktbuf]
1711 mov [pxe_udp_read_pkt.buffer],bx
1712 mov [pxe_udp_read_pkt.buffer+2],fs
1713 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1714 mov eax,[si+tftp_remoteip]
1715 mov [pxe_udp_read_pkt.sip],eax
1716 mov eax,[MyIP]
1717 mov [pxe_udp_read_pkt.dip],eax
1718 mov ax,[si+tftp_remoteport]
1719 mov [pxe_udp_read_pkt.rport],ax
1720 mov ax,[si+tftp_localport]
1721 mov [pxe_udp_read_pkt.lport],ax
1722 mov di,pxe_udp_read_pkt
1723 mov bx,PXENV_UDP_READ
1724 push si ; <G>
1725 call pxenv
1726 pop si ; <G>
1727 and ax,ax
1728 jz .recv_ok
1730 ; No packet, or receive failure
1731 mov dx,[BIOS_timer]
1732 pop ax ; <F> Old time
1733 pop cx ; <E> Timeout
1734 cmp ax,dx ; Same time -> don't advance timeout
1735 je .wait_data ; Same clock tick
1736 loop .wait_data ; Decrease timeout
1738 pop cx ; <D> Didn't get any, send another ACK
1739 shl word [PktTimeout],1 ; Exponential backoff
1740 loop .send_ack
1741 jmp kaboom ; Forget it...
1743 .recv_ok: pop dx ; <F>
1744 pop cx ; <E>
1746 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1747 jb .wait_data ; Bad size for a DATA packet
1749 mov bx,[si+tftp_pktbuf]
1750 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1751 jne .wait_data ; Then wait for something else
1753 mov ax,[si+tftp_lastpkt]
1754 xchg ah,al ; Host byte order
1755 inc ax ; Which packet are we waiting for?
1756 xchg ah,al ; Network byte order
1757 cmp [fs:bx+2],ax
1758 je .right_packet
1760 ; Wrong packet, ACK the packet and then try again
1761 ; This is presumably because the ACK got lost,
1762 ; so the server just resent the previous packet
1763 mov ax,[fs:bx+2]
1764 call ack_packet
1765 jmp .send_ok ; Reset timeout
1767 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1769 pop cx ; <D> Don't need the retry count anymore
1771 mov [si+tftp_lastpkt],ax ; Update last packet number
1773 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1774 sub cx,byte 4 ; Skip TFTP header
1776 ; If this is a zero-length block, don't mess with the pointers,
1777 ; since we may have just set up the previous block that way
1778 jz .last_block
1780 ; Set pointer to data block
1781 lea ax,[bx+4] ; Data past TFTP header
1782 mov [si+tftp_dataptr],ax
1784 add [si+tftp_filepos],ecx
1785 mov [si+tftp_bytesleft],cx
1787 cmp cx,[si+tftp_blksize] ; Is it a full block?
1788 jb .last_block ; If so, it's not EOF
1790 ; If we had the exact right number of bytes, always get
1791 ; one more packet to get the (zero-byte) EOF packet and
1792 ; close the socket.
1793 mov eax,[si+tftp_filepos]
1794 cmp [si+tftp_filesize],eax
1795 je .packet_loop
1800 .last_block: ; Last block - ACK packet immediately
1801 mov ax,[fs:bx+2]
1802 call ack_packet
1804 ; Make sure we know we are at end of file
1805 mov eax,[si+tftp_filepos]
1806 mov [si+tftp_filesize],eax
1811 ; ack_packet:
1813 ; Send ACK packet. This is a common operation and so is worth canning.
1815 ; Entry:
1816 ; SI = TFTP block
1817 ; AX = Packet # to ack (network byte order)
1818 ; Exit:
1819 ; ZF = 0 -> Error
1820 ; All registers preserved
1822 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1824 ack_packet:
1825 pushad
1826 mov [ack_packet_buf+2],ax ; Packet number to ack
1827 mov ax,[si]
1828 mov [pxe_udp_write_pkt.lport],ax
1829 mov ax,[si+tftp_remoteport]
1830 mov [pxe_udp_write_pkt.rport],ax
1831 mov eax,[si+tftp_remoteip]
1832 mov [pxe_udp_write_pkt.sip],eax
1833 xor eax,[MyIP]
1834 and eax,[Netmask]
1835 jz .nogw
1836 mov eax,[Gateway]
1837 .nogw:
1838 mov [pxe_udp_write_pkt.gip],eax
1839 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1840 mov [pxe_udp_write_pkt.buffersize], word 4
1841 mov di,pxe_udp_write_pkt
1842 mov bx,PXENV_UDP_WRITE
1843 call pxenv
1844 cmp ax,byte 0 ; ZF = 1 if write OK
1845 popad
1849 ; unload_pxe:
1851 ; This function unloads the PXE and UNDI stacks and unclaims
1852 ; the memory.
1854 unload_pxe:
1855 test byte [KeepPXE],01h ; Should we keep PXE around?
1856 jnz reset_pxe
1858 push ds
1859 push es
1861 mov ax,cs
1862 mov ds,ax
1863 mov es,ax
1865 mov si,new_api_unload
1866 cmp byte [APIVer+1],2 ; Major API version >= 2?
1867 jae .new_api
1868 mov si,old_api_unload
1869 .new_api:
1871 .call_loop: xor ax,ax
1872 lodsb
1873 and ax,ax
1874 jz .call_done
1875 xchg bx,ax
1876 mov di,pxe_unload_stack_pkt
1877 push di
1878 xor ax,ax
1879 mov cx,pxe_unload_stack_pkt_len >> 1
1880 rep stosw
1881 pop di
1882 call pxenv
1883 jc .cant_free
1884 mov ax,word [pxe_unload_stack_pkt.status]
1885 cmp ax,PXENV_STATUS_SUCCESS
1886 jne .cant_free
1887 jmp .call_loop
1889 .call_done:
1890 mov bx,0FF00h
1892 mov dx,[RealBaseMem]
1893 cmp dx,[BIOS_fbm] ; Sanity check
1894 jna .cant_free
1895 inc bx
1897 ; Check that PXE actually unhooked the INT 1Ah chain
1898 movzx eax,word [4*0x1a]
1899 movzx ecx,word [4*0x1a+2]
1900 shl ecx,4
1901 add eax,ecx
1902 shr eax,10
1903 cmp ax,dx ; Not in range
1904 jae .ok
1905 cmp ax,[BIOS_fbm]
1906 jae .cant_free
1907 ; inc bx
1909 .ok:
1910 mov [BIOS_fbm],dx
1911 .pop_ret:
1912 pop es
1913 pop ds
1916 .cant_free:
1917 mov si,cant_free_msg
1918 call writestr
1919 push ax
1920 xchg bx,ax
1921 call writehex4
1922 mov al,'-'
1923 call writechr
1924 pop ax
1925 call writehex4
1926 mov al,'-'
1927 call writechr
1928 mov eax,[4*0x1a]
1929 call writehex8
1930 call crlf
1931 jmp .pop_ret
1933 ; We want to keep PXE around, but still we should reset
1934 ; it to the standard bootup configuration
1935 reset_pxe:
1936 push es
1937 push cs
1938 pop es
1939 mov bx,PXENV_UDP_CLOSE
1940 mov di,pxe_udp_close_pkt
1941 call pxenv
1942 pop es
1946 ; gendotquad
1948 ; Take an IP address (in network byte order) in EAX and
1949 ; output a dotted quad string to ES:DI.
1950 ; DI points to terminal null at end of string on exit.
1952 gendotquad:
1953 push eax
1954 push cx
1955 mov cx,4
1956 .genchar:
1957 push eax
1958 cmp al,10 ; < 10?
1959 jb .lt10 ; If so, skip first 2 digits
1961 cmp al,100 ; < 100
1962 jb .lt100 ; If so, skip first digit
1964 aam 100
1965 ; Now AH = 100-digit; AL = remainder
1966 add ah,'0'
1967 mov [es:di],ah
1968 inc di
1970 .lt100:
1971 aam 10
1972 ; Now AH = 10-digit; AL = remainder
1973 add ah,'0'
1974 mov [es:di],ah
1975 inc di
1977 .lt10:
1978 add al,'0'
1979 stosb
1980 mov al,'.'
1981 stosb
1982 pop eax
1983 ror eax,8 ; Move next char into LSB
1984 loop .genchar
1985 dec di
1986 mov [es:di], byte 0
1987 pop cx
1988 pop eax
1991 ; uchexbytes/lchexbytes
1993 ; Take a number of bytes in memory and convert to upper/lower-case
1994 ; hexadecimal
1996 ; Input:
1997 ; DS:SI = input bytes
1998 ; ES:DI = output buffer
1999 ; CX = number of bytes
2000 ; Output:
2001 ; DS:SI = first byte after
2002 ; ES:DI = first byte after
2003 ; CX = 0
2005 ; Trashes AX, DX
2008 lchexbytes:
2009 mov dl,'a'-'9'-1
2010 jmp xchexbytes
2011 uchexbytes:
2012 mov dl,'A'-'9'-1
2013 xchexbytes:
2014 .loop:
2015 lodsb
2016 mov ah,al
2017 shr al,4
2018 call .outchar
2019 mov al,ah
2020 call .outchar
2021 loop .loop
2023 .outchar:
2024 and al,0Fh
2025 add al,'0'
2026 cmp al,'9'
2027 jna .done
2028 add al,dl
2029 .done:
2030 stosb
2034 ; pxe_get_cached_info
2036 ; Get a DHCP packet from the PXE stack into the trackbuf.
2038 ; Input:
2039 ; DL = packet type
2040 ; Output:
2041 ; CX = buffer size
2043 ; Assumes CS == DS == ES.
2045 pxe_get_cached_info:
2046 pushad
2047 mov di,pxe_bootp_query_pkt
2048 push di
2049 xor ax,ax
2050 stosw ; Status
2051 movzx ax,dl
2052 stosw ; Packet type
2053 mov ax,trackbufsize
2054 stosw ; Buffer size
2055 mov ax,trackbuf
2056 stosw ; Buffer offset
2057 xor ax,ax
2058 stosw ; Buffer segment
2060 pop di ; DI -> parameter set
2061 mov bx,PXENV_GET_CACHED_INFO
2062 call pxenv
2063 jc .err
2064 and ax,ax
2065 jnz .err
2067 popad
2068 mov cx,[pxe_bootp_query_pkt.buffersize]
2071 .err:
2072 mov si,err_pxefailed
2073 jmp kaboom
2076 ; parse_dhcp
2078 ; Parse a DHCP packet. This includes dealing with "overloaded"
2079 ; option fields (see RFC 2132, section 9.3)
2081 ; This should fill in the following global variables, if the
2082 ; information is present:
2084 ; MyIP - client IP address
2085 ; ServerIP - boot server IP address
2086 ; Netmask - network mask
2087 ; Gateway - default gateway router IP
2088 ; BootFile - boot file name
2089 ; DNSServers - DNS server IPs
2090 ; LocalDomain - Local domain name
2091 ; MACLen, MAC - Client identifier, if MACLen == 0
2093 ; This assumes the DHCP packet is in "trackbuf" and the length
2094 ; of the packet in in CX on entry.
2097 parse_dhcp:
2098 mov byte [OverLoad],0 ; Assume no overload
2099 mov eax, [trackbuf+bootp.yip]
2100 and eax, eax
2101 jz .noyip
2102 cmp al,224 ; Class D or higher -> bad
2103 jae .noyip
2104 mov [MyIP], eax
2105 .noyip:
2106 mov eax, [trackbuf+bootp.sip]
2107 and eax, eax
2108 jz .nosip
2109 cmp al,224 ; Class D or higher -> bad
2110 jae .nosip
2111 mov [ServerIP], eax
2112 .nosip:
2113 sub cx, bootp.options
2114 jbe .nooptions
2115 mov si, trackbuf+bootp.option_magic
2116 lodsd
2117 cmp eax, BOOTP_OPTION_MAGIC
2118 jne .nooptions
2119 call parse_dhcp_options
2120 .nooptions:
2121 mov si, trackbuf+bootp.bootfile
2122 test byte [OverLoad],1
2123 jz .nofileoverload
2124 mov cx,128
2125 call parse_dhcp_options
2126 jmp short .parsed_file
2127 .nofileoverload:
2128 cmp byte [si], 0
2129 jz .parsed_file ; No bootfile name
2130 mov di,BootFile
2131 mov cx,32
2132 rep movsd
2133 xor al,al
2134 stosb ; Null-terminate
2135 .parsed_file:
2136 mov si, trackbuf+bootp.sname
2137 test byte [OverLoad],2
2138 jz .nosnameoverload
2139 mov cx,64
2140 call parse_dhcp_options
2141 .nosnameoverload:
2145 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2146 ; size is CX -- some DHCP servers leave option fields unterminated
2147 ; in violation of the spec.
2149 ; For parse_some_dhcp_options, DH contains the minimum value for
2150 ; the option to recognize -- this is used to restrict parsing to
2151 ; PXELINUX-specific options only.
2153 parse_dhcp_options:
2154 xor dx,dx
2156 parse_some_dhcp_options:
2157 .loop:
2158 and cx,cx
2159 jz .done
2161 lodsb
2162 dec cx
2163 jz .done ; Last byte; must be PAD, END or malformed
2164 cmp al, 0 ; PAD option
2165 je .loop
2166 cmp al,255 ; END option
2167 je .done
2169 ; Anything else will have a length field
2170 mov dl,al ; DL <- option number
2171 xor ax,ax
2172 lodsb ; AX <- option length
2173 dec cx
2174 sub cx,ax ; Decrement bytes left counter
2175 jb .done ; Malformed option: length > field size
2177 cmp dl,dh ; Is the option value valid?
2178 jb .opt_done
2180 mov bx,dhcp_option_list
2181 .find_option:
2182 cmp bx,dhcp_option_list_end
2183 jae .opt_done
2184 cmp dl,[bx]
2185 je .found_option
2186 add bx,3
2187 jmp .find_option
2188 .found_option:
2189 pushad
2190 call [bx+1]
2191 popad
2193 ; Fall through
2194 ; Unknown option. Skip to the next one.
2195 .opt_done:
2196 add si,ax
2197 jmp .loop
2198 .done:
2201 section .data
2202 dhcp_option_list:
2203 section .text
2205 %macro dopt 2
2206 section .data
2207 db %1
2208 dw dopt_%2
2209 section .text
2210 dopt_%2:
2211 %endmacro
2214 ; Parse individual DHCP options. SI points to the option data and
2215 ; AX to the option length. DL contains the option number.
2216 ; All registers are saved around the routine.
2218 dopt 1, subnet_mask
2219 mov ebx,[si]
2220 mov [Netmask],ebx
2223 dopt 3, router
2224 mov ebx,[si]
2225 mov [Gateway],ebx
2228 dopt 6, dns_servers
2229 mov cx,ax
2230 shr cx,2
2231 cmp cl,DNS_MAX_SERVERS
2232 jna .oklen
2233 mov cl,DNS_MAX_SERVERS
2234 .oklen:
2235 mov di,DNSServers
2236 rep movsd
2237 mov [LastDNSServer],di
2240 dopt 16, local_domain
2241 mov bx,si
2242 add bx,ax
2243 xor ax,ax
2244 xchg [bx],al ; Zero-terminate option
2245 mov di,LocalDomain
2246 call dns_mangle ; Convert to DNS label set
2247 mov [bx],al ; Restore ending byte
2250 dopt 43, vendor_encaps
2251 mov dh,208 ; Only recognize PXELINUX options
2252 mov cx,ax ; Length of option = max bytes to parse
2253 call parse_some_dhcp_options ; Parse recursive structure
2256 dopt 52, option_overload
2257 mov bl,[si]
2258 mov [OverLoad],bl
2261 dopt 54, server
2262 mov eax,[si]
2263 cmp dword [ServerIP],0
2264 jne .skip ; Already have a next server IP
2265 cmp al,224 ; Class D or higher
2266 jae .skip
2267 mov [ServerIP],eax
2268 .skip: ret
2270 dopt 61, client_identifier
2271 cmp ax,MAC_MAX ; Too long?
2272 ja .skip
2273 cmp ax,2 ; Too short?
2274 jb .skip
2275 cmp [MACLen],ah ; Only do this if MACLen == 0
2276 jne .skip
2277 push ax
2278 lodsb ; Client identifier type
2279 cmp al,[MACType]
2280 pop ax
2281 jne .skip ; Client identifier is not a MAC
2282 dec ax
2283 mov [MACLen],al
2284 mov di,MAC
2285 jmp dhcp_copyoption
2286 .skip: ret
2288 dopt 67, bootfile_name
2289 mov di,BootFile
2290 jmp dhcp_copyoption
2292 dopt 97, uuid_client_identifier
2293 cmp ax,17 ; type byte + 16 bytes UUID
2294 jne .skip
2295 mov dl,[si] ; Must have type 0 == UUID
2296 or dl,[HaveUUID] ; Capture only the first instance
2297 jnz .skip
2298 mov byte [HaveUUID],1 ; Got UUID
2299 mov di,UUIDType
2300 jmp dhcp_copyoption
2301 .skip: ret
2303 dopt 209, pxelinux_configfile
2304 mov di,ConfigName
2305 or byte [DHCPMagic],2 ; Got config file
2306 jmp dhcp_copyoption
2308 dopt 210, pxelinux_pathprefix
2309 mov di,PathPrefix
2310 or byte [DHCPMagic],4 ; Got path prefix
2311 jmp dhcp_copyoption
2313 dopt 211, pxelinux_reboottime
2314 cmp al,4
2315 jne .done
2316 mov ebx,[si]
2317 xchg bl,bh ; Convert to host byte order
2318 rol ebx,16
2319 xchg bl,bh
2320 mov [RebootTime],ebx
2321 or byte [DHCPMagic],8 ; Got RebootTime
2322 .done: ret
2324 ; Common code for copying an option verbatim
2325 ; Copies the option into ES:DI and null-terminates it.
2326 ; Returns with AX=0 and SI past the option.
2327 dhcp_copyoption:
2328 xchg cx,ax ; CX <- option length
2329 rep movsb
2330 xchg cx,ax ; AX <- 0
2331 stosb ; Null-terminate
2334 section .data
2335 dhcp_option_list_end:
2336 section .text
2338 section .data
2339 HaveUUID db 0
2340 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2341 section .text
2344 ; genipopt
2346 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2347 ; option into IPOption based on a DHCP packet in trackbuf.
2348 ; Assumes CS == DS == ES.
2350 genipopt:
2351 pushad
2352 mov di,IPOption
2353 mov eax,'ip='
2354 stosd
2355 dec di
2356 mov eax,[MyIP]
2357 call gendotquad
2358 mov al,':'
2359 stosb
2360 mov eax,[ServerIP]
2361 call gendotquad
2362 mov al,':'
2363 stosb
2364 mov eax,[Gateway]
2365 call gendotquad
2366 mov al,':'
2367 stosb
2368 mov eax,[Netmask]
2369 call gendotquad ; Zero-terminates its output
2370 sub di,IPOption
2371 mov [IPOptionLen],di
2372 popad
2376 ; Call the receive loop while idle. This is done mostly so we can respond to
2377 ; ARP messages, but perhaps in the future this can be used to do network
2378 ; console.
2380 ; hpa sez: people using automatic control on the serial port get very
2381 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2382 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2383 ; passed since the last poll, and reset this when a character is
2384 ; received (RESET_IDLE).
2386 %if HAVE_IDLE
2388 reset_idle:
2389 push ax
2390 mov ax,[cs:BIOS_timer]
2391 mov [cs:IdleTimer],ax
2392 pop ax
2395 check_for_arp:
2396 push ax
2397 mov ax,[cs:BIOS_timer]
2398 sub ax,[cs:IdleTimer]
2399 cmp ax,4
2400 pop ax
2401 jae .need_poll
2403 .need_poll: pushad
2404 push ds
2405 push es
2406 mov ax,cs
2407 mov ds,ax
2408 mov es,ax
2409 mov di,packet_buf
2410 mov [pxe_udp_read_pkt.status],al ; 0
2411 mov [pxe_udp_read_pkt.buffer],di
2412 mov [pxe_udp_read_pkt.buffer+2],ds
2413 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2414 mov eax,[MyIP]
2415 mov [pxe_udp_read_pkt.dip],eax
2416 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2417 mov di,pxe_udp_read_pkt
2418 mov bx,PXENV_UDP_READ
2419 call pxenv
2420 ; Ignore result...
2421 pop es
2422 pop ds
2423 popad
2424 RESET_IDLE
2427 %endif ; HAVE_IDLE
2429 ; -----------------------------------------------------------------------------
2430 ; Common modules
2431 ; -----------------------------------------------------------------------------
2433 %include "getc.inc" ; getc et al
2434 %include "conio.inc" ; Console I/O
2435 %include "writestr.inc" ; String output
2436 writestr equ cwritestr
2437 %include "writehex.inc" ; Hexadecimal output
2438 %include "configinit.inc" ; Initialize configuration
2439 %include "parseconfig.inc" ; High-level config file handling
2440 %include "parsecmd.inc" ; Low-level config file handling
2441 %include "bcopy32.inc" ; 32-bit bcopy
2442 %include "loadhigh.inc" ; Load a file into high memory
2443 %include "font.inc" ; VGA font stuff
2444 %include "graphics.inc" ; VGA graphics
2445 %include "highmem.inc" ; High memory sizing
2446 %include "strcpy.inc" ; strcpy()
2447 %include "rawcon.inc" ; Console I/O w/o using the console functions
2448 %include "dnsresolv.inc" ; DNS resolver
2449 %include "adv.inc" ; Auxillary Data Vector
2451 ; -----------------------------------------------------------------------------
2452 ; Begin data section
2453 ; -----------------------------------------------------------------------------
2455 section .data
2457 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2458 db CR, LF, 0
2459 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2460 bailmsg equ err_bootfailed
2461 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2462 err_pxefailed db 'PXE API call failed, error ', 0
2463 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2464 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2465 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2466 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2467 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2468 apiver_str db 'PXE API version is ',0
2469 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2470 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2471 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2472 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2473 undi_data_msg db 'UNDI data segment at: ',0
2474 undi_data_len_msg db 'UNDI data segment size: ',0
2475 undi_code_msg db 'UNDI code segment at: ',0
2476 undi_code_len_msg db 'UNDI code segment size: ',0
2477 cant_free_msg db 'Failed to free base memory, error ', 0
2478 notfound_msg db 'not found', CR, LF, 0
2479 myipaddr_msg db 'My IP address seems to be ',0
2480 tftpprefix_msg db 'TFTP prefix: ', 0
2481 localboot_msg db 'Booting from local disk...', CR, LF, 0
2482 trying_msg db 'Trying to load: ', 0
2483 fourbs_msg db BS, BS, BS, BS, 0
2484 default_str db 'default', 0
2485 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2486 cfgprefix db 'pxelinux.cfg/' ; No final null!
2487 cfgprefix_len equ ($-cfgprefix)
2490 ; Command line options we'd like to take a look at
2492 ; mem= and vga= are handled as normal 32-bit integer values
2493 initrd_cmd db 'initrd='
2494 initrd_cmd_len equ $-initrd_cmd
2496 ; This one we make ourselves
2497 bootif_str db 'BOOTIF='
2498 bootif_str_len equ $-bootif_str
2500 ; Config file keyword table
2502 %include "keywords.inc"
2505 ; Extensions to search for (in *forward* order).
2506 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2508 align 4, db 0
2509 exten_table: db '.cbt' ; COMBOOT (specific)
2510 db '.0', 0, 0 ; PXE bootstrap program
2511 db '.com' ; COMBOOT (same as DOS)
2512 db '.c32' ; COM32
2513 exten_table_end:
2514 dd 0, 0 ; Need 8 null bytes here
2517 ; PXE unload sequences
2519 new_api_unload:
2520 db PXENV_UDP_CLOSE
2521 db PXENV_UNDI_SHUTDOWN
2522 db PXENV_UNLOAD_STACK
2523 db PXENV_STOP_UNDI
2524 db 0
2525 old_api_unload:
2526 db PXENV_UDP_CLOSE
2527 db PXENV_UNDI_SHUTDOWN
2528 db PXENV_UNLOAD_STACK
2529 db PXENV_UNDI_CLEANUP
2530 db 0
2533 ; PXE query packets partially filled in
2535 section .bss
2536 pxe_bootp_query_pkt:
2537 .status: resw 1 ; Status
2538 .packettype: resw 1 ; Boot server packet type
2539 .buffersize: resw 1 ; Packet size
2540 .buffer: resw 2 ; seg:off of buffer
2541 .bufferlimit: resw 1 ; Unused
2543 section .data
2544 pxe_udp_open_pkt:
2545 .status: dw 0 ; Status
2546 .sip: dd 0 ; Source (our) IP
2548 pxe_udp_close_pkt:
2549 .status: dw 0 ; Status
2551 pxe_udp_write_pkt:
2552 .status: dw 0 ; Status
2553 .sip: dd 0 ; Server IP
2554 .gip: dd 0 ; Gateway IP
2555 .lport: dw 0 ; Local port
2556 .rport: dw 0 ; Remote port
2557 .buffersize: dw 0 ; Size of packet
2558 .buffer: dw 0, 0 ; seg:off of buffer
2560 pxe_udp_read_pkt:
2561 .status: dw 0 ; Status
2562 .sip: dd 0 ; Source IP
2563 .dip: dd 0 ; Destination (our) IP
2564 .rport: dw 0 ; Remote port
2565 .lport: dw 0 ; Local port
2566 .buffersize: dw 0 ; Max packet size
2567 .buffer: dw 0, 0 ; seg:off of buffer
2570 ; Misc initialized (data) variables
2572 alignb 4, db 0
2573 BaseStack dd StackBuf ; ESP of base stack
2574 dw 0 ; SS of base stack
2575 NextSocket dw 49152 ; Counter for allocating socket numbers
2576 KeepPXE db 0 ; Should PXE be kept around?
2579 ; TFTP commands
2581 tftp_tail db 'octet', 0 ; Octet mode
2582 tsize_str db 'tsize' ,0 ; Request size
2583 tsize_len equ ($-tsize_str)
2584 db '0', 0
2585 blksize_str db 'blksize', 0 ; Request large blocks
2586 blksize_len equ ($-blksize_str)
2587 asciidec TFTP_LARGEBLK
2588 db 0
2589 tftp_tail_len equ ($-tftp_tail)
2591 alignb 2, db 0
2593 ; Options negotiation parsing table (string pointer, string len, offset
2594 ; into socket structure)
2596 tftp_opt_table:
2597 dw tsize_str, tsize_len, tftp_filesize
2598 dw blksize_str, blksize_len, tftp_blksize
2599 tftp_opts equ ($-tftp_opt_table)/6
2602 ; Error packet to return on options negotiation error
2604 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2605 dw TFTP_EOPTNEG ; ERROR 8: bad options
2606 db 'tsize option required', 0 ; Error message
2607 tftp_opt_err_len equ ($-tftp_opt_err)
2609 alignb 4, db 0
2610 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2613 ; IP information (initialized to "unknown" values)
2614 MyIP dd 0 ; My IP address
2615 ServerIP dd 0 ; IP address of boot server
2616 Netmask dd 0 ; Netmask of this subnet
2617 Gateway dd 0 ; Default router
2618 ServerPort dw TFTP_PORT ; TFTP server port
2621 ; Variables that are uninitialized in SYSLINUX but initialized here
2623 alignb 4, db 0
2624 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2625 BufSafeBytes dw trackbufsize ; = how many bytes?
2626 %ifndef DEPEND
2627 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2628 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2629 %endif
2630 %endif